[CORO_UCONTEXT] Node 24 / V8 13 compatibility (ucontext coroutines; closed, superseded by #5) - #2
jbaczuk-qualia wants to merge 4 commits into
Conversation
…om run() - SetAccessor -> SetNativeDataProperty, Holder() -> This() (removed in V8 13) - uni::Return takes PropertyCallbackInfo by const reference: since V8 13 the argument slots live inline in the struct, so a by-value copy lost every GetReturnValue().Set() and all accessors read as undefined - fibers_async.js: return fn(...args) from runInAsyncScope so fiber.run() returns the function's result when the fiber finishes Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
V8 13 keeps GC state such as the marking write barrier in compiler thread_local storage that is not copied to coroutine threads, so the CORO_PTHREAD build corrupts the heap under node 24. Same-thread coroutines (upstream's default, and the async-resource-threadless branch) see all of it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ack to V8 find_thread_id_key located V8's isolate, thread-data and thread-id pthread keys by value. V8 >= 12 keeps the first two in thread_local storage, so the scan silently found nothing (its asserts are compiled out) and coroutines stopped getting their own V8 ThreadId, which the Locker/Unlocker archiving of JS stacks relies on. Fall back to comparing two helper-thread snapshots: the ThreadId slot is the small int that increments between them. The isolate and thread-data slots are maintained by Locker/Unlocker themselves and are only swapped when found. FIBERS_DEBUG_TLS=1 prints what was detected. If the node binary exports v8_qualia_set_thread_stack_start (Qualia's node build), call it on every coroutine switch with the target stack's start so cppgc's conservative stack scan, stack limits and IsOnStack() see the coroutine stack instead of the OS thread's. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…on and fail loudly The V8 >= 13 fallback compared two helper-thread TLS snapshots and required the ThreadId slot to read exactly n and n + 1. V8 creates platform worker threads lazily, so a thread created between the two snapshots consumes an id and the match fails; observed about once per 80 loads on node 24. The comparison now accepts a gap of up to 64, requires the matching key to be unique, and retries with a fresh snapshot pair up to eight times (150/150 loads afterwards). A failed discovery is now a fatal error with a message instead of an assert: the assert is compiled out of Release builds, and without the key every coroutine shares the OS thread's V8 ThreadId, so Locker/Unlocker archiving corrupts JS stacks intermittently later. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Closed 2026-09-21, kept for posterity. This is the |
Summary
Makes fibers build and run on
qualialabs/node#custom-v24(Node 24.21.0, V8 13.6). Four commits:e0fbe77compile against V8 13 and return the fiber's result fromrun().SetAccessor->SetNativeDataProperty,Holder()->This(),kFinalizerweak callbacks ->kParameter.PropertyCallbackInfois now stored inline, so the by-valueuni::Return()helper silently dropped every getter result; it takes a const reference now.fibers_async.jsreturns the wrapped function's value sorun()resolves to it (README semantics).a7f5750ucontext coroutines on Linux and arm64 instead of pthreads. Samebinding.gypchange as build: use ucontext coroutines instead of pthreads on Linux and arm64 #3.CORO_PTHREADbuilds and passes the fibers suite (16/19) and GC stress on the patched node 24, so V8 13 itself does not rule it out (an earlier version of this description said it did; that was wrong). What does rule it out is node 24: GDC with pthread fibers dies on its first HTTPS request becausecrypto_context.cckeeps the root cert store inthread_localstatics and a fiber thread re-registers its cleanup hook (CleanupQueue::AddCHECK). ucontext avoids the whole class, costs ~0.7 µs per switch instead of ~13 µs plus an OS thread per fiber, and passes more of the suite (18/19 vs 16/19).a692f79find V8'sThreadIdTLS key on V8 >= 12 and report the running stack to V8. The legacy TLS-key scan finds nothing on V8 >= 12, soFibersilently ran without swapping the thread id. New scan: snapshot pthread TLS from a helper thread that has entered the isolate and pick the key whose value is a small monotonically assigned id.FIBERS_DEBUG_TLS=1prints what it found. On every switch fibers now callsv8_qualia_set_thread_stack_start()(resolved withdlsym, no-op when absent) so cppgc's conservative stack scan walks the fiber stack instead of the OS thread's.2fb6179make the key discovery tolerate concurrent thread creation and fail loudly. The two-snapshot diff flaked about 1 run in 80 when another thread was assigned aThreadIdbetween the snapshots (vb == va + 1no longer held) and died on anassert. It now retries up to 8 times, acceptsvb - va <= 64, requires exactly one candidate key, and aborts with a message naming the problem instead of an assert. 150/150 process starts clean afterwards.Verification
test/*.js: 18/19 on custom node 24 with ucontext (future-exception.jsonly, which fails on every runtime and backend); the shipped pthread build is 16/19 on node 18 and on this node (pool.js,cleanup.jssegfault).gc_stress.jswith--stress-incremental-marking --stress-compaction): 150 rounds clean on a Graviton2 remote-dev host, 300 rounds on an M-series container. Needs [CORO_UCONTEXT] Node 24.21.0 with CORO_UCONTEXT-based fibers support (custom-v24; closed, superseded by #6) node#4 commitfbc38d02; before it the ucontext run segfaulted within ~10 rounds because cppgc scanned from a stale stack start.55f54c27); that is a node fix, nothing here changes for it.global-deployment-centerruns on it in qli5 (login, subs, methods); 200k-yield leak test shows a flat heap and no external growth.Not published yet: the monorepo experiment vendors
npm packof this branch asfibers-5.0.5-node24.tgz.5.0.5is already published, so this needs a new version (5.0.6 is taken by #3's bump; use 5.1.0 or rebase onto #3). Related PRs: qualialabs/node#4, qualialabs/node-builder#16, qualialabs/meteor-lite#41, qualialabs/qualia#56265 and #56266.🤖 Generated with Claude Code