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
{{ message }}
Repository navigation
Commit ab72efc
Browse filesBrowse the repository at this point in the historyBrowse files
fix(runtime): strong Worker wrapper lifetime while the thread runs (#456)
* fix(runtime): Worker wrappers are strong roots while their thread runs
Replaces the finalizer-resurrection lifetime with reachability: the
wrapper's persistent goes strong once the thread starts and is released
only by the thread-exit notification, posted from the worker's teardown
to the parent's event loop — terminate() initiates the wind-down but
never drops the root early, so no GC can condemn a wrapper whose thread
is still draining. ObjectManager's refuse-and-re-weaken branch stays as
a defensive fallback but is unreachable for workers.
The motivation is a reproduced heap corruption: the patched collector's
kFinalizer resurrection handles ephemeron keys in the atomic pause but
not under concurrent marking — a resurrected WeakMap key whose values
are reachable only through the entry leaves a dangling value slot that
crashes ConcurrentMarkingVisitor::RecordSlot on a later cycle. Strong
lifetime takes Worker off that path entirely; the collector bug is
tracked separately for the other resurrectable wrapper types.
The thread-exit notification also dispatches the internal
nsworkerended event on the Worker object, so node:worker_threads'
Worker shim now emits 'exit' exactly once for self-close as well as
terminate().
Suite: 1663/0 incl. new WorkerLifetimeTests (WeakMap-key repro that
crashed before this change, collectability after terminate and
self-close, delivery to an unreferenced live worker).
* docs(runtime): the listener-bag rule is Node parity plus patch independence, not a live crash
The wrapper-keyed-WeakMap corruption was a collector bug fixed in the
v8-14.9.207.39-6 prebuilts; the rule stays because own-instance state is
Node's design for handler attributes and keeps the builtins off the
resurrection/ephemeron interplay the kFinalizer patch must re-cover on
every V8 upgrade.
* fix(runtime): reach the parent through its event loop, never its isolate, from the worker thread
Worker-thread posts to the parent read the parent isolate's runtime slot and
then the runtime's loop. The parent's destructor terminates its children
without joining them, clears that slot and disposes the isolate, so a child
ending while a worker-parent was torn down could read a freed isolate or a
runtime mid-destruction. The wrapper now captures a weak_ptr to the parent's
loop on the parent's thread at construction; a loop that has shut down drops
the post and an expired pointer means the parent is gone. BackgroundLooper
also reads everything it needs before publishing isDisposed_, which is what
allows a tearing-down parent to delete the wrapper concurrently.
* test(runtime): a terminated worker whose dropped message sentinels a port it owns must end
* fix(runtime): settle terminate() from the thread-ended notification, after exit
The shim emitted exit and resolved terminate() off a microtask, before the
thread was down and before messages and errors the worker had already
queued on the parent's loop had run. Both now follow the runtime's
end-of-worker notification, so nothing the worker sent can arrive after
exit. The code stays 0 for every end, as the cross-runtime suite pins.
* docs(runtime): drop the resurrection rationale the strong Worker root made stale, and name the real patch gate
0 commit comments