(forceUpdate)
| 737 | // which resolves to the package containing this file regardless of |
| 738 | // install layout (global npm / local node_modules / dev clone). |
| 739 | function executeForceUpdate(forceUpdate) { |
| 740 | // Concurrency guard: if a prior invocation is still in flight, refuse and |
| 741 | // return the BUSY sentinel. The in-flight caller owns the outcome (state |
| 742 | // file write, process.exit(78) on success); a second concurrent attempt |
| 743 | // would (a) race the atomic-rename state-file writes and clobber the first |
| 744 | // attempt's telemetry row, and (b) potentially double-exit. See |
| 745 | // FORCE_UPDATE_BUSY docstring above for context. |
| 746 | if (_inFlight) { |
| 747 | console.log('[ForceUpdate] BUSY: another invocation already in flight, skipping'); |
| 748 | return FORCE_UPDATE_BUSY; |
| 749 | } |
| 750 | _inFlight = true; |
| 751 | try { |
| 752 | return _executeForceUpdateInner(forceUpdate); |
| 753 | } finally { |
| 754 | // Always release the mutex, even on throw. Callers may rely on retrying |
| 755 | // after a failure (e.g. heartbeat cooldown), so the flag MUST NOT remain |
| 756 | // set after the function returns/throws. Note: on a successful upgrade, |
| 757 | // _executeForceUpdateInner returns true and the caller invokes |
| 758 | // process.exit(78); the finally still runs before exit -- which is fine, |
| 759 | // there is nothing else to coordinate with at that point. |
| 760 | _inFlight = false; |
| 761 | } |
| 762 | } |
| 763 | |
| 764 | function _executeForceUpdateInner(forceUpdate) { |
| 765 | const INSTALL_ROOT = getEvolverInstallRoot(); |
no test coverage detected