| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 9e975f1 commit 73b5c16
14 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3213,6 +3213,54 @@ The `Worker` instance terminated because it reached its memory limit. | |||
| 3213 | 3213 | The path for the main script of a worker is neither an absolute path | |
| 3214 | 3214 | nor a relative path starting with `./` or `../`. | |
| 3215 | 3215 | ||
| 3216 | + <a id="ERR_WORKER_MESSAGING_ERRORED"></a> | ||
| 3217 | + | ||
| 3218 | + ### `ERR_WORKER_MESSAGING_ERRORED` | ||
| 3219 | + | ||
| 3220 | + <!-- YAML | ||
| 3221 | + added: REPLACEME | ||
| 3222 | + --> | ||
| 3223 | + | ||
| 3224 | + > Stability: 1.1 - Active development | ||
| 3225 | + | ||
| 3226 | + The destination thread threw an error while processing a message sent via [`postMessageToThread()`][]. | ||
| 3227 | + | ||
| 3228 | + <a id="ERR_WORKER_MESSAGING_FAILED"></a> | ||
| 3229 | + | ||
| 3230 | + ### `ERR_WORKER_MESSAGING_FAILED` | ||
| 3231 | + | ||
| 3232 | + <!-- YAML | ||
| 3233 | + added: REPLACEME | ||
| 3234 | + --> | ||
| 3235 | + | ||
| 3236 | + > Stability: 1.1 - Active development | ||
| 3237 | + | ||
| 3238 | + The thread requested in [`postMessageToThread()`][] is invalid or has no `workerMessage` listener. | ||
| 3239 | + | ||
| 3240 | + <a id="ERR_WORKER_MESSAGING_SAME_THREAD"></a> | ||
| 3241 | + | ||
| 3242 | + ### `ERR_WORKER_MESSAGING_SAME_THREAD` | ||
| 3243 | + | ||
| 3244 | + <!-- YAML | ||
| 3245 | + added: REPLACEME | ||
| 3246 | + --> | ||
| 3247 | + | ||
| 3248 | + > Stability: 1.1 - Active development | ||
| 3249 | + | ||
| 3250 | + The thread id requested in [`postMessageToThread()`][] is the current thread id. | ||
| 3251 | + | ||
| 3252 | + <a id="ERR_WORKER_MESSAGING_TIMEOUT"></a> | ||
| 3253 | + | ||
| 3254 | + ### `ERR_WORKER_MESSAGING_TIMEOUT` | ||
| 3255 | + | ||
| 3256 | + <!-- YAML | ||
| 3257 | + added: REPLACEME | ||
| 3258 | + --> | ||
| 3259 | + | ||
| 3260 | + > Stability: 1.1 - Active development | ||
| 3261 | + | ||
| 3262 | + Sending a message via [`postMessageToThread()`][] timed out. | ||
| 3263 | + | ||
| 3216 | 3264 | <a id="ERR_WORKER_UNSERIALIZABLE_ERROR"></a> | |
| 3217 | 3265 | ||
| 3218 | 3266 | ### `ERR_WORKER_UNSERIALIZABLE_ERROR` | |
@@ -3954,6 +4002,7 @@ An error occurred trying to allocate memory. This should never happen. | |||
| 3954 | 4002 | [`new URLSearchParams(iterable)`]: url.md#new-urlsearchparamsiterable | |
| 3955 | 4003 | [`package.json`]: packages.md#nodejs-packagejson-field-definitions | |
| 3956 | 4004 | [`postMessage()`]: worker_threads.md#portpostmessagevalue-transferlist | |
| 4005 | + [`postMessageToThread()`]: worker_threads.md#workerpostmessagetothreadthreadid-value-transferlist-timeout | ||
| 3957 | 4006 | [`process.on('exit')`]: process.md#event-exit | |
| 3958 | 4007 | [`process.send()`]: process.md#processsendmessage-sendhandle-options-callback | |
| 3959 | 4008 | [`process.setUncaughtExceptionCaptureCallback()`]: process.md#processsetuncaughtexceptioncapturecallbackfn | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -327,6 +327,18 @@ possible to record such errors in an error log, either periodically (which is | |||
| 327 | 327 | likely best for long-running application) or upon process exit (which is likely | |
| 328 | 328 | most convenient for scripts). | |
| 329 | 329 | ||
| 330 | + ### Event: `'workerMessage'` | ||
| 331 | + | ||
| 332 | + <!-- YAML | ||
| 333 | + added: REPLACEME | ||
| 334 | + --> | ||
| 335 | + | ||
| 336 | + * `value` {any} A value transmitted using [`postMessageToThread()`][]. | ||
| 337 | + * `source` {number} The transmitting worker thread ID or `0` for the main thread. | ||
| 338 | + | ||
| 339 | + The `'workerMessage'` event is emitted for any incoming message send by the other | ||
| 340 | + party by using [`postMessageToThread()`][]. | ||
| 341 | + | ||
| 330 | 342 | ### Event: `'uncaughtException'` | |
| 331 | 343 | ||
| 332 | 344 | <!-- YAML | |
@@ -4173,6 +4185,7 @@ cases: | |||
| 4173 | 4185 | [`net.Server`]: net.md#class-netserver | |
| 4174 | 4186 | [`net.Socket`]: net.md#class-netsocket | |
| 4175 | 4187 | [`os.constants.dlopen`]: os.md#dlopen-constants | |
| 4188 | + [`postMessageToThread()`]: worker_threads.md#workerpostmessagetothreadthreadid-value-transferlist-timeout | ||
| 4176 | 4189 | [`process.argv`]: #processargv | |
| 4177 | 4190 | [`process.config`]: #processconfig | |
| 4178 | 4191 | [`process.execPath`]: #processexecpath | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -220,6 +220,118 @@ if (isMainThread) { | |||
| 220 | 220 | } | |
| 221 | 221 | ``` | |
| 222 | 222 | ||
| 223 | + ## `worker.postMessageToThread(threadId, value[, transferList][, timeout])` | ||
| 224 | + | ||
| 225 | + <!-- YAML | ||
| 226 | + added: REPLACEME | ||
| 227 | + --> | ||
| 228 | + | ||
| 229 | + > Stability: 1.1 - Active development | ||
| 230 | + | ||
| 231 | + * `destination` {number} The target thread ID. If the thread ID is invalid, a | ||
| 232 | + [`ERR_WORKER_MESSAGING_FAILED`][] error will be thrown. If the target thread ID is the current thread ID, | ||
| 233 | + a [`ERR_WORKER_MESSAGING_SAME_THREAD`][] error will be thrown. | ||
| 234 | + * `value` {any} The value to send. | ||
| 235 | + * `transferList` {Object\[]} If one or more `MessagePort`-like objects are passed in `value`, | ||
| 236 | + a `transferList` is required for those items or [`ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST`][] is thrown. | ||
| 237 | + See [`port.postMessage()`][] for more information. | ||
| 238 | + * `timeout` {number} Time to wait for the message to be delivered in milliseconds. | ||
| 239 | + By default it's `undefined`, which means wait forever. If the operation times out, | ||
| 240 | + a [`ERR_WORKER_MESSAGING_TIMEOUT`][] error is thrown. | ||
| 241 | + * Returns: {Promise} A promise which is fulfilled if the message was successfully processed by destination thread. | ||
| 242 | + | ||
| 243 | + Sends a value to another worker, identified by its thread ID. | ||
| 244 | + | ||
| 245 | + If the target thread has no listener for the `workerMessage` event, then the operation will throw | ||
| 246 | + a [`ERR_WORKER_MESSAGING_FAILED`][] error. | ||
| 247 | + | ||
| 248 | + If the target thread threw an error while processing the `workerMessage` event, then the operation will throw | ||
| 249 | + a [`ERR_WORKER_MESSAGING_ERRORED`][] error. | ||
| 250 | + | ||
| 251 | + This method should be used when the target thread is not the direct | ||
| 252 | + parent or child of the current thread. | ||
| 253 | + If the two threads are parent-children, use the [`require('node:worker_threads').parentPort.postMessage()`][] | ||
| 254 | + and the [`worker.postMessage()`][] to let the threads communicate. | ||
| 255 | + | ||
| 256 | + The example below shows the use of of `postMessageToThread`: it creates 10 nested threads, | ||
| 257 | + the last one will try to communicate with the main thread. | ||
| 258 | + | ||
| 259 | + ```mjs | ||
| 260 | + import { fileURLToPath } from 'node:url'; | ||
| 261 | + import { once } from 'node:events'; | ||
| 262 | + import process from 'node:process'; | ||
| 263 | + import { | ||
| 264 | + isMainThread, | ||
| 265 | + postMessageToThread, | ||
| 266 | + threadId, | ||
| 267 | + workerData, | ||
| 268 | + Worker, | ||
| 269 | + } from 'node:worker_threads'; | ||
| 270 | + | ||
| 271 | + const channel = new BroadcastChannel('sync'); | ||
| 272 | + const level = workerData?.level ?? 0; | ||
| 273 | + | ||
| 274 | + if (level < 10) { | ||
| 275 | + const worker = new Worker(fileURLToPath(import.meta.url), { | ||
| 276 | + workerData: { level: level + 1 }, | ||
| 277 | + }); | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + if (level === 0) { | ||
| 281 | + process.on('workerMessage', (value, source) => { | ||
| 282 | + console.log(`${source} -> ${threadId}:`, value); | ||
| 283 | + postMessageToThread(source, { message: 'pong' }); | ||
| 284 | + }); | ||
| 285 | + } else if (level === 10) { | ||
| 286 | + process.on('workerMessage', (value, source) => { | ||
| 287 | + console.log(`${source} -> ${threadId}:`, value); | ||
| 288 | + channel.postMessage('done'); | ||
| 289 | + channel.close(); | ||
| 290 | + }); | ||
| 291 | + | ||
| 292 | + await postMessageToThread(0, { message: 'ping' }); | ||
| 293 | + } | ||
| 294 | + | ||
| 295 | + channel.onmessage = channel.close; | ||
| 296 | + ``` | ||
| 297 | + | ||
| 298 | + ```cjs | ||
| 299 | + const { once } = require('node:events'); | ||
| 300 | + const { | ||
| 301 | + isMainThread, | ||
| 302 | + postMessageToThread, | ||
| 303 | + threadId, | ||
| 304 | + workerData, | ||
| 305 | + Worker, | ||
| 306 | + } = require('node:worker_threads'); | ||
| 307 | + | ||
| 308 | + const channel = new BroadcastChannel('sync'); | ||
| 309 | + const level = workerData?.level ?? 0; | ||
| 310 | + | ||
| 311 | + if (level < 10) { | ||
| 312 | + const worker = new Worker(__filename, { | ||
| 313 | + workerData: { level: level + 1 }, | ||
| 314 | + }); | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + if (level === 0) { | ||
| 318 | + process.on('workerMessage', (value, source) => { | ||
| 319 | + console.log(`${source} -> ${threadId}:`, value); | ||
| 320 | + postMessageToThread(source, { message: 'pong' }); | ||
| 321 | + }); | ||
| 322 | + } else if (level === 10) { | ||
| 323 | + process.on('workerMessage', (value, source) => { | ||
| 324 | + console.log(`${source} -> ${threadId}:`, value); | ||
| 325 | + channel.postMessage('done'); | ||
| 326 | + channel.close(); | ||
| 327 | + }); | ||
| 328 | + | ||
| 329 | + postMessageToThread(0, { message: 'ping' }); | ||
| 330 | + } | ||
| 331 | + | ||
| 332 | + channel.onmessage = channel.close; | ||
| 333 | + ``` | ||
| 334 | + | ||
| 223 | 335 | ## `worker.receiveMessageOnPort(port)` | |
| 224 | 336 | ||
| 225 | 337 | <!-- YAML | |
@@ -1361,6 +1473,10 @@ thread spawned will spawn another until the application crashes. | |||
| 1361 | 1473 | [`Buffer.allocUnsafe()`]: buffer.md#static-method-bufferallocunsafesize | |
| 1362 | 1474 | [`Buffer`]: buffer.md | |
| 1363 | 1475 | [`ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST`]: errors.md#err_missing_message_port_in_transfer_list | |
| 1476 | + [`ERR_WORKER_MESSAGING_ERRORED`]: errors.md#err_worker_messaging_errored | ||
| 1477 | + [`ERR_WORKER_MESSAGING_FAILED`]: errors.md#err_worker_messaging_failed | ||
| 1478 | + [`ERR_WORKER_MESSAGING_SAME_THREAD`]: errors.md#err_worker_messaging_same_thread | ||
| 1479 | + [`ERR_WORKER_MESSAGING_TIMEOUT`]: errors.md#err_worker_messaging_timeout | ||
| 1364 | 1480 | [`ERR_WORKER_NOT_RUNNING`]: errors.md#err_worker_not_running | |
| 1365 | 1481 | [`EventTarget`]: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget | |
| 1366 | 1482 | [`FileHandle`]: fs.md#class-filehandle | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1911,6 +1911,10 @@ E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error); | |||
| 1911 | 1911 | E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') => | |
| 1912 | 1912 | `Initiated Worker with ${msg}: ${ArrayPrototypeJoin(errors, ', ')}`, | |
| 1913 | 1913 | Error); | |
| 1914 | + E('ERR_WORKER_MESSAGING_ERRORED', 'The destination thread threw an error while processing the message', Error); | ||
| 1915 | + E('ERR_WORKER_MESSAGING_FAILED', 'Cannot find the destination thread or listener', Error); | ||
| 1916 | + E('ERR_WORKER_MESSAGING_SAME_THREAD', 'Cannot sent a message to the same thread', Error); | ||
| 1917 | + E('ERR_WORKER_MESSAGING_TIMEOUT', 'Sending a message to another thread timed out', Error); | ||
| 1914 | 1918 | E('ERR_WORKER_NOT_RUNNING', 'Worker instance not running', Error); | |
| 1915 | 1919 | E('ERR_WORKER_OUT_OF_MEMORY', | |
| 1916 | 1920 | 'Worker terminated due to reaching memory limit: %s', Error); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -44,6 +44,8 @@ const { | |||
| 44 | 44 | kStdioWantsMoreDataCallback, | |
| 45 | 45 | } = workerIo; | |
| 46 | 46 | ||
| 47 | + const { setupMainThreadPort } = require('internal/worker/messaging'); | ||
| 48 | + | ||
| 47 | 49 | const { | |
| 48 | 50 | onGlobalUncaughtException, | |
| 49 | 51 | } = require('internal/process/execution'); | |
@@ -99,6 +101,7 @@ port.on('message', (message) => { | |||
| 99 | 101 | manifestURL, | |
| 100 | 102 | publicPort, | |
| 101 | 103 | workerData, | |
| 104 | + mainThreadPort, | ||
| 102 | 105 | } = message; | |
| 103 | 106 | ||
| 104 | 107 | if (doEval !== 'internal') { | |
@@ -112,6 +115,7 @@ port.on('message', (message) => { | |||
| 112 | 115 | } | |
| 113 | 116 | ||
| 114 | 117 | require('internal/worker').assignEnvironmentData(environmentData); | |
| 118 | + setupMainThreadPort(mainThreadPort); | ||
| 115 | 119 | ||
| 116 | 120 | if (SharedArrayBuffer !== undefined && Atomics !== undefined) { | |
| 117 | 121 | // The counter is only passed to the workers created by the main thread, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -56,6 +56,7 @@ const { | |||
| 56 | 56 | ReadableWorkerStdio, | |
| 57 | 57 | WritableWorkerStdio, | |
| 58 | 58 | } = workerIo; | |
| 59 | + const { createMainThreadPort, destroyMainThreadPort } = require('internal/worker/messaging'); | ||
| 59 | 60 | const { deserializeError } = require('internal/error_serdes'); | |
| 60 | 61 | const { fileURLToPath, isURL, pathToFileURL } = require('internal/url'); | |
| 61 | 62 | const { kEmptyObject } = require('internal/util'); | |
@@ -251,14 +252,18 @@ class Worker extends EventEmitter { | |||
| 251 | 252 | ||
| 252 | 253 | this[kParentSideStdio] = { stdin, stdout, stderr }; | |
| 253 | 254 | ||
| 254 | - const { port1, port2 } = new MessageChannel(); | ||
| 255 | - const transferList = [port2]; | ||
| 255 | + const mainThreadPortToWorker = createMainThreadPort(this.threadId); | ||
| 256 | + const { | ||
| 257 | + port1: publicPortToParent, | ||
| 258 | + port2: publicPortToWorker, | ||
| 259 | + } = new MessageChannel(); | ||
| 260 | + const transferList = [mainThreadPortToWorker, publicPortToWorker]; | ||
| 256 | 261 | // If transferList is provided. | |
| 257 | 262 | if (options.transferList) | |
| 258 | 263 | ArrayPrototypePush(transferList, | |
| 259 | 264 | ...new SafeArrayIterator(options.transferList)); | |
| 260 | 265 | ||
| 261 | - this[kPublicPort] = port1; | ||
| 266 | + this[kPublicPort] = publicPortToParent; | ||
| 262 | 267 | ArrayPrototypeForEach(['message', 'messageerror'], (event) => { | |
| 263 | 268 | this[kPublicPort].on(event, (message) => this.emit(event, message)); | |
| 264 | 269 | }); | |
@@ -272,14 +277,15 @@ class Worker extends EventEmitter { | |||
| 272 | 277 | cwdCounter: cwdCounter || workerIo.sharedCwdCounter, | |
| 273 | 278 | workerData: options.workerData, | |
| 274 | 279 | environmentData, | |
| 275 | - publicPort: port2, | ||
| 276 | 280 | manifestURL: getOptionValue('--experimental-policy') ? | |
| 277 | 281 | require('internal/process/policy').url : | |
| 278 | 282 | null, | |
| 279 | 283 | manifestSrc: getOptionValue('--experimental-policy') ? | |
| 280 | 284 | require('internal/process/policy').src : | |
| 281 | 285 | null, | |
| 282 | 286 | hasStdin: !!options.stdin, | |
| 287 | + publicPort: publicPortToWorker, | ||
| 288 | + mainThreadPort: mainThreadPortToWorker, | ||
| 283 | 289 | }, transferList); | |
| 284 | 290 | // Use this to cache the Worker's loopStart value once available. | |
| 285 | 291 | this[kLoopStartTime] = -1; | |
@@ -302,6 +308,7 @@ class Worker extends EventEmitter { | |||
| 302 | 308 | debug(`[${threadId}] hears end event for Worker ${this.threadId}`); | |
| 303 | 309 | drainMessagePort(this[kPublicPort]); | |
| 304 | 310 | drainMessagePort(this[kPort]); | |
| 311 | + destroyMainThreadPort(this.threadId); | ||
| 305 | 312 | this.removeAllListeners('message'); | |
| 306 | 313 | this.removeAllListeners('messageerrors'); | |
| 307 | 314 | this[kPublicPort].unref(); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments