From 9fcef461bb7419b7e8aa0db76377fb1b29f95fa1 Mon Sep 17 00:00:00 2001 From: Robert Jefe Lindstaedt Date: Wed, 27 Apr 2016 14:53:24 +0200 Subject: [PATCH 1/2] process: adds process.children() returning list of pids motivation: parent processes may not have a complete references to their children. This would inroduce a way of storing data as pids in the process object. Further work can be done, to provide a foreigh pid as paremeter and query its children, w/o having had direct reference during child creation --- doc/api/process.md | 28 ++++++++++++++++++++++++ lib/internal/bootstrap_node.js | 1 + lib/internal/child_process.js | 1 + lib/internal/process.js | 8 +++++++ test/parallel/test-process-children.js | 30 ++++++++++++++++++++++++++ 5 files changed, 68 insertions(+) create mode 100644 test/parallel/test-process-children.js diff --git a/doc/api/process.md b/doc/api/process.md index bdadaaea88c1..fc0c4aad543c 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -459,6 +459,34 @@ catch (err) { } ``` +## process.children() + +Returns a list of children `pid`s, the current process has spawned. + +_Note: the child processes do not de-register from their parent automatically. +General bookkeeping must be done by application. This means for certain +subsequent operations the application must check for errors._ + +```js +const spawn = require('child_process').spawn; + +const code = 'setTimeout(() => {}, 400)'; + +spawn(process.argv[0], ['-e', code]); +spawn(process.argv[0], ['-e', code]); + +console.log(process.children().length); +// => [ 12355 , 12359] + +process.children().forEach((el) => { + try { + process.kill(el); + } catch (e) { + console.log('Process was dead already or pid was reassiagned', e); + } +}); +``` + ## process.config An Object containing the JavaScript representation of the configure options diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 195dfbc0fb89..2287bea7b92b 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -53,6 +53,7 @@ NativeModule.require('internal/process/stdio').setup(); _process.setupKillAndExit(); _process.setupSignalHandlers(); + _process.setupChildrenCount(); // Do not initialize channel in debugger agent, it deletes env variable // and the main thread won't see it. diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index ff26e8b75ab5..036398358718 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -303,6 +303,7 @@ ChildProcess.prototype.spawn = function(options) { } this.pid = this._handle.pid; + process.childrenCount.push(this.pid); stdio.forEach(function(stdio, i) { if (stdio.type === 'ignore') return; diff --git a/lib/internal/process.js b/lib/internal/process.js index 17ca5bc326c0..8d6a94dbdacf 100644 --- a/lib/internal/process.js +++ b/lib/internal/process.js @@ -14,6 +14,7 @@ exports.setupConfig = setupConfig; exports.setupKillAndExit = setupKillAndExit; exports.setupSignalHandlers = setupSignalHandlers; exports.setupChannel = setupChannel; +exports.setupChildrenCount = setupChildrenCount; exports.setupRawDebug = setupRawDebug; @@ -177,6 +178,13 @@ function setupChannel() { } +function setupChildrenCount() { + process.childrenCount = []; + process.children = function() { + return this.childrenCount; + }; +} + function setupRawDebug() { var format = require('util').format; var rawDebug = process._rawDebug; diff --git a/test/parallel/test-process-children.js b/test/parallel/test-process-children.js new file mode 100644 index 000000000000..a2707482043e --- /dev/null +++ b/test/parallel/test-process-children.js @@ -0,0 +1,30 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const spawn = require('child_process').spawn; +const execFileSync = require('child_process').execFileSync; + +const code = 'setTimeout(() => {}, 400)'; + +spawn(process.argv[0], ['-e', code]); +spawn(process.argv[0], ['-e', code]); +assert(process.children().length === 2); + +process.children().forEach((el) => { + assert.doesNotThrow(() => { + process.kill(el); + }, Error); +}); + +const nested_child_code = ` + const spawn = require('child_process').spawn; + spawn(process.argv[0], ['-e', 'setTimeout(()=>{})']); + console.log(process.children().length === 1) +`; + +const res = execFileSync(process.argv[0], ['-e', nested_child_code]); +assert(Buffer.compare(res, new Buffer('true\n')) === 0); + +// For later regression: deregistering children is not implemented +assert(process.children().length !== 0); From 1f6f8bb22dee5e8fb0148d16edaaafc23ef2bb4a Mon Sep 17 00:00:00 2001 From: Robert Jefe Lindstaedt Date: Sat, 30 Apr 2016 16:47:46 +0200 Subject: [PATCH 2/2] process: reverts childcounting; adds event 'spawn' --- doc/api/process.md | 35 ++++++-------------------- lib/internal/bootstrap_node.js | 1 - lib/internal/child_process.js | 2 +- lib/internal/process.js | 8 ------ test/parallel/test-process-children.js | 30 ---------------------- test/parallel/test-process-emit.js | 5 ++++ 6 files changed, 13 insertions(+), 68 deletions(-) delete mode 100644 test/parallel/test-process-children.js diff --git a/doc/api/process.md b/doc/api/process.md index fc0c4aad543c..445e6a5af088 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -95,6 +95,13 @@ log, either periodically (probably best for long-running programs, allowing you to clear the map, which in the case of a very buggy program could grow indefinitely) or upon process exit (more convenient for scripts). +## Event: 'spawn' + +Emitted whenever a process calls the internal `child_process.spawn()` and assign +the child a `pid`. This is useful for bookkeeping - especially for subsequent +calls to `process.kill()`, in case no reference to `child` was held and hence +`child.kill()` is not an option. + ## Event: 'uncaughtException' The `'uncaughtException'` event is emitted when an exception bubbles all the @@ -459,34 +466,6 @@ catch (err) { } ``` -## process.children() - -Returns a list of children `pid`s, the current process has spawned. - -_Note: the child processes do not de-register from their parent automatically. -General bookkeeping must be done by application. This means for certain -subsequent operations the application must check for errors._ - -```js -const spawn = require('child_process').spawn; - -const code = 'setTimeout(() => {}, 400)'; - -spawn(process.argv[0], ['-e', code]); -spawn(process.argv[0], ['-e', code]); - -console.log(process.children().length); -// => [ 12355 , 12359] - -process.children().forEach((el) => { - try { - process.kill(el); - } catch (e) { - console.log('Process was dead already or pid was reassiagned', e); - } -}); -``` - ## process.config An Object containing the JavaScript representation of the configure options diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 2287bea7b92b..195dfbc0fb89 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -53,7 +53,6 @@ NativeModule.require('internal/process/stdio').setup(); _process.setupKillAndExit(); _process.setupSignalHandlers(); - _process.setupChildrenCount(); // Do not initialize channel in debugger agent, it deletes env variable // and the main thread won't see it. diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index 036398358718..5cef0f07fb83 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -303,7 +303,7 @@ ChildProcess.prototype.spawn = function(options) { } this.pid = this._handle.pid; - process.childrenCount.push(this.pid); + process.emit('spawn', this.pid); stdio.forEach(function(stdio, i) { if (stdio.type === 'ignore') return; diff --git a/lib/internal/process.js b/lib/internal/process.js index 8d6a94dbdacf..17ca5bc326c0 100644 --- a/lib/internal/process.js +++ b/lib/internal/process.js @@ -14,7 +14,6 @@ exports.setupConfig = setupConfig; exports.setupKillAndExit = setupKillAndExit; exports.setupSignalHandlers = setupSignalHandlers; exports.setupChannel = setupChannel; -exports.setupChildrenCount = setupChildrenCount; exports.setupRawDebug = setupRawDebug; @@ -178,13 +177,6 @@ function setupChannel() { } -function setupChildrenCount() { - process.childrenCount = []; - process.children = function() { - return this.childrenCount; - }; -} - function setupRawDebug() { var format = require('util').format; var rawDebug = process._rawDebug; diff --git a/test/parallel/test-process-children.js b/test/parallel/test-process-children.js deleted file mode 100644 index a2707482043e..000000000000 --- a/test/parallel/test-process-children.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; -require('../common'); -const assert = require('assert'); - -const spawn = require('child_process').spawn; -const execFileSync = require('child_process').execFileSync; - -const code = 'setTimeout(() => {}, 400)'; - -spawn(process.argv[0], ['-e', code]); -spawn(process.argv[0], ['-e', code]); -assert(process.children().length === 2); - -process.children().forEach((el) => { - assert.doesNotThrow(() => { - process.kill(el); - }, Error); -}); - -const nested_child_code = ` - const spawn = require('child_process').spawn; - spawn(process.argv[0], ['-e', 'setTimeout(()=>{})']); - console.log(process.children().length === 1) -`; - -const res = execFileSync(process.argv[0], ['-e', nested_child_code]); -assert(Buffer.compare(res, new Buffer('true\n')) === 0); - -// For later regression: deregistering children is not implemented -assert(process.children().length !== 0); diff --git a/test/parallel/test-process-emit.js b/test/parallel/test-process-emit.js index f4f6d546f15c..ca249a12db78 100644 --- a/test/parallel/test-process-emit.js +++ b/test/parallel/test-process-emit.js @@ -1,6 +1,7 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); +const spawn = require('child_process').spawn const sym = Symbol(); process.on('normal', common.mustCall((data) => { @@ -20,3 +21,7 @@ process.emit(sym, 'symbolData'); process.emit('SIGPIPE', 'signalData'); assert.strictEqual(isNaN(process._eventsCount), false); + +process.on('spawn', common.mustCall((pid) => { + assert.strictEqual(isNaN(pid), false); +}))