nodejs/node
AsyncWrap, HandleWrap, ReqWrap
These three abstract classes link Node's JS-callback world to libuv's handle/request world, with proper async-resource bookkeeping for async_hooks, AsyncLocalStorage, diagnostics_channel, and the inspector. Almost every async I/O path in Node ends in a subclass of one of them.
AsyncWrap (src/async_wrap.h, src/async_wrap.cc)
AsyncWrap adds async-resource identity to a BaseObject:
- Each instance is assigned an
asyncIdand atriggerAsyncIdat creation. - It exposes
MakeCallback(...)which runs a JS callback inside the right async-context-frame, drivesprocess.nextTick, drains microtasks, dispatchesbefore/afterto async hooks, and publishesdiagnostics_channelevents for tracing. - It tracks the async-hook state on the
Environment, soasync_hooks.executionAsyncId()returns the current resource's id.
graph LR
Wrap[AsyncWrap subclass] --> Hooks[async_hooks dispatch]
Wrap --> CB[v8::Local<v8::Function> JS callback]
Wrap --> Frame[AsyncContextFrame]
CB --> JS[JS handler]
Frame --> ALS[AsyncLocalStorage]MakeCallback is the hot path. It is also the choke point you can break on in a debugger to inspect every JS reentry.
The provider type (AsyncWrap::ProviderType) is an enum like PROVIDER_TCPWRAP, PROVIDER_FSREQCALLBACK, PROVIDER_TLSWRAP. Each subclass calls the constructor with its provider, which is what async_hooks.createHook({ init }) reports.
HandleWrap (src/handle_wrap.h, src/handle_wrap.cc)
HandleWrap is for long-lived libuv handles (uv_tcp_t, uv_pipe_t, uv_tty_t, uv_udp_t, uv_timer_t, uv_async_t, uv_signal_t, uv_fs_event_t).
- Holds a
uv_handle_t*of the right subtype. - Implements
Close()that callsuv_closeand finalizes when libuv invokes the close callback. - Tracks "ref" / "unref" state so
process.ref(handle)/process.unref(handle)work uniformly.
Subclasses include LibuvStreamWrap (the bridge to StreamBase), UDPWrap, TimerWrap, ProcessWrap, TTYWrap, FSEventWrap.
ReqWrap<T> (src/req_wrap.h)
ReqWrap is for one-shot libuv requests (uv_write_t, uv_fs_t, uv_getaddrinfo_t, uv_random_t, uv_work_t).
- Holds a
T req_field whereTis one of the libuv request types. - Schedules itself with the libuv API (e.g.
uv_write(&req, handle, bufs, …)). - Owns a JS callback (in case the request fires when the JS object would otherwise be GC'd).
- Calls back into JS when the libuv request completes (
OnDone).
Examples:
| Subclass | Wraps |
|---|---|
WriteWrap |
uv_write_t (used by stream writes) |
ShutdownWrap |
uv_shutdown_t |
ConnectWrap |
uv_connect_t |
GetAddrInfoReqWrap |
uv_getaddrinfo_t |
FSReqCallback |
uv_fs_t (callback variant) |
FSReqPromise |
uv_fs_t (promise variant) |
RandomBytesJob |
uv_work_t (crypto) |
Pbkdf2Job |
uv_work_t (crypto) |
How a write moves through the stack
sequenceDiagram
participant JS as net.Socket.write(buf, cb)
participant SB as StreamBase::Writev
participant Wrap as TCPWrap (HandleWrap)
participant Req as WriteWrap (ReqWrap)
participant uv as libuv
JS->>SB: write
SB->>Req: new WriteWrap
SB->>Wrap: queue req
Wrap->>uv: uv_write(req, &handle, bufs, AfterWrite)
uv-->>Wrap: AfterWrite(req, status)
Wrap->>Req: OnDone(status)
Req->>JS: MakeCallback(cb, ...)MakeCallback here is the inherited AsyncWrap::MakeCallback that pulls in the right triggerAsyncId (the WriteWrap's trigger is the TCPWrap's asyncId, so the JS callback runs with the socket as the active async resource).
Cleanup discipline
AsyncWrap instances are usually GC'd when the JS object goes away. HandleWraps have to call uv_close first, which is asynchronous; the libuv callback then frees the C++ object via Close. Subsystems that hold strong references to a HandleWrap need BaseObjectPtr so the JS object cannot be reaped before libuv finishes closing.
Entry points for modification
- Adding a new C++ async resource? Pick the right base: long-lived handle ⇒
HandleWrap; one-shot request ⇒ReqWrap<T>; pure JS-side resource that just needs an asyncId ⇒AsyncWrapdirectly. - Bug in async-hook dispatch?
src/async_wrap.cc#MakeCallbackis the choke point. - Bug in handle ref/unref?
HandleWrap::Ref/Unrefand thewrap_object/kFlag*bitfields. - Tests live at
test/async-hooks/and acrosstest/parallel/test-async-*.js.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.