Reputation: 10199
I spawn a child process. After I spawn it, I write to the outgoing stream, so I want to synchronously detect if it failed to spawn before doing that (otherwise I get a write EPIPE
error).
let proc = cp.spawn("path/file.exe");
proc.on("error", (err) => { throw err; });
proc.stdin.write(...);
Now if I put the write
on a setTimeout(() => { proc.stdin.write(...); }, 0)
, the error
handler will get hit first, and we'll crash before doing the write
, but that's a little ugly.
What I have found is that after cp.spawn
returns, we can check the proc.pid
property, and if it's a number, we know that the process spawned. If we give it the path of a nonexistent executable, proc.pid
is undefined
, so it seems that we can synchronously check if the process failed to spawn, and we don't need to rely on the asynchronous error
callback (though the callback catches other errors).
Node's documentation has a section on checking for a failed spawn, but it suggests the asynchronous method:
const subprocess = spawn('bad_command');
subprocess.on('error', (err) => {
console.log('Failed to start subprocess.');
});
Nowhere does it mention that checking for an undefined
proc.pid
is a suitable synchronous alternative for checking for a failed spawn, which me leads to wonder if this a robust and recommended way of doing it. Can anyone comment on this?
Upvotes: 1
Views: 2222
Reputation: 10199
Chatted with the folks at Node. Quoting from here.
Is there anyway to detect if a process has successfully spawned?
Sadly, no, not in the general case.
I have no guarantee that the process successfully spawned until I start actually writing to its streams and get errors.
Yup, that’s correct. libuv does a best-effort thing here, but it’s impossible to get a guaranteed result without resorting to e.g. polling techniques that look up the process pid and what that process is currently doing.
I have found that I can check
proc.pid
right afterspawn
ing, and if I pass in a faulty path, it'sundefined
. If I pass in the correct path, it's set to a number, which seems promising. However, someone on SO said that the spawning might still fail after the OS gives back a pid.
Part of the problem here is that there is no clear line for determining what a “failed” spawn attempt means – at what point do you consider a process spawned successfully? There can be errors at any point during process setup.
What exactly is guaranteed after the
spawn
function returns?
On Unix, that the execve()
system call was entered in the child process, and it succeeded in loading the relevant parts of the target executable into memory.
On Windows, a similar thing goes for CreateProcess()
.
Either way, there are still plenty of reasons why starting a process might fail – missing DLLs/shared libraries, memory exhaustion while running setup code, etc.
At what point in the spawning process are we?
If the spawn()
call succeeds, all you know that it’s up to the OS and the spawned process. No code from Node.js/libuv will be executed anymore inside the child.
And can I rely on this
proc.pid === undefined
technique?
For checking whether the executable could be found, yes, that’s reliable.
If not, how can I at least asynchronously know if the process successfully spawned?
The only thing that comes to my mind would be polling the OS’s way of providing information about the process tree in general.
Why is there no
success
callback?
When, and most importantly, from where would that callback be called? spawn()
finishing means that the OS and the target executable’s code are in charge now, so Node.js can’t make the process do something like saying “I’m good to go”.
Another user recommends:
One thing you could do, is in the child process, write to stdout "spawned successfully", read from stdout in the parent process, and then wait for that message.
const k = cp.spawn('bash');
let stdout = '';
k.on('data', function(d){
stdout+= String(d);
if(stdout.match(/spawned successfully/)){
// do your thing
}
});
Upvotes: 3