| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 56188fe commit 4c317ce
13 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,16 +7,25 @@ if (cluster.isMaster) { | |||
| 7 | 7 | workers: [1], | |
| 8 | 8 | payload: ['string', 'object'], | |
| 9 | 9 | sendsPerBroadcast: [1, 10], | |
| 10 | + serialization: ['json', 'advanced'], | ||
| 10 | 11 | n: [1e5] | |
| 11 | 12 | }); | |
| 12 | 13 | ||
| 13 | - function main({ n, workers, sendsPerBroadcast, payload }) { | ||
| 14 | + function main({ | ||
| 15 | + n, | ||
| 16 | + workers, | ||
| 17 | + sendsPerBroadcast, | ||
| 18 | + payload, | ||
| 19 | + serialization | ||
| 20 | + }) { | ||
| 14 | 21 | const expectedPerBroadcast = sendsPerBroadcast * workers; | |
| 15 | 22 | var readies = 0; | |
| 16 | 23 | var broadcasts = 0; | |
| 17 | 24 | var msgCount = 0; | |
| 18 | 25 | var data; | |
| 19 | 26 | ||
| 27 | + cluster.settings.serialization = serialization; | ||
| 28 | + | ||
| 20 | 29 | switch (payload) { | |
| 21 | 30 | case 'string': | |
| 22 | 31 | data = 'hello world!'; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -321,6 +321,9 @@ arbitrary command execution.** | |||
| 321 | 321 | <!-- YAML | |
| 322 | 322 | added: v0.5.0 | |
| 323 | 323 | changes: | |
| 324 | + - version: REPLACEME | ||
| 325 | + pr-url: https://github.com/nodejs/node/pull/30162 | ||
| 326 | + description: The `serialization` option is supported now. | ||
| 324 | 327 | - version: v8.0.0 | |
| 325 | 328 | pr-url: https://github.com/nodejs/node/pull/10866 | |
| 326 | 329 | description: The `stdio` option can now be a string. | |
@@ -340,6 +343,9 @@ changes: | |||
| 340 | 343 | * `execPath` {string} Executable used to create the child process. | |
| 341 | 344 | * `execArgv` {string[]} List of string arguments passed to the executable. | |
| 342 | 345 | **Default:** `process.execArgv`. | |
| 346 | + * `serialization` {string} Specify the kind of serialization used for sending | ||
| 347 | + messages between processes. Possible values are `'json'` and `'advanced'`. | ||
| 348 | + See [Advanced Serialization][] for more details. **Default:** `'json'`. | ||
| 343 | 349 | * `silent` {boolean} If `true`, stdin, stdout, and stderr of the child will be | |
| 344 | 350 | piped to the parent, otherwise they will be inherited from the parent, see | |
| 345 | 351 | the `'pipe'` and `'inherit'` options for [`child_process.spawn()`][]'s | |
@@ -386,6 +392,9 @@ The `shell` option available in [`child_process.spawn()`][] is not supported by | |||
| 386 | 392 | <!-- YAML | |
| 387 | 393 | added: v0.1.90 | |
| 388 | 394 | changes: | |
| 395 | + - version: REPLACEME | ||
| 396 | + pr-url: https://github.com/nodejs/node/pull/30162 | ||
| 397 | + description: The `serialization` option is supported now. | ||
| 389 | 398 | - version: v8.8.0 | |
| 390 | 399 | pr-url: https://github.com/nodejs/node/pull/15380 | |
| 391 | 400 | description: The `windowsHide` option is supported now. | |
@@ -411,6 +420,9 @@ changes: | |||
| 411 | 420 | [`options.detached`][]). | |
| 412 | 421 | * `uid` {number} Sets the user identity of the process (see setuid(2)). | |
| 413 | 422 | * `gid` {number} Sets the group identity of the process (see setgid(2)). | |
| 423 | + * `serialization` {string} Specify the kind of serialization used for sending | ||
| 424 | + messages between processes. Possible values are `'json'` and `'advanced'`. | ||
| 425 | + See [Advanced Serialization][] for more details. **Default:** `'json'`. | ||
| 414 | 426 | * `shell` {boolean|string} If `true`, runs `command` inside of a shell. Uses | |
| 415 | 427 | `'/bin/sh'` on Unix, and `process.env.ComSpec` on Windows. A different | |
| 416 | 428 | shell can be specified as a string. See [Shell Requirements][] and | |
@@ -998,6 +1010,11 @@ The `'message'` event is triggered when a child process uses | |||
| 998 | 1010 | The message goes through serialization and parsing. The resulting | |
| 999 | 1011 | message might not be the same as what is originally sent. | |
| 1000 | 1012 | ||
| 1013 | + If the `serialization` option was set to `'advanced'` used when spawning the | ||
| 1014 | + child process, the `message` argument can contain data that JSON is not able | ||
| 1015 | + to represent. | ||
| 1016 | + See [Advanced Serialization][] for more details. | ||
| 1017 | + | ||
| 1001 | 1018 | ### `subprocess.channel` | |
| 1002 | 1019 | <!-- YAML | |
| 1003 | 1020 | added: v7.1.0 | |
@@ -1474,6 +1491,26 @@ the same requirement. Thus, in `child_process` functions where a shell can be | |||
| 1474 | 1491 | spawned, `'cmd.exe'` is used as a fallback if `process.env.ComSpec` is | |
| 1475 | 1492 | unavailable. | |
| 1476 | 1493 | ||
| 1494 | + ## Advanced Serialization | ||
| 1495 | + <!-- YAML | ||
| 1496 | + added: REPLACEME | ||
| 1497 | + --> | ||
| 1498 | + | ||
| 1499 | + Child processes support a serialization mechanism for IPC that is based on the | ||
| 1500 | + [serialization API of the `v8` module][v8.serdes], based on the | ||
| 1501 | + [HTML structured clone algorithm][]. This is generally more powerful and | ||
| 1502 | + supports more built-in JavaScript object types, such as `BigInt`, `Map` | ||
| 1503 | + and `Set`, `ArrayBuffer` and `TypedArray`, `Buffer`, `Error`, `RegExp` etc. | ||
| 1504 | + | ||
| 1505 | + However, this format is not a full superset of JSON, and e.g. properties set on | ||
| 1506 | + objects of such built-in types will not be passed on through the serialization | ||
| 1507 | + step. Additionally, performance may not be equivalent to that of JSON, depending | ||
| 1508 | + on the structure of the passed data. | ||
| 1509 | + Therefore, this feature requires opting in by setting the | ||
| 1510 | + `serialization` option to `'advanced'` when calling [`child_process.spawn()`][] | ||
| 1511 | + or [`child_process.fork()`][]. | ||
| 1512 | + | ||
| 1513 | + [Advanced Serialization]: #child_process_advanced_serialization | ||
| 1477 | 1514 | [`'disconnect'`]: process.html#process_event_disconnect | |
| 1478 | 1515 | [`'error'`]: #child_process_event_error | |
| 1479 | 1516 | [`'exit'`]: #child_process_event_exit | |
@@ -1507,5 +1544,7 @@ unavailable. | |||
| 1507 | 1544 | [`subprocess.stdout`]: #child_process_subprocess_stdout | |
| 1508 | 1545 | [`util.promisify()`]: util.html#util_util_promisify_original | |
| 1509 | 1546 | [Default Windows Shell]: #child_process_default_windows_shell | |
| 1547 | + [HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm | ||
| 1510 | 1548 | [Shell Requirements]: #child_process_shell_requirements | |
| 1511 | 1549 | [synchronous counterparts]: #child_process_synchronous_process_creation | |
| 1550 | + [v8.serdes]: v8.html#v8_serialization_api | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -724,6 +724,9 @@ values are `'rr'` and `'none'`. | |||
| 724 | 724 | <!-- YAML | |
| 725 | 725 | added: v0.7.1 | |
| 726 | 726 | changes: | |
| 727 | + - version: REPLACEME | ||
| 728 | + pr-url: https://github.com/nodejs/node/pull/30162 | ||
| 729 | + description: The `serialization` option is supported now. | ||
| 727 | 730 | - version: v9.5.0 | |
| 728 | 731 | pr-url: https://github.com/nodejs/node/pull/18399 | |
| 729 | 732 | description: The `cwd` option is supported now. | |
@@ -746,6 +749,10 @@ changes: | |||
| 746 | 749 | **Default:** `process.argv.slice(2)`. | |
| 747 | 750 | * `cwd` {string} Current working directory of the worker process. **Default:** | |
| 748 | 751 | `undefined` (inherits from parent process). | |
| 752 | + * `serialization` {string} Specify the kind of serialization used for sending | ||
| 753 | + messages between processes. Possible values are `'json'` and `'advanced'`. | ||
| 754 | + See [Advanced Serialization for `child_process`][] for more details. | ||
| 755 | + **Default:** `false`. | ||
| 749 | 756 | * `silent` {boolean} Whether or not to send output to parent's stdio. | |
| 750 | 757 | **Default:** `false`. | |
| 751 | 758 | * `stdio` {Array} Configures the stdio of forked processes. Because the | |
@@ -874,4 +881,5 @@ socket.on('data', (id) => { | |||
| 874 | 881 | [`process` event: `'message'`]: process.html#process_event_message | |
| 875 | 882 | [`server.close()`]: net.html#net_event_close | |
| 876 | 883 | [`worker.exitedAfterDisconnect`]: #cluster_worker_exitedafterdisconnect | |
| 884 | + [Advanced Serialization for `child_process`]: child_process.html#child_process_advanced_serialization | ||
| 877 | 885 | [Child Process module]: child_process.html#child_process_child_process_fork_modulepath_args_options | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -119,6 +119,11 @@ the child process. | |||
| 119 | 119 | The message goes through serialization and parsing. The resulting message might | |
| 120 | 120 | not be the same as what is originally sent. | |
| 121 | 121 | ||
| 122 | + If the `serialization` option was set to `advanced` used when spawning the | ||
| 123 | + process, the `message` argument can contain data that JSON is not able | ||
| 124 | + to represent. | ||
| 125 | + See [Advanced Serialization for `child_process`][] for more details. | ||
| 126 | + | ||
| 122 | 127 | ### Event: `'multipleResolves'` | |
| 123 | 128 | <!-- YAML | |
| 124 | 129 | added: v10.12.0 | |
@@ -2457,6 +2462,7 @@ cases: | |||
| 2457 | 2462 | [`require.resolve()`]: modules.html#modules_require_resolve_request_options | |
| 2458 | 2463 | [`subprocess.kill()`]: child_process.html#child_process_subprocess_kill_signal | |
| 2459 | 2464 | [`v8.setFlagsFromString()`]: v8.html#v8_v8_setflagsfromstring_flags | |
| 2465 | + [Advanced Serialization for `child_process`]: child_process.html#child_process_advanced_serialization | ||
| 2460 | 2466 | [Android building]: https://github.com/nodejs/node/blob/master/BUILDING.md#androidandroid-based-devices-eg-firefox-os | |
| 2461 | 2467 | [Child Process]: child_process.html | |
| 2462 | 2468 | [Cluster]: cluster.html | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -108,12 +108,12 @@ function fork(modulePath /* , args, options */) { | |||
| 108 | 108 | return spawn(options.execPath, args, options); | |
| 109 | 109 | } | |
| 110 | 110 | ||
| 111 | - function _forkChild(fd) { | ||
| 111 | + function _forkChild(fd, serializationMode) { | ||
| 112 | 112 | // set process.send() | |
| 113 | 113 | const p = new Pipe(PipeConstants.IPC); | |
| 114 | 114 | p.open(fd); | |
| 115 | 115 | p.unref(); | |
| 116 | - const control = setupChannel(process, p); | ||
| 116 | + const control = setupChannel(process, p, serializationMode); | ||
| 117 | 117 | process.on('newListener', function onNewListener(name) { | |
| 118 | 118 | if (name === 'message' || name === 'disconnect') control.ref(); | |
| 119 | 119 | }); | |
@@ -547,7 +547,8 @@ function spawn(file, args, options) { | |||
| 547 | 547 | envPairs: opts.envPairs, | |
| 548 | 548 | stdio: options.stdio, | |
| 549 | 549 | uid: options.uid, | |
| 550 | - gid: options.gid | ||
| 550 | + gid: options.gid, | ||
| 551 | + serialization: options.serialization, | ||
| 551 | 552 | }); | |
| 552 | 553 | ||
| 553 | 554 | return child; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -326,7 +326,11 @@ function setupChildProcessIpcChannel() { | |||
| 326 | 326 | // Make sure it's not accidentally inherited by child processes. | |
| 327 | 327 | delete process.env.NODE_CHANNEL_FD; | |
| 328 | 328 | ||
| 329 | - require('child_process')._forkChild(fd); | ||
| 329 | + const serializationMode = | ||
| 330 | + process.env.NODE_CHANNEL_SERIALIZATION_MODE || 'json'; | ||
| 331 | + delete process.env.NODE_CHANNEL_SERIALIZATION_MODE; | ||
| 332 | + | ||
| 333 | + require('child_process')._forkChild(fd, serializationMode); | ||
| 330 | 334 | assert(process.send); | |
| 331 | 335 | } | |
| 332 | 336 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,6 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | ||
| 3 | - const { JSON, Object } = primordials; | ||
| 3 | + const { Object } = primordials; | ||
| 4 | 4 | ||
| 5 | 5 | const { | |
| 6 | 6 | errnoException, | |
@@ -55,8 +55,6 @@ const { | |||
| 55 | 55 | ||
| 56 | 56 | const { SocketListSend, SocketListReceive } = SocketList; | |
| 57 | 57 | ||
| 58 | - // Lazy loaded for startup performance. | ||
| 59 | - let StringDecoder; | ||
| 60 | 58 | // Lazy loaded for startup performance and to allow monkey patching of | |
| 61 | 59 | // internalBinding('http_parser').HTTPParser. | |
| 62 | 60 | let freeParser; | |
@@ -343,6 +341,15 @@ ChildProcess.prototype.spawn = function(options) { | |||
| 343 | 341 | const ipcFd = stdio.ipcFd; | |
| 344 | 342 | stdio = options.stdio = stdio.stdio; | |
| 345 | 343 | ||
| 344 | + if (options.serialization !== undefined && | ||
| 345 | + options.serialization !== 'json' && | ||
| 346 | + options.serialization !== 'advanced') { | ||
| 347 | + throw new ERR_INVALID_OPT_VALUE('options.serialization', | ||
| 348 | + options.serialization); | ||
| 349 | + } | ||
| 350 | + | ||
| 351 | + const serialization = options.serialization || 'json'; | ||
| 352 | + | ||
| 346 | 353 | if (ipc !== undefined) { | |
| 347 | 354 | // Let child process know about opened IPC channel | |
| 348 | 355 | if (options.envPairs === undefined) | |
@@ -353,7 +360,8 @@ ChildProcess.prototype.spawn = function(options) { | |||
| 353 | 360 | options.envPairs); | |
| 354 | 361 | } | |
| 355 | 362 | ||
| 356 | - options.envPairs.push('NODE_CHANNEL_FD=' + ipcFd); | ||
| 363 | + options.envPairs.push(`NODE_CHANNEL_FD=${ipcFd}`); | ||
| 364 | + options.envPairs.push(`NODE_CHANNEL_SERIALIZATION_MODE=${serialization}`); | ||
| 357 | 365 | } | |
| 358 | 366 | ||
| 359 | 367 | validateString(options.file, 'options.file'); | |
@@ -446,7 +454,7 @@ ChildProcess.prototype.spawn = function(options) { | |||
| 446 | 454 | this.stdio.push(stdio[i].socket === undefined ? null : stdio[i].socket); | |
| 447 | 455 | ||
| 448 | 456 | // Add .send() method and start listening for IPC data | |
| 449 | - if (ipc !== undefined) setupChannel(this, ipc); | ||
| 457 | + if (ipc !== undefined) setupChannel(this, ipc, serialization); | ||
| 450 | 458 | ||
| 451 | 459 | return err; | |
| 452 | 460 | }; | |
@@ -513,7 +521,8 @@ class Control extends EventEmitter { | |||
| 513 | 521 | } | |
| 514 | 522 | } | |
| 515 | 523 | ||
| 516 | - function setupChannel(target, channel) { | ||
| 524 | + let serialization; | ||
| 525 | + function setupChannel(target, channel, serializationMode) { | ||
| 517 | 526 | target.channel = channel; | |
| 518 | 527 | ||
| 519 | 528 | // _channel can be deprecated in version 8 | |
@@ -528,12 +537,16 @@ function setupChannel(target, channel) { | |||
| 528 | 537 | ||
| 529 | 538 | const control = new Control(channel); | |
| 530 | 539 | ||
| 531 | - if (StringDecoder === undefined) | ||
| 532 | - StringDecoder = require('string_decoder').StringDecoder; | ||
| 533 | - const decoder = new StringDecoder('utf8'); | ||
| 534 | - var jsonBuffer = ''; | ||
| 535 | - var pendingHandle = null; | ||
| 536 | - channel.buffering = false; | ||
| 540 | + if (serialization === undefined) | ||
| 541 | + serialization = require('internal/child_process/serialization'); | ||
| 542 | + const { | ||
| 543 | + initMessageChannel, | ||
| 544 | + parseChannelMessages, | ||
| 545 | + writeChannelMessage | ||
| 546 | + } = serialization[serializationMode]; | ||
| 547 | + | ||
| 548 | + let pendingHandle = null; | ||
| 549 | + initMessageChannel(channel); | ||
| 537 | 550 | channel.pendingHandle = null; | |
| 538 | 551 | channel.onread = function(arrayBuffer) { | |
| 539 | 552 | const recvHandle = channel.pendingHandle; | |
@@ -545,21 +558,7 @@ function setupChannel(target, channel) { | |||
| 545 | 558 | if (recvHandle) | |
| 546 | 559 | pendingHandle = recvHandle; | |
| 547 | 560 | ||
| 548 | - // Linebreak is used as a message end sign | ||
| 549 | - var chunks = decoder.write(pool).split('\n'); | ||
| 550 | - var numCompleteChunks = chunks.length - 1; | ||
| 551 | - // Last line does not have trailing linebreak | ||
| 552 | - var incompleteChunk = chunks[numCompleteChunks]; | ||
| 553 | - if (numCompleteChunks === 0) { | ||
| 554 | - jsonBuffer += incompleteChunk; | ||
| 555 | - this.buffering = jsonBuffer.length !== 0; | ||
| 556 | - return; | ||
| 557 | - } | ||
| 558 | - chunks[0] = jsonBuffer + chunks[0]; | ||
| 559 | - | ||
| 560 | - for (var i = 0; i < numCompleteChunks; i++) { | ||
| 561 | - var message = JSON.parse(chunks[i]); | ||
| 562 | - | ||
| 561 | + for (const message of parseChannelMessages(channel, pool)) { | ||
| 563 | 562 | // There will be at most one NODE_HANDLE message in every chunk we | |
| 564 | 563 | // read because SCM_RIGHTS messages don't get coalesced. Make sure | |
| 565 | 564 | // that we deliver the handle with the right message however. | |
@@ -574,9 +573,6 @@ function setupChannel(target, channel) { | |||
| 574 | 573 | handleMessage(message, undefined, false); | |
| 575 | 574 | } | |
| 576 | 575 | } | |
| 577 | - jsonBuffer = incompleteChunk; | ||
| 578 | - this.buffering = jsonBuffer.length !== 0; | ||
| 579 | - | ||
| 580 | 576 | } else { | |
| 581 | 577 | this.buffering = false; | |
| 582 | 578 | target.disconnect(); | |
@@ -775,8 +771,7 @@ function setupChannel(target, channel) { | |||
| 775 | 771 | ||
| 776 | 772 | const req = new WriteWrap(); | |
| 777 | 773 | ||
| 778 | - const string = JSON.stringify(message) + '\n'; | ||
| 779 | - const err = channel.writeUtf8String(req, string, handle); | ||
| 774 | + const err = writeChannelMessage(channel, req, message, handle); | ||
| 780 | 775 | const wasAsyncWrite = streamBaseState[kLastWriteWasAsync]; | |
| 781 | 776 | ||
| 782 | 777 | if (err === 0) { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments