Repository navigation
[Prototype] Node 24: run await continuations from fibers in hidden fibers (port of #6 + #8) - #9
Draft
jbaczuk-qualia wants to merge 5 commits into
Draft
jbaczuk-qualia wants to merge 5 commits into
jbaczuk-qualia wants to merge 5 commits into
Conversation
Prototype, needs the patched node (v8-microtask-dispatch.h). While a fiber runs, its context's CPED is a marker, so every promise reaction registered inside it (the code after `await`, `.then` callbacks) is handed to a dispatcher that runs it in a new fiber. Those fibers are hidden: `Fiber.current` reports undefined there, as it does after an `await` today, so Fiber.current-based mode switches (e.g. cursor.observeChanges returning a Promise) are unchanged. Future.wait uses the new `Fiber.currentIncludingHidden`, so blocking calls work. The dispatch API is resolved with dlsym, so this binary still loads (with the feature off) on stock node. FIBERS_AWAIT_DISPATCH=0 disables it; Fiber.microtasksDispatched counts dispatched jobs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Instead of a new hidden fiber per dispatched job, keep one long-lived hidden dispatch fiber. After each job it runs the dispatchable jobs queued right behind it (Fiber.__runNextDispatchable, needs the matching node patch), so a chain of awaits in fibered code costs one switch onto the fiber and one back instead of two per await. A job that blocks keeps the fiber and the next dispatch starts a new one; a fiber that was parked and later resumed never pulls in other jobs. FIBERS_AWAIT_REUSE=0 restores one new fiber per job. Fiber.microtasksBatched counts jobs run without a switch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- V8 13 moved the continuation-preserved embedder data (CPED) slot from the context to the isolate; use Isolate::Get/SetContinuationPreserved EmbedderData there and keep the Context API on older V8. - Tag with `true` instead of a heap object. On V8 13 the CPED slot is a GC root visited once per archived thread state (one per fiber), and a young object visited twice in one scavenge corrupts the heap. `true` lives in read-only space and never moves. - Don't install the dispatcher when node 24's AsyncContextFrame is on: it keeps AsyncLocalStorage state in the same slot. Detected with Fiber.__cpedInUse() inside an AsyncLocalStorage.run(); fibers needs --no-async-context-frame on node 24 anyway. - Keep Fiber.microtasksBatched on the shared native module, so it reads correctly when an app loads more than one copy of fibers_async.js. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tly requested Helper processes (build tools, child processes) load fibers on node 24 without --no-async-context-frame; a warning on every load is noise. Fiber.awaitDispatchEnabled reports whether dispatch is on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without AsyncContextFrame an AsyncLocalStorage enables promise hooks for the whole process, which made every await ~5x slower in processes that use no AsyncLocalStorage of their own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Port of the node 18 fiber-await prototype (#6 + #8) to node 24, stacked on #5 (
node24-pthread; merge #5 first). Needs qualialabs/node#10. Code after anawait(or a.thencallback) registered inside a fiber runs on a hidden fiber, reused and batched across consecutive jobs;Fiber.currentstill readsundefinedthere, soFiber.current-based mode switches are unchanged, whileFuture.wait(andPromise.awaitwith qualialabs/shared-packages#265) can block.f8fdefdfibers_async.js)b632621dcf9992trueinstead of a heap object (the CPED slot is a GC root visited once per archived thread, i.e. per fiber, and a young object visited twice in a scavenge corrupts the heap); don't install the dispatcher when AsyncContextFrame is on (it keeps AsyncLocalStorage state in the same slot; detected withFiber.__cpedInUse());Fiber.microtasksBatchedkept on the shared native module1eacecdFIBERS_AWAIT_DISPATCH=1asks for dispatch (helper processes load fibers without the flag);Fiber.awaitDispatchEnabledreports the stated0ed894AsyncLocalStorageotherwise turns on promise hooks for the whole process and made fiberless awaits ~5x slowerMeasurement tooling for node 24 (counters,
fiber-await/{test,bench}.js,app_stats.mjs, README) is onjbaczuk/fiber-await-v24-stats, which is not meant to merge.Testing
node 24.21.0 from node#10, arm64 Docker,
--no-async-context-frame:fiber-await/test.js: 11/11 with reuse on and off;FIBERS_AWAIT_DISPATCH=0, stock [Prototype] Run await continuations from fibers in hidden fibers (needs node#7) #6 node, or AsyncContextFrame on: the expected 7 failures, andFiber.awaitDispatchEnabled === falsewith AsyncContextFrame on.test/: all pass (includingpool.js/cleanup.js, thanks to Node 24 / V8 13 compatibility with CORO_PTHREAD coroutines #5'scoro_destroyfix). znewsham's scenarios (Yield after await #7, running fiber): 9/9. meteor-promise suite: 15/15.--stress-scavenge=20,--stress-compaction): behaviour checks pass.jbaczuk/node-24-gdc-fiber-await, on top of qualia#56473, vendoring this branch with a prebuilt node 24 binary): boots; a fibered startup hook that awaits and then callsMeteor.users.find().count()works on a hidden fiber; fiberless code still throws. Two hand-patches needed there: meteor-lite#41's tarball guard, and shared-packages#265's one-liner in both meteor-promise copies (GDC loads the one nested under@meteor/promise/node_modules).🤖 Generated with Claude Code