feat(async_hooks): Add AsyncLocalStorage and expand async resource tracking - #1789
Draft
nabetti1720 wants to merge 17 commits into
Draft
nabetti1720 wants to merge 17 commits into
nabetti1720 wants to merge 17 commits into
Conversation
nabetti1720
marked this pull request as draft
September 20, 2026 09:12
nabetti1720
force-pushed
the
feat/async-local-storage
branch
2 times, most recently
from
September 20, 2026 12:28
bbc52ab to
764464d
Compare
nabetti1720
marked this pull request as ready for review
September 20, 2026 12:34
nabetti1720
force-pushed
the
feat/async-local-storage
branch
from
September 20, 2026 14:09
ad14e55 to
af236cc
Compare
nabetti1720
force-pushed
the
feat/async-local-storage
branch
3 times, most recently
from
September 22, 2026 08:59
1b673e6 to
01d1301
Compare
…rhead async resource tracking
- Expose init_state, shutdown_state, and run_pending_jobs directly - Simplify timer initialization and scheduling APIs - Remove obsolete runtime wrappers and aliases - Preserve ProviderType as a public hooking API
- Centralize hook registration and callback selection - Consolidate AsyncLocalStorage state transitions and cleanup - Simplify async resource ID parsing and runtime state cleanup
…ions Keep a compatibility fallback for Promise jobs whose execution context cannot be identified by QuickJS, and preserve timer context while draining pending jobs. Document the fallback's concurrency limitation.
Store hook callbacks in AsyncHookState and capture only a hook ID in enable/disable closures to prevent runtime shutdown GC assertions. Add a subprocess regression test for clean hook shutdown.
nabetti1720
force-pushed
the
feat/async-local-storage
branch
from
September 23, 2026 11:47
3aa1311 to
3e688fd
Compare
nabetti1720
force-pushed
the
feat/async-local-storage
branch
from
September 23, 2026 12:02
3e688fd to
a2ea16e
Compare
nabetti1720
marked this pull request as draft
September 24, 2026 23:32
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.
Issue # (if available)
Closes #1170
Description of changes
NOTE: Given the immense scale and high level of complexity of this PR, we are fully leveraging AI (GPT-5.6 Luna). However, we are carefully directing the course of revisions and verifying their validity through human review whenever possible.
We are reviewing the functional placement regarding the asynchronous implementation, resulting in a large volume of changes associated with moving the code.
Summary
This change significantly expands LLRT’s
async_hooksimplementation and addsAsyncLocalStoragesupport.It adds async lifecycle tracking for Promises, native resources, timers, DNS lookups, microtasks, and module loading while keeping tracking overhead low and making object ownership and cleanup explicit.
Background
The previous implementation had several limitations:
destroycallback handling were fragmentedAsyncLocalStoragewas not implementedThis change centralizes async resource metadata and uses JavaScript-managed weak references and finalization to observe object lifetimes. Rust retains only the metadata and callback handles required to bridge lifecycle events.
Technical Changes
Promise identity
WeakMapFinalizationRegistryNative resource management
WeakRefPersistent<Object>AsyncResource, containing:Finalization and destroy lifecycle
AsyncLocalStoragestate associated with finalized async IDs is removeddestroycallbacks execute in the finalized resource’s async context and restore the previous context afterwardAsyncLocalStorage
The following APIs are implemented:
AsyncLocalStorageAsyncLocalStorage.bindAsyncLocalStorage.snapshotdisableenterWithexitgetStorerundefaultValuenameAsyncLocalStorageinstances are registered through weak handles so discarded instances do not remain strongly reachable.Propagation is limited to active storage instances:
Tracking overhead
PromiseHook processing is gated by a tracking bitmask covering:
initbeforeafterresolvedestroyAsyncLocalStorageWhen no hook or ALS tracking is required, PromiseHook processing returns immediately without allocating async IDs or registering finalization callbacks.
Native async operation integration
The following operations participate in async hook lifecycle tracking:
queueMicrotaskTimer and DNS callbacks attempt to invoke the corresponding
afterhook even when the user callback throws. The original callback error is returned after theafterhook has been attempted.Async and trigger IDs
checked_addHook callback lifetime
enableanddisableclosures capture only a hook ID and the enabled stateAsyncHookStateAPI and Type Definitions
Updated:
API.mdtypes/async_hooks.d.tsThe documentation and declarations include
executionAsyncResourceandAsyncLocalStorage.Tests
Coverage includes:
AsyncLocalStoragePromise propagationruncallsenterWithandexitdisablebindandsnapshotAsyncLocalStorageinstancesValidation
async_hooks: 11/11 passedasync_local_storage: 16/16 passedcargo test -p llrt_async_hookscargo check -p llrt_async_hooksgit diff --checkKnown Limitation
QuickJS does not expose the Promise reaction job currently being executed. As a result, the bridge cannot always determine which Promise continuation is calling
AsyncLocalStorage.getStore().The synchronous execution scope created by
AsyncLocalStorage.run()is restored immediately after the callback returns. The store for a returned Promise is retained separately so that its continuations can recover the context. This compatibility fallback stores only the latest Promise store and therefore cannot completely isolate concurrent Promise continuations.The remaining limitation requires QuickJS support for identifying the currently executing Promise reaction job, or an equivalent async execution context. Once that runtime information is available, LLRT can use it to provide Node.js-compatible concurrent Promise propagation.
Reproduction code:
Expected:
LLRT:
Checklist
tests/unitand/or in Rust for my feature if neededmake fixto format JS and apply Clippy auto fixesmake checktypes/directoryBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.