10

I start a spawn child process this way:

let process = spawn(apiPath, {
  detached: true
})

process.unref()

process.stdout.on('data', data => { /* do something */ })

When I start the process I need to keep it attached because I want to read its output. But just before closing my Node process (the parent) I want to detach all not finished children processes to keep them running in background, but as the documentation say:

When using the detached option to start a long-running process, the process will not stay running in the background after the parent exits unless it is provided with a stdio configuration that is not connected to the parent.

But with the option stdio: 'ignore' I can't read the stdout which is a problem.

I tried to manually close the pipes before to close the parent process but it is unsuccessful:

// Trigger just before the main process end
process.stdin.end()
process.stderr.unpipe()
process.stdout.unpipe()
Opsse
  • 1,851
  • 2
  • 22
  • 38
  • 1
    I'm a little confused why you'd expect to be able to read stdout/stderr of a process that is independent of Node. Either you need to capture the output, because the process is performing tasks that are part of your program (just running in parallel) in which case Node _should_ be the parent; or you're starting a truly independent program, in which case its standard output is not your Node program's concern and you should make them share data in a way that makes sense for two independent programs (e.g. a database, file monitor, an API server, whatever). – Mike 'Pomax' Kamermans Jan 10 '20 at 20:54
  • Maybe I wasn't clear enough, when I start the process I need to keep it attached because I want to read its output. But just before closing my Node process (the parent) I want to detach all not finished children processes to keep them running in background. – Opsse Jan 10 '20 at 21:27
  • Why not having different processes/program, and share data between them using a file or some other mean. – ROOT Jan 10 '20 at 21:40
  • It's not what a pipe do? So you suggest to handle the communication between processes myself? – Opsse Jan 10 '20 at 22:09
  • But _why_ would you detach the process? Either it's doing something in service of your program, in which case your program should wait until it's done, or it should signal the process that it's out of time and needs to finish up what it's doing because it's about to get SIGKILL'd - Basically: what's the actual use case? Because this sounds like a prime candidate for an [XY problem](https://meta.stackexchange.com/a/66378/395686) where you're trying to do something, and you thought of a way to do that, and you're asking about that way of doing things instead of asking about the original problem – Mike 'Pomax' Kamermans Jan 10 '20 at 23:08
  • If you want to know, here is the use case. We're building an Electron UI for a verification tool. The UI should launch the verifier (the child process) and communicate with it. However the computation time of the verifier is variable (seconds up to days). We also need to be able to regularly send command to the verifier (not just listening). Since we have no control over the verification time, the UI needs to connect and communicate at the beginning with the verifier; and we need to be able to quit the UI and let the verifier finish its job (the final result will be written in a file anyway). – Opsse Jan 11 '20 at 10:49

1 Answers1

1

After many tests I found at least one way to solve this problem : destroying all pipe before to leave the main process.

One tricky point is that the child process have to handle correctly the pipes destroying, if not it could got an error and close anyway. In this example the node child process seems to have no problem with this but it could be different with other scenario.

main.js

const { spawn } = require('child_process')

console.log('Start Main')

let child = spawn('node', ['child.js'], { detached: true })
child.unref() // With this the main process end after fully disconnect the child

child.stdout.on('data', data => {
  console.log(`Got data : ${data}`)
})

// In real case should be triggered just before the end of the main process
setTimeout(() => {
  console.log('Disconnect the child')

  child.stderr.unpipe()
  child.stderr.destroy()
  child.stdout.unpipe()
  child.stdout.destroy()
  child.stdin.end()
  child.stdin.destroy()
}, 5000)

child.js

console.log('Start Child')

setInterval(function() {
   process.stdout.write('hello from child')
}, 1000)

output

Start Main
Got data : Start Child

Got data : hello from child
Got data : hello from child
Got data : hello from child
Got data : hello from child
Disconnect the child

Opsse
  • 1,851
  • 2
  • 22
  • 38