Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

child_process: Expose 'detached' option to allow spawning detached child processes #2832

Closed
wants to merge 3 commits into from

Conversation

AvianFlu
Copy link

This functionality now exists in libuv master - uv_spawn() is now able to spawn detached (i.e. daemonized) child processes on both unix and Windows.

This patch exposes that option to Node.JS userland as a detached option to child_process.spawn.

var spawn = require('child_process').spawn;

var child = spawn('server.js', ['-p', '80'], {
  detached: true
});

child.on('detached', function (pid) {
  console.log('Child is now detached.  pid: %d.', pid);
});

Despite the fact that the stdio streams created in js are closed in libuv, the child closing its streams and exiting isn't enough to tell child_process.js that the child has exited.

this._internal.onexit is called successfully, but the maybeExit function is explicitly waiting for close events from the number of streams that were opened in js. Despite the fact that the underlying streams have been closed, the close events on the stdio streams in node don't fire of their own accord.

This keeps the parent process alive indefinitely, as the criteria specified for emitting the exit event will never occur.

Since a detached process is semantically somewhat different than a normal child process that exits, a separate detached event seemed like a good way to solve this. If the process being spawned is detached, the parent now emits a detached event without waiting for the other close events.

It might be better to somehow close or destroy the stdout and stderr streams, but the event loop doesn't seem to care, and node exits just fine.

@AndreasMadsen
Copy link
Member

Thanks for taking this up :D - but ...

I don't think it is necessary to set a pid argument when emitting detached, since you can get this from child.pid.

You should also consider to how child.kill() would work.

I tested this using the following script:

var spawn = require('child_process').spawn;

if (process.argv[2] === 'child') {
  setInterval(function () {
    console.log(".");
  }, 2000);

} else {
  setInterval(function () {
    console.log("-");
  }, 1);

  var child = spawn(process.execPath, [process.argv[1], 'child'], {
    detached: true
  });

  console.log('child:' + child.pid);
  child.on('exit', function () { console.log('exit'); });
  child.on('detached', function (pid) { console.log('detached: ', pid); });
}

but to my surprise the parent dies prematurely, or I conclude that from the following output. And ps do also shows the process don't exist.

Andreass-MacBook-Pro:node-deattach Andreas$ node child/test.js 
child:21752
detached:  21752
-
-
Andreass-MacBook-Pro:node-deattach Andreas$ .
.
.
.
.
.
.
.
.
.

The output from the unattached file seams goes to the terminal, that is okay but gives also problems when the terminal is closed since it would result in a SIGPIPE.

@AvianFlu
Copy link
Author

@AndreasMadsen Thanks! First, the idea was that the detached event replaces the exit event, so you shouldn't be expecting exit to ever fire, at least not at present.

You're right about the pid argument not being necessary because child.pid is the same - I added it as sugar, it serves no vital purpose.

With regards to the stdio issue, thanks! Please try again with the spawn-detached branch of my libuv fork, which has an additional commit now that should fix the issue.

@AndreasMadsen
Copy link
Member

@AvianFlu nice that fixed the stdio issue.

I do not expect the exit event to fire that was just a test, the issue is that the parent die prematurely when spawning something with detached: true. The parent should be alive forever (or until SIGTERM is send) because of setInterval but it isn't. When I run my test script this is what I see:

Andreass-MacBook-Pro:node-deattach Andreas$ node child/test.js 
child:2408
detached:  2408
-
-
Andreass-MacBook-Pro:node-deattach Andreas$ 

And I check for living node programs in another terminal:

Andreass-MacBook-Pro:~ Andreas$ ps -o pid,sess,state,command -U Andreas | grep node
 2408 ffffff800c044f20 Ss   /Users/Andreas/node/bin/node /Users/Andreas/GitHub/node-deattach/child/test.js child
 2410 ffffff800c047180 S+   grep node
Andreass-MacBook-Pro:~ Andreas$ 

As you see the parent clearly dies when it shouldn't.

@AvianFlu
Copy link
Author

@AndreasMadsen - @mmalecki Also saw the timer not keep the parent alive on OS X. For me, on linux, adding a setinterval to the parent does keep it alive, indefinitely. What OS are you on?

@AndreasMadsen
Copy link
Member

@AvianFlu I'm on OS X.

@AvianFlu
Copy link
Author

@AndreasMadsen - the timer behavior you're seeing is related to #2515 - it's a libev bug.

With that said, I'm going to tweak this and come back with a better version.

@AvianFlu AvianFlu closed this Feb 28, 2012
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants