electron and node on windows, kill a spawned process - node.js

i'm starting a background process (on windows) from electron main, something like this:
app_exe = require("child_process").spawn(
"app.exe" ,
[ "--params", ... ],
{ stdio: "ignore" }
);
this works fine, i can see this from process explorer:
but i cannot kill the process when the electron is closed ( .on("closed") or on("window-all-closed") )
i tried child.kill([signal]), but also tree-kill or taskkill with no results: only the first process (6036 from the example) is killed, the second (5760) remains stale.
also exec taskkill /F /T /PID doesn't kill it.
the only way to kill is exec taskkill /F /IM app.exe /T, but in this way i cannot run two instances of the electron app.
i'm missing something obvious on process management on windows?

I was seeing a similar issue on Windows 7 machines. I believe newer OS' will automatically kill the child processes.
What I had to do was to just save off the PID of the spawned-process and send it a SIGTERM message to kill it when all the windows closed. Now, if there's a chance that the process died by other means before the Electron app shut down, the OS may have recycled the child process' PID, so for extra robustness, I used the find-process npm module to make sure that the PID that I held on to is associated with the correct process.
const proc = cp.spawn("app.exe");
app.on("window-all-closed", async () => {
const list = await require("find-process")("pid", proc.pid);
app.quit();
if (list[0] && list[0].name.toLowerCase() === "app.exe")
process.kill(proc.pid);
});
Now if your Electron app does not exit gracefully (and the above code isn't run), you'd have to rely on another technique.
If you control the child process that you're spawning, you can try to kick off a thread that listens to or pings the main process. If it doesn't see the main process, it can kill itself.
If you don't control the spawned-app, then I'm out of ideas, but the above code will handle most cases.

I had exactly the same issue, and no question / answer on the forums could resolve that problem. So after some research i've found a simple workaround and im sharing it :
// Workaround to close all processes / sub-processes after closing the app
electron.app.once('window-all-closed', electron.app.quit);
electron.app.once('before-quit', () => {
window.removeAllListeners('close');
});
And its working perfectly for me, hope it does for you.

You can try it with this code:
ipcMain.on('exampletab:close', () => {
ipcMain.removeAllListeners();
exampleWindow.close();
});
This code save my lot of time. When you close your child window, then use removeAllListeners() to remove Previous closed Windows.

Related

Descendent of a child process not receiving SIGTERM on ubuntu but receives on mac

So I have some code which runs a command in a spawned child process. I do this using the execa module.
const childProcess = execa.command('yarn start');
const localhostStarted = await waitForLocalhost({ port: 8000 });
expect(localhostStarted.done).toBe(true);
childProcess.kill('SIGINT', { forceKillAfterTimeout: 2000 });
The yarn start command executes webpack-dev-server in another child process of its own. However when I kill the childProcess that I spawned, it does not automatically kill its spawned webpack-dev-server process. It is known to be an issue here https://github.com/webpack/webpack-dev-server/issues/2168.
To fix this I add manual listeners for SIGINT & SIGTERM inside my script which runs when yarn start is called
['SIGINT', 'SIGTERM'].forEach((signal) => {
console.log('registering events');
process.on(signal, () => {
console.log('received signal', signal);
devServer.close(() => {
console.log('exiting proces');
process.exit(0);
});
});
});
This fixes the issue on my local machine and when I kill child process I spawn, it kills all its descendents i.e the dev-server process too.
However, this fix still does not work on CI, and since the child process gets killed on CI but not the dev-server process, my tests dont exit and keeps hanging.
My local machine is OSX 10.15 but on CI we use ubuntu. If I change CI to use macos 10.15, then the fix works on CI too.
I am unable to find any docs on this issue which explains the different behaviour on why the signal of SIGTERM is not received by the dev-server process on ubuntu machines but receives fine on mac machines.

How to disable debugging with a signal in nodejs?

We can start debugger in nodejs using kill -USR1 PID. How can we disable it using any signal or any other way without terminating the worker?
Reference - https://nodejs.org/en/docs/guides/debugging-getting-started/#enable-inspector
While SIGUSR1 is used to start the debug inspector (equivalent to running node with the --inspect flag), you can customize SIGUSR2 to close the inspector without killing the process.
Adding something like this to your main app should add that behavior.
const inspector = require('inspector');
process.on('SIGUSR2', () => {
console.log('Received SIGUSR2. Closing inspector.');
inspector.close();
});
https://nodejs.org/api/inspector.html#inspector_inspector_close

Is there a way to get chrome-devtools (--inspect) to automatically detach from a process that has ended?

The process, as it is:
start code with --debug --inspect:
"debug": "node --max-old-space-size=8192 --debug=5567 --inspect dist/index.js",
Open devtools with url provided, something like:
chrome-devtools://devtools/remote/serve_file/#62cd277117e6f8ec53e31b1be58290a6f7ab42ef/inspector.html?experiments=true&v8only=true&ws=localhost:9229/node
Do some debugging.
Realize you have a bug, kill process (Ctrl-C) and make code changes.
Restart.
Start fails: Unable to open devtools socket: address already in use
Realize that you have to manually refresh the chrome-devtools page to get it to realize the process is no longer available.
Node inspector was much better about this. Is there something I'm missing? Some flag that will make the chrome-devtools automatically detach from a process that has stopped?
$ versions
npm 3.10.3
node v6.7.0
OS Darwin 15.6.0 Darwin Kernel Version 15.6.0
edit: I started using Node Inspector Manager (Chrome extension) and found that this whole process went much better.
I don't have that problem, chrome-devtools shows me a message that the debugging connection was closed when I Ctrl-C the node process.
What system are you running on? Maybe that's something that's been fixed in a newer Node or Chrome version? Or maybe you have something in your Node app that prevents proper shutdown of the socket. Can you try with a simple script instead?
jcollum here (user835611 should get the answer credit, since they tipped me off):
I had two listeners for the process termination signals:
process.on('SIGINT', function() {
logger.warn('SIGINT received. Shutting down.');
return process.exit(0);
});
process.on('SIGTERM', function() {
logger.warn('SIGTERM received. Shutting down.');
return process.exit(0);
});
Turns out those were blocking chrome's dev tools from seeing the shutdown. I don't know how that would work but that's what I observed. Commenting out process.exit made no change. How odd, it seems that having a listener on the SIGTERM / INT events blocks Chrome's dev tools from seeing that the process has exited. Never seen a side effect like that from an event listener.

NodeJS: exit parent, leave child alive

i am writing an utility. One command of this utility is to run an external application.
var child_process = require('child_process');
var fs = require('fs');
var out = fs.openSync('.../../log/out.log', 'a');
var err = fs.openSync('.../../log/err.log', 'a');
exports.Unref = function(app, argv) {
var child = child_process.spawn(app, argv, {
detached: true,
stdio: [ 'ignore', out, err ]
});
child.unref();
//process.exit(0);
};
Currently:
$ utility run app --some-args // run external app
// cant enter next command while app is running
My Problem is that if i run this command, the terminal is locked while the "external" Application is running.
But the terminal window shouldn't be locked by the child_process.
i wanna run:
$ utility run app --some-args
$ next-command
$ next-command
The external Application (a desktop application) will be closed by hisself.
Like this:
$ subl server.js // this runs Sublime Text and passes a file to the editor
$ npm start // The terminal does not locked - i can execute next command while Sublime is still running
You know what i mean ^^?
Appending ['>>../../log/out.log', '2>>../../log/err.log'] to the end of argv instead of leaving two files open should work since it's the open file handles that are keeping the process alive.
Passing opened file descriptors in stdio in addition to detached: true will not work the way you expect because there is no way to unref() the file descriptors in the parent process and have it still work for the child process. Even if there was a way, I believe that when the parent process exited, the OS would clean up (close) the file descriptors it had open, which would cause problems for the detached child process.
The only possible way that this might have been able to work would have been by passing file descriptors to child processes, but that functionality was dropped several stable branches ago because the same functionality did not exist on some other platforms (read: Windows).

Running Node app through Grunt

I am trying to run my Node application as a Grunt task. I need to spawn this as a child process, however, to allow me to run the watch task in parallel.
This works:
grunt.registerTask('start', function () {
grunt.util.spawn(
{ cmd: 'node'
, args: ['app.js']
})
grunt.task.run('watch:app')
})
However, when changes are detected by the watch task, this will trigger the start task again. Before I spawn another child process of my Node app, I need to kill the previous one.
I can't figure out how to kill the process, however. Something like this does not work:
var child
grunt.registerTask('start', function () {
if (child) child.kill()
child = grunt.util.spawn(
{ cmd: 'node'
, args: ['app.js']
})
grunt.task.run('watch:app')
})
It appears that:
Even though I store the spawned process in a variable outside of the function context, it does not persist, so the next time the start task is run, child is undefined.
child has no kill function…
Take a look at grunt-nodemon which handles a lot of the headaches related to spawning a child process.
This is because grunt-contrib-watch currently spawns all task runs as child processes. So the variable child is not within the same process context. Fairly soon, grunt-contrib-watch#0.3.0 will be released with a nospawn option. This will let you configure the watch to spawn task runs within the same context and would make your above example work.
Take a look at this issue for a little more information:
https://github.com/gruntjs/grunt-contrib-watch/issues/45

Resources