Scope
Port current Bun's worker VM teardown and source-provider ownership contracts required by js/web/broadcastchannel/broadcast-channel-worker-gc.test.ts at corpus position 4088.
Tracked from #66.
Confirmed root cause
Two stale Zig/C++ contracts overlapped:
- Home entered worker execution through the callback/RAII
JSLockHolderbridge and terminated withpthread_exit, while the linked current Bun C++ teardown destroys the JSC VM under a raw API lock that must never be released afterward. Current Bun returns normally from the worker thread and abandons that raw lock with the destroyed VM. - Current C++
SourceProvider::~SourceProviderowns and dereferencesResolvedSource.specifier, but Home's older Zig loader copied the borrowed JSCBunStringpointer without taking a+1. GuardMalloc proved the resulting ownership violation: twoJSString/source-provider owners shared one countedAtomStringImplreference, so the first sweep destructor freed it and the next dereference faulted.
This was independent of BroadcastChannel itself; a trivial postMessage("ready") worker reproduced it deterministically with Malloc=1.
Completed acceptance criteria
- acquire the worker VM API lock through the raw non-RAII entrypoint
- return normally from shutdown without touching the destroyed lock or worker
- match current Bun's unlink / dispatch-exit / outstanding-count ordering
- clear worker VM TLS and release uWS thread resources
- avoid tearing down the VM through live uncaught-error JSC frames
- retain every borrowed transpiler
ResolvedSource.specifierhanded to C++ - preserve permanent strict-malloc coverage for the upstream lifecycle file
- report child failures as structured
ERR_WORKER_PROCESSerrors with phase, operation, test name, exit/signal, cause, and composed stack - pass all four upstream worker/GC tests under
Malloc=1and GuardMalloc
Implemented and pushed in https://github.com/home-lang/home/commit/bea868ee0