Repository navigation
fix(cli): stopping remove-background on a Mac no longer aborts the process - #6848
miguel-heygen wants to merge 1 commit into
Conversation
Edit accuracy: accurate 271 (base branch 271), smooth 116 of thoseThe gate passes. Quarantined, measured but not gated (0) Unstable (1)
|
25757f6 to
a0b2422
Compare
There was a problem hiding this comment.
Verdict: APPROVE · 0 blocker · 1 important · 0 nits · head a0b24221
Checks
Ran locally: both tests in inference.stop.test.ts fail against main's inference.ts (released during the frame, then a 20 s hang) and pass at head, along with commandResult.test.ts (7/7). pipeline.ts awaits session.process one frame at a time, so a single inFlight covers every frame. close() after a stop releases once. CI is green at head, including Windows render verification. The only other in-process caller is preview's studio server (studioServer.ts:739); see the inline comment.
| export function endProcess(exitCode: number, host: ProcessEnd): void { | ||
| if (!drainsToExit) return host.exit(exitCode); | ||
| host.setExitCode(exitCode); | ||
| host.later(() => host.exit(exitCode), DRAIN_GRACE_MS).unref(); |
There was a problem hiding this comment.
important (non-blocking): drainToExit() is process-wide, and preview runs this pipeline in-process (studioServer.ts:739). After any CoreML job in Studio, the preview's 3 s hard deadline (preview.ts:1741, requestCliExit(0) for a hung shutdownStudio(), e.g. dead Chrome) only sets exitCode and arms this 8 s timer. A hung Studio quit then takes about 11 s instead of 3 s. If you want to keep the 3 s deadline, give endProcess a shorter grace or skip the drain when no session is live (clear the flag in close()).
Stopping
hyperframes remove-backgroundon a Mac (Ctrl-C, a timeout, a kill, a quit of the app that started it) no longer aborts the process withlibc++abi: terminating due to uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument. The run stops with a message, removes its partial output, and exits.Two things were wrong together, and each alone still aborted:
registerBrowserSessionCleanupransession.release()on the signal, with no wait for the frame in flight. The session now remembers that frame: a stop waits for it, releases once, and refuses the next frame ("Background removal was stopped."), so the run unwinds through its ownfinally(the.tmppartial is removed).close()after a stop does not release twice.process.exit(). With CoreML's runtime loaded,exit()runs its static destructors before Node has torn the addon down, and one of them locks a destroyed mutex. A run that used CoreML now ends the way a finished run does, by letting the event loop drain (process.exitCodeset,endProcessincommandResult.ts, withcli.tsstill the only place that touchesprocess.exit). A forced exit follows after 8 s if something still holds the loop.A run that finishes on its own was never affected (it exits through the drain already).
What I measured
fails without the fix:
packages/cli/src/background-removal/inference.stop.test.ts"releases the session only after that frame has ended" fails on main'sinference.ts(the session is released while the frame is inside it);packages/cli/src/utils/commandResult.test.ts"after CoreML ran, leaves the exit to Node's own teardown and forces it only after a grace" covers the exit half. Both pass with the change, withinference.coremlTempand the rest ofcommandResult;check:cli-process-ownership,oxfmt --checkandoxlintare clean.On an Apple Silicon Mac with the CLI of the current release (
remove-background clip.mp4 -o out.webm --quality beston a 60 s 1080p 25 fps clip, CoreML), SIGTERM 20 s in:mutex lock failedabort, a.tmppartial left.tmpleft, exit 1A full run (250 s, 6,250 frames, CoreML, 11.5 fps) with no signal finished with exit 0 on the released CLI, so the abort needs a stop.
What I did NOT exercise
process.exitpaths (a closed pipe, a failure after the output is written) when CoreML ran: they still callexit()directly.