You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
LOC-7420: tolerate a busy binary instead of crashing the consumer
On Windows, BrowserStackLocal.exe in ~/.browserstack is routinely
unopenable for a moment -- an AV scan of a freshly written executable, a
tunnel still releasing its handle, two test workers starting at once --
and the open fails with EBUSY/EPERM. POSIX allows opening and unlinking a
file in use, so this only manifests on Windows. Several defects turned
that transient condition into a hard crash before any session started.
download.js and LocalBinary.js registered their write-stream 'error'
handler inside the async https.get callback. createWriteStream fails at
the open() syscall and emits on the next tick, well before the TLS round
trip completes, so the error arrived with no listener and node's
`throw er` killed the download child. The handlers now attach
immediately after createWriteStream.
Handling that error is not sufficient on its own: the request is still in
flight, and without tearing it down the child stays alive downloading
into a stream nobody reads, so the parent's spawnSync blocks for a whole
download before it can retry. The throw was also doing the job of
stopping the download; download.js now destroys the request explicitly.
retryBinaryDownload did its work inside an async callback, so on the sync
path it returned undefined to a caller that had already given up -- which
surfaced as "Couldn't find binary file" while the retries carried on,
orphaned, in the background. This happened even when the unlink
succeeded, so it is not a consequence of the EPERM. The sync path is now
synchronous end to end and returns its result.
Retrying instantly against a live lock just burns the retry budget in
milliseconds, so a busy binary is now probed (openSync 'r+') and waited
on, bounded, rather than deleted.
spawnSync reports spawn-level failures through obj.error, leaving stdout
null; reading .length threw a TypeError that replaced the real cause in
the reported message, after which the binary was deleted anyway.
execFile raises the same class of failure synchronously rather than
through its callback -- EBADARCH from a binary built for another
architecture, for instance -- and inside the getBinaryPath callback an
uncaught throw there killed the consumer's process outright, the same
failure mode as the download path. Both are now handled, and the
unlinkSync calls in the error paths no longer throw out of start().
Tests cover the retry return value, the retry ceiling, the busy probe and
the unhandled-'error' regression. They force the open to fail rather than
reproducing a lock, since the defect is any createWriteStream failure
rather than EBUSY specifically -- so they need no Windows runner, network
or credentials.
.eslintrc.json moves from es6 to es2017 for Atomics/SharedArrayBuffer,
used for the bounded wait on the sync path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0 commit comments