nodejs/node
Child processes and worker threads
Purpose
Run additional Node (or external) processes (child_process) and additional Node threads inside the same process (worker_threads). Both produce isolated execution environments with structured channels back to the parent.
Directory layout
lib/
child_process.js
worker_threads.js
cluster.js // multi-process cluster on top of child_process
internal/child_process.js
internal/worker.js // Worker class JS
internal/worker/ // Worker support modules
js_transferable.js // structured-clone primitives bridge
io.js // MessagePort + parentPort
messaging.js // postMessage / structured-clone integration
src/
process_wrap.{cc,h} // ProcessWrap : HandleWrap (uv_process_t)
spawn_sync.{cc,h} // spawnSync implementation
node_worker.{cc,h} // Worker C++ class (per-thread Environment)
node_messaging.{cc,h} // MessagePort + structured cloneKey abstractions
| Type / file | Role |
|---|---|
ChildProcess (lib/internal/child_process.js) |
EventEmitter wrapping a ProcessWrap and the parent ends of stdio handles. |
ProcessWrap (src/process_wrap.cc) |
HandleWrap over uv_process_t. Spawns and reaps the child. |
SyncProcessRunner (src/spawn_sync.cc) |
Drives synchronous spawnSync using nested loops/timeouts. |
Worker (lib/internal/worker.js) |
JS class for worker_threads.Worker. |
Worker (src/node_worker.cc) |
C++ class that owns a child Environment + thread. |
MessagePort (src/node_messaging.cc) |
Structured-clone-capable port; backs both Worker IPC and the WHATWG MessagePort. |
MessageEvent / MessageChannel (src/node_messaging.cc) |
WHATWG-spec primitives bound to JS. |
How child_process.spawn works
sequenceDiagram
participant JS as child_process.spawn
participant CP as ChildProcess
participant PW as ProcessWrap
participant uv as libuv uv_spawn
participant K as kernel
JS->>CP: spawn(file, args, opts)
CP->>PW: spawn(uv_process_options_t)
PW->>uv: uv_spawn (fork/exec on POSIX, CreateProcessW on Win)
uv->>K: process created
K-->>uv: pid
uv-->>PW: ok
PW-->>CP: ProcessWrap.pid
CP-->>JS: ChildProcess instance
K->>uv: SIGCHLD / process exit
uv-->>PW: ExitCallback
PW-->>CP: emit 'exit' / 'close'stdio is implemented by handing libuv preconfigured handles (uv_pipe_t, uv_tty_t, file descriptors, IPC channels). The IPC channel uses a Pipe set up with IPC mode so that handle-passing (subprocess.send(msg, handle)) is supported.
child_process.fork is spawn with process.execPath and an inherited IPC channel; it loads the module path passed in.
How a Worker thread is created
graph TD
JS[worker_threads.Worker] --> Wrap[node::worker::Worker C++]
Wrap --> Iso[New v8::Isolate]
Iso --> Env[New node::Environment]
Env --> Realm[New principal Realm]
Env --> Thread[std::thread runs uv loop]
Realm --> Bootstrap[lib/internal/main/worker_thread.js]
Bootstrap --> User[user worker entry]
Wrap --> ParentPort[MessagePort pair]- Each worker has its own
v8::Isolate,node::Environment, libuv loop, and OS thread. They share the V8 platform and the parent process address space (soSharedArrayBuffers work cross-thread). - The parent and child are linked by a
MessagePortpair —worker.parentPorton the child,workeritself behaves as a port on the parent. Worker.terminate()triggers a Wave: stop the loop, dispose the Environment, dispose the Isolate, join the thread.
Structured clone and transfer
MessagePort.postMessage(value, transferList) runs node::messaging::SerializeMessage (src/node_messaging.cc) which uses V8's ValueSerializer plus Node's transferable types:
MessagePort(closes the local port, reopens on the peer).ArrayBuffer(transfer ownership).SharedArrayBuffer,WebAssembly.Module(clone reference).Blob(Node's Blob is fully transferable).crypto.KeyObject(viaJSTransferableplumbing inlib/internal/worker/js_transferable.js).- User-defined
JSTransferablesubclasses (used byFileHandle,URLPattern, etc.).
Cluster
lib/cluster.js is a thin wrapper using child_process.fork + IPC. The child receives sockets via handle-passing on the IPC channel (subprocess.send(msg, handle)). It is mostly a legacy module — modern deployments prefer worker threads or external orchestrators.
Permission model
child_processdomain (src/permission/child_process_permission.cc) blocks all child-process spawning under--permissionunless explicitly granted.workerdomain (src/permission/worker_permission.cc) similarly gatesnew Worker(...).
Integration points
- Stdio: child stdio handles become
net.Socket/Pipeinstances on the parent's side, so they can be read with the same Streams API. - Inspector: workers run their own inspector agent;
--inspect-brk-nodedebug option propagates. - Diagnostics channel: workers and child processes publish lifecycle events (
child_process.spawn,child_process.exit,node:worker_threads:create). - Snapshots: workers boot via
lib/internal/main/worker_thread.js, sharing the same embedded snapshot mechanism as the main thread.
Entry points for modification
- Spawn semantics?
lib/internal/child_process.js(JS) andsrc/process_wrap.cc(C++). - Sync exec?
src/spawn_sync.cc. - Worker bootstrap?
lib/internal/main/worker_thread.jsandsrc/node_worker.cc. - MessagePort transfer?
src/node_messaging.ccandlib/internal/worker/js_transferable.js.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.