diff --git a/doc/api/errors.md b/doc/api/errors.md index 07d439da8e17ed..fa4eb436803e5c 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -3564,6 +3564,22 @@ added: v18.1.0 The `Response` that has been passed to `WebAssembly.compileStreaming` or to `WebAssembly.instantiateStreaming` is not a valid WebAssembly response. + + +### `ERR_WORKER_HANDLE_NOT_TRANSFERABLE` + +An attempt was made to transfer a `net.Socket` or `net.Server` to another thread +via a `worker_threads` `postMessage()` call while it was not in a transferable +state, for example because it had already started reading or had buffered data. + + + +### `ERR_WORKER_HANDLE_TRANSFER_UNSUPPORTED` + +An attempt was made to transfer a `net.Socket` or `net.Server` to another thread +on a platform where moving the underlying handle between event loops is not +supported (currently Windows). + ### `ERR_WORKER_INIT_FAILED` diff --git a/doc/api/net.md b/doc/api/net.md index e2f06776409898..81863d9b0a13c5 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -305,6 +305,11 @@ added: v0.1.90 This class is used to create a TCP or [IPC][] server. +A listening TCP `net.Server` can be transferred to a worker thread by listing it +in the `transferList` of a [`worker_threads`][] `postMessage()` call. This moves +the underlying listening socket to the receiving thread, where it resumes +accepting connections. See [Transferring TCP handles to other threads][]. + ### `new net.Server([options][, connectionListener])` * `options` {Object} See @@ -751,6 +756,41 @@ is received. For example, it is passed to the listeners of a [`'connection'`][] event emitted on a [`net.Server`][], so the user can use it to interact with the client. +### Transferring TCP handles to other threads + +A connected TCP `net.Socket` can be moved to another thread by listing it in the +`transferList` of a [`worker_threads`][] `postMessage()` call. After the +transfer, the source socket is destroyed on the sending thread (further use +fails with `ERR_STREAM_DESTROYED` rather than silently dropping data), and the +socket continues to work on the receiving thread. This makes it possible to +accept connections on one thread and distribute them across a pool of worker +threads, for example to build a `node:cluster`-like model on top of worker +threads. + +The socket must be a freshly accepted or created TCP connection: it must still +be attached to a live handle, must not be connecting or destroyed, and must not +have started reading or have buffered data. Otherwise `postMessage()` throws +`ERR_WORKER_HANDLE_NOT_TRANSFERABLE`. Only TCP sockets are supported, and only +on Unix-like platforms; on Windows `postMessage()` throws +`ERR_WORKER_HANDLE_TRANSFER_UNSUPPORTED`. + +```cjs +const net = require('node:net'); +const { Worker } = require('node:worker_threads'); + +// worker.js receives `{ socket }` messages and handles each connection. +const worker = new Worker('./worker.js'); + +const server = net.createServer((socket) => { + // Hand the freshly accepted connection off to the worker thread. + worker.postMessage({ socket }, [socket]); +}); +server.listen(8000); +``` + +A listening [`net.Server`][] can be transferred the same way, which moves the +listening socket itself (and its pending accept queue) to the receiving thread. + ### `new net.Socket([options])`