| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -374,6 +374,9 @@ controller.abort(); | |||
| 374 | 374 | <!-- YAML | |
| 375 | 375 | added: v0.5.0 | |
| 376 | 376 | changes: | |
| 377 | + - version: REPLACEME | ||
| 378 | + pr-url: https://github.com/nodejs/node/pull/37256 | ||
| 379 | + description: timeout was added. | ||
| 377 | 380 | - version: REPLACEME | |
| 378 | 381 | pr-url: https://github.com/nodejs/node/pull/37325 | |
| 379 | 382 | description: killSignal for AbortSignal was added. | |
@@ -410,8 +413,8 @@ changes: | |||
| 410 | 413 | See [Advanced serialization][] for more details. **Default:** `'json'`. | |
| 411 | 414 | * `signal` {AbortSignal} Allows closing the child process using an | |
| 412 | 415 | AbortSignal. | |
| 413 | - * `killSignal` {string} The signal value to be used when the spawned | ||
| 414 | - process will be killed by the abort signal. **Default:** `'SIGTERM'`. | ||
| 416 | + * `killSignal` {string|integer} The signal value to be used when the spawned | ||
| 417 | + process will be killed by timeout or abort signal. **Default:** `'SIGTERM'`. | ||
| 415 | 418 | * `silent` {boolean} If `true`, stdin, stdout, and stderr of the child will be | |
| 416 | 419 | piped to the parent, otherwise they will be inherited from the parent, see | |
| 417 | 420 | the `'pipe'` and `'inherit'` options for [`child_process.spawn()`][]'s | |
@@ -423,6 +426,8 @@ changes: | |||
| 423 | 426 | * `uid` {number} Sets the user identity of the process (see setuid(2)). | |
| 424 | 427 | * `windowsVerbatimArguments` {boolean} No quoting or escaping of arguments is | |
| 425 | 428 | done on Windows. Ignored on Unix. **Default:** `false`. | |
| 429 | + * `timeout` {number} In milliseconds the maximum amount of time the process | ||
| 430 | + is allowed to run. **Default:** `undefined`. | ||
| 426 | 431 | * Returns: {ChildProcess} | |
| 427 | 432 | ||
| 428 | 433 | The `child_process.fork()` method is a special case of | |
@@ -478,6 +483,9 @@ if (process.argv[2] === 'child') { | |||
| 478 | 483 | <!-- YAML | |
| 479 | 484 | added: v0.1.90 | |
| 480 | 485 | changes: | |
| 486 | + - version: REPLACEME | ||
| 487 | + pr-url: https://github.com/nodejs/node/pull/37256 | ||
| 488 | + description: timeout was added. | ||
| 481 | 489 | - version: REPLACEME | |
| 482 | 490 | pr-url: https://github.com/nodejs/node/pull/37325 | |
| 483 | 491 | description: killSignal for AbortSignal was added. | |
@@ -528,8 +536,10 @@ changes: | |||
| 528 | 536 | normally be created on Windows systems. **Default:** `false`. | |
| 529 | 537 | * `signal` {AbortSignal} allows aborting the child process using an | |
| 530 | 538 | AbortSignal. | |
| 531 | - * `killSignal` {string} The signal value to be used when the spawned | ||
| 532 | - process will be killed by the abort signal. **Default:** `'SIGTERM'`. | ||
| 539 | + * `timeout` {number} In milliseconds the maximum amount of time the process | ||
| 540 | + is allowed to run. **Default:** `undefined`. | ||
| 541 | + * `killSignal` {string|integer} The signal value to be used when the spawned | ||
| 542 | + process will be killed by timeout or abort signal. **Default:** `'SIGTERM'`. | ||
| 533 | 543 | ||
| 534 | 544 | * Returns: {ChildProcess} | |
| 535 | 545 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -646,15 +646,14 @@ function abortChildProcess(child, killSignal) { | |||
| 646 | 646 | * @returns {ChildProcess} | |
| 647 | 647 | */ | |
| 648 | 648 | function spawn(file, args, options) { | |
| 649 | - const child = new ChildProcess(); | ||
| 650 | 649 | options = normalizeSpawnArguments(file, args, options); | |
| 650 | + validateTimeout(options.timeout, 'options.timeout'); | ||
| 651 | + validateAbortSignal(options.signal, 'options.signal'); | ||
| 652 | + const killSignal = sanitizeKillSignal(options.killSignal); | ||
| 653 | + const child = new ChildProcess(); | ||
| 651 | 654 | ||
| 652 | 655 | if (options.signal) { | |
| 653 | 656 | const signal = options.signal; | |
| 654 | - // Validate signal, if present | ||
| 655 | - validateAbortSignal(signal, 'options.signal'); | ||
| 656 | - const killSignal = sanitizeKillSignal(options.killSignal); | ||
| 657 | - // Do nothing and throw if already aborted | ||
| 658 | 657 | if (signal.aborted) { | |
| 659 | 658 | onAbortListener(); | |
| 660 | 659 | } else { | |
@@ -673,6 +672,26 @@ function spawn(file, args, options) { | |||
| 673 | 672 | debug('spawn', options); | |
| 674 | 673 | child.spawn(options); | |
| 675 | 674 | ||
| 675 | + if (options.timeout > 0) { | ||
| 676 | + let timeoutId = setTimeout(() => { | ||
| 677 | + if (timeoutId) { | ||
| 678 | + try { | ||
| 679 | + child.kill(killSignal); | ||
| 680 | + } catch (err) { | ||
| 681 | + child.emit('error', err); | ||
| 682 | + } | ||
| 683 | + timeoutId = null; | ||
| 684 | + } | ||
| 685 | + }, options.timeout); | ||
| 686 | + | ||
| 687 | + child.once('exit', () => { | ||
| 688 | + if (timeoutId) { | ||
| 689 | + clearTimeout(timeoutId); | ||
| 690 | + timeoutId = null; | ||
| 691 | + } | ||
| 692 | + }); | ||
| 693 | + } | ||
| 694 | + | ||
| 676 | 695 | return child; | |
| 677 | 696 | } | |
| 678 | 697 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,54 @@ | |||
| 1 | + // Flags: --expose-internals | ||
| 2 | + 'use strict'; | ||
| 3 | + | ||
| 4 | + const { mustCall } = require('../common'); | ||
| 5 | + const { strictEqual, throws } = require('assert'); | ||
| 6 | + const fixtures = require('../common/fixtures'); | ||
| 7 | + const { fork } = require('child_process'); | ||
| 8 | + const { getEventListeners } = require('events'); | ||
| 9 | + const { | ||
| 10 | + EventTarget, | ||
| 11 | + } = require('internal/event_target'); | ||
| 12 | + | ||
| 13 | + { | ||
| 14 | + // Verify default signal | ||
| 15 | + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { | ||
| 16 | + timeout: 5, | ||
| 17 | + }); | ||
| 18 | + cp.on('exit', mustCall((code, ks) => strictEqual(ks, 'SIGTERM'))); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + { | ||
| 22 | + // Verify correct signal + closes after at least 4 ms. | ||
| 23 | + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { | ||
| 24 | + timeout: 5, | ||
| 25 | + killSignal: 'SIGKILL', | ||
| 26 | + }); | ||
| 27 | + cp.on('exit', mustCall((code, ks) => strictEqual(ks, 'SIGKILL'))); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + { | ||
| 31 | + // Verify timeout verification | ||
| 32 | + throws(() => fork(fixtures.path('child-process-stay-alive-forever.js'), { | ||
| 33 | + timeout: 'badValue', | ||
| 34 | + }), /ERR_OUT_OF_RANGE/); | ||
| 35 | + | ||
| 36 | + throws(() => fork(fixtures.path('child-process-stay-alive-forever.js'), { | ||
| 37 | + timeout: {}, | ||
| 38 | + }), /ERR_OUT_OF_RANGE/); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + { | ||
| 42 | + // Verify abort signal gets unregistered | ||
| 43 | + const signal = new EventTarget(); | ||
| 44 | + signal.aborted = false; | ||
| 45 | + | ||
| 46 | + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { | ||
| 47 | + timeout: 6, | ||
| 48 | + signal, | ||
| 49 | + }); | ||
| 50 | + strictEqual(getEventListeners(signal, 'abort').length, 1); | ||
| 51 | + cp.on('exit', mustCall(() => { | ||
| 52 | + strictEqual(getEventListeners(signal, 'abort').length, 0); | ||
| 53 | + })); | ||
| 54 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,51 @@ | |||
| 1 | + // Flags: --experimental-abortcontroller | ||
| 2 | + 'use strict'; | ||
| 3 | + | ||
| 4 | + const { mustCall } = require('../common'); | ||
| 5 | + const { strictEqual, throws } = require('assert'); | ||
| 6 | + const fixtures = require('../common/fixtures'); | ||
| 7 | + const { spawn } = require('child_process'); | ||
| 8 | + const { getEventListeners } = require('events'); | ||
| 9 | + | ||
| 10 | + const aliveForeverFile = 'child-process-stay-alive-forever.js'; | ||
| 11 | + { | ||
| 12 | + // Verify default signal + closes | ||
| 13 | + const cp = spawn(process.execPath, [fixtures.path(aliveForeverFile)], { | ||
| 14 | + timeout: 5, | ||
| 15 | + }); | ||
| 16 | + cp.on('exit', mustCall((code, ks) => strictEqual(ks, 'SIGTERM'))); | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + { | ||
| 20 | + // Verify SIGKILL signal + closes | ||
| 21 | + const cp = spawn(process.execPath, [fixtures.path(aliveForeverFile)], { | ||
| 22 | + timeout: 6, | ||
| 23 | + killSignal: 'SIGKILL', | ||
| 24 | + }); | ||
| 25 | + cp.on('exit', mustCall((code, ks) => strictEqual(ks, 'SIGKILL'))); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + { | ||
| 29 | + // Verify timeout verification | ||
| 30 | + throws(() => spawn(process.execPath, [fixtures.path(aliveForeverFile)], { | ||
| 31 | + timeout: 'badValue', | ||
| 32 | + }), /ERR_OUT_OF_RANGE/); | ||
| 33 | + | ||
| 34 | + throws(() => spawn(process.execPath, [fixtures.path(aliveForeverFile)], { | ||
| 35 | + timeout: {}, | ||
| 36 | + }), /ERR_OUT_OF_RANGE/); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + { | ||
| 40 | + // Verify abort signal gets unregistered | ||
| 41 | + const controller = new AbortController(); | ||
| 42 | + const { signal } = controller; | ||
| 43 | + const cp = spawn(process.execPath, [fixtures.path(aliveForeverFile)], { | ||
| 44 | + timeout: 6, | ||
| 45 | + signal, | ||
| 46 | + }); | ||
| 47 | + strictEqual(getEventListeners(signal, 'abort').length, 1); | ||
| 48 | + cp.on('exit', mustCall(() => { | ||
| 49 | + strictEqual(getEventListeners(signal, 'abort').length, 0); | ||
| 50 | + })); | ||
| 51 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments