Open-Source Wikis

/

Node.js

/

Primitives

/

AsyncWrap, HandleWrap, ReqWrap

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 asyncId and a triggerAsyncId at creation.
  • It exposes MakeCallback(...) which runs a JS callback inside the right async-context-frame, drives process.nextTick, drains microtasks, dispatches before/after to async hooks, and publishes diagnostics_channel events for tracing.
  • It tracks the async-hook state on the Environment, so async_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 calls uv_close and 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 where T is 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 ⇒ AsyncWrap directly.
  • Bug in async-hook dispatch? src/async_wrap.cc#MakeCallback is the choke point.
  • Bug in handle ref/unref? HandleWrap::Ref / Unref and the wrap_object / kFlag* bitfields.
  • Tests live at test/async-hooks/ and across test/parallel/test-async-*.js.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

AsyncWrap, HandleWrap, ReqWrap – Node.js wiki | Factory