electron-webpack
electron-webpack copied to clipboard
“electron-webpack dev” doesn’t close all child processes
Steps to reproduce:
- clone https://github.com/electron-userland/electron-webpack-quick-start and follow installation instructions;
- run
yarn dev.
This will start electron-webpack dev which in my case resulted in the following processes being started:
$ pstree -p 22667
node(22667)─┬─sh(22687)───node(22688)───node(22689)─┬─electron(22715)─┬─electron(22717)─┬─electron(22749)─┬─{electron}(22750)
│ │ │ │ ├─{electron}(22751)
│ │ │ │ ├─{electron}(22752)
│ │ │ │ ├─{electron}(22753)
│ │ │ │ ├─{electron}(22754)
│ │ │ │ ├─{electron}(22755)
│ │ │ │ ├─{electron}(22756)
│ │ │ │ ├─{electron}(22757)
│ │ │ │ ├─{electron}(22758)
│ │ │ │ ├─{electron}(22759)
│ │ │ │ ├─{electron}(22760)
│ │ │ │ ├─{electron}(22761)
│ │ │ │ ├─{electron}(22803)
│ │ │ │ ├─{electron}(22816)
│ │ │ │ ├─{electron}(22817)
│ │ │ │ ├─{electron}(22818)
│ │ │ │ └─{electron}(22819)
│ │ │ ├─electron(22766)─┬─{electron}(22768)
│ │ │ │ ├─{electron}(22769)
│ │ │ │ ├─{electron}(22770)
│ │ │ │ ├─{electron}(22771)
│ │ │ │ ├─{electron}(22772)
│ │ │ │ ├─{electron}(22773)
│ │ │ │ ├─{electron}(22774)
│ │ │ │ ├─{electron}(22782)
│ │ │ │ ├─{electron}(22784)
│ │ │ │ ├─{electron}(22785)
│ │ │ │ ├─{electron}(22791)
│ │ │ │ ├─{electron}(22793)
│ │ │ │ └─{electron}(22804)
│ │ │ └─electron(22767)─┬─{electron}(22775)
│ │ │ ├─{electron}(22776)
│ │ │ ├─{electron}(22777)
│ │ │ ├─{electron}(22779)
│ │ │ ├─{electron}(22780)
│ │ │ ├─{electron}(22781)
│ │ │ ├─{electron}(22783)
│ │ │ ├─{electron}(22786)
│ │ │ ├─{electron}(22787)
│ │ │ ├─{electron}(22788)
│ │ │ ├─{electron}(22789)
│ │ │ ├─{electron}(22808)
│ │ │ ├─{electron}(22820)
│ │ │ └─{electron}(22821)
│ │ ├─electron(22745)─┬─{electron}(22794)
│ │ │ ├─{electron}(22795)
│ │ │ ├─{electron}(22796)
│ │ │ ├─{electron}(22797)
│ │ │ ├─{electron}(22798)
│ │ │ ├─{electron}(22799)
│ │ │ ├─{electron}(22800)
│ │ │ └─{electron}(22801)
│ │ ├─{electron}(22716)
│ │ ├─{electron}(22718)
│ │ ├─{electron}(22719)
│ │ ├─{electron}(22720)
│ │ ├─{electron}(22721)
│ │ ├─{electron}(22722)
│ │ ├─{electron}(22723)
│ │ ├─{electron}(22724)
│ │ ├─{electron}(22725)
│ │ ├─{electron}(22726)
│ │ ├─{electron}(22727)
│ │ ├─{electron}(22728)
│ │ ├─{electron}(22729)
│ │ ├─{electron}(22730)
│ │ ├─{electron}(22731)
│ │ ├─{electron}(22733)
│ │ ├─{electron}(22734)
│ │ ├─{electron}(22738)
│ │ ├─{electron}(22740)
│ │ ├─{electron}(22741)
│ │ ├─{electron}(22742)
│ │ ├─{electron}(22743)
│ │ ├─{electron}(22744)
│ │ ├─{electron}(22746)
│ │ ├─{electron}(22748)
│ │ ├─{electron}(22812)
│ │ ├─{electron}(22813)
│ │ ├─{electron}(22814)
│ │ └─{electron}(22815)
│ ├─node(22701)───node(22702)─┬─{node}(22703)
│ │ ├─{node}(22704)
│ │ ├─{node}(22705)
│ │ ├─{node}(22706)
│ │ ├─{node}(22707)
│ │ ├─{node}(22708)
│ │ ├─{node}(22709)
│ │ ├─{node}(22710)
│ │ └─{node}(22711)
│ ├─{node}(22690)
│ ├─{node}(22691)
│ ├─{node}(22692)
│ ├─{node}(22693)
│ ├─{node}(22694)
│ ├─{node}(22695)
│ ├─{node}(22696)
│ ├─{node}(22697)
│ └─{node}(22698)
├─{node}(22678)
├─{node}(22679)
├─{node}(22680)
├─{node}(22681)
├─{node}(22682)
├─{node}(22683)
├─{node}(22684)
├─{node}(22685)
└─{node}(22686)
Then I closed application window and 22667 is, correctly, gone.
$ pstree -p 22667
But something still occupies my RAM and apparently that’s…
$ pstree -p 22701
node(22701)───node(22702)─┬─{node}(22703)
├─{node}(22704)
├─{node}(22705)
├─{node}(22706)
├─{node}(22707)
├─{node}(22708)
├─{node}(22709)
├─{node}(22710)
└─{node}(22711)
$ ps --no-headers o command ww 22701
/bin/sh /tmp/user/…/yarn--…-…/node /home/…/electron-webpack-problem/node_modules/webpack-dev-server/bin/webpack-dev-server.js --color --env.autoClean=false --config /home/…/electron-webpack-problem/node_modules/electron-webpack/webpack.renderer.config.js
Unfortunately, SIGTERM still doesn’t close all processes:
$ pstree -p 22701
$ pstree -p 22702
node(22702)─┬─{node}(22703)
├─{node}(22704)
├─{node}(22705)
├─{node}(22706)
├─{node}(22707)
├─{node}(22708)
├─{node}(22709)
├─{node}(22710)
└─{node}(22711)
$ ps --no-headers o command ww 22702
/usr/bin/node /home/…/electron-webpack-problem/node_modules/webpack-dev-server/bin/webpack-dev-server.js --color --env.autoClean=false --config /home/…/electron-webpack-problem/node_modules/electron-webpack/webpack.renderer.config.js
SIGTERMing 22702 finally frees up my RAM and gets rid of unnecessary processes.
I think that relatively long waiting period before returning to terminal prompt (where I had run yarn dev) after application window was closed may be related to the above issue.
(Node 8, GNU/Linux.)
The problem has disappeared.
Still happening to me on newest version, Node 10, Win10. Did you change anything?
Probably some system update solved the problem.
When I am ctrl+cing electron-webpack dev, 3 processes remain open:
-
Electron at 100% CPU with this commandline:
C:\Users\User\Dev\electron-webpack-quick-start\node_modules\electron\dist\electron.exe" --type=renderer --no-sandbox --enable-features=SharedArrayBuffer --service-pipe-token=10686501403369708028 --lang=en-US --app-path="C:\Users\User\Dev\electron-webpack-quick-start\node_modules\electron\dist\resources\default_app.asar" --node-integration=true --webview-tag=true --no-sandbox --background-color=#fff --device-scale-factor=1 --num-raster-threads=2 --enable-main-frame-before-activation --service-request-channel-token=10686501403369708028 --renderer-client-id=6 --mojo-platform-channel-handle=2008 /prefetch:1 -
This Node.js process:
C:\Program Files\nodejs\node.exe" --max-old-space-size=2048 C:\Users\User\Dev\electron-webpack-quick-start\node_modules\fork-ts-checker-webpack-plugin\lib\service.js -
And this Node.js process:
node "C:\Users\User\Dev\electron-webpack-quick-start\node_modules\.bin\\..\webpack-dev-server\bin\webpack-dev-server.js" --color --env.autoClean=false --config C:\Users\User\Dev\electron-webpack-quick-start\node_modules\electron-webpack\webpack.renderer.config.js
@develar Please see my last comment here. Thanks!
Same thing here node v8.9.4 though. Gets quite annoying after a while.
UP! after aborting process electron-webpack dev doesn't dev-server process doesn't kill
@fabiospampinato @loopmode @develar Please address!
I can replicate this issue on windows, Node 12
@stereokai I do not maintain this repo, and this issue hasn't caused any troubles to me yet. You'd have better chances to get this fixed by submitting a PR.
@fabiospampinato, sure! Can you please look at my comment above and see maybe you have some insight into what I shared there?
I once looked into this issue and I had the feeling that we're not sending the right signal - SIGINT, and that maybe we need to use SIGTERM instead. However, then I saw that it's only used on Linux while I experience the issue on windows. Since this only happens in development - at least for me - I gave up on this and wrote a short script to kill the processes manually instead. I can share it if someone is interested.
Nonetheless, the issue is still there and unsolved. If somebody got the time, it must be something in https://github.com/electron-userland/electron-webpack/blob/master/packages/electron-webpack/src/dev/ChildProcessManager.ts (I think so at least). I wouldn't bother rebuilding and testing, but instead just hack ChildProcessManager.js inside node_modules until the culprit is found. However I have no time at all for it, these days :(
That's a great starting point, I'll try to make time for this soon
node_modules\electron-webpack\out\dev\ChildProcessManager.js line 84 SIGINT to SIGTERM node_modules\electron-webpack\out\dev\dev-runner.js line 326 SIGINT to SIGTERM
Seems to get rid of the issue. Not sure this is the proper fix for it though.
Nevermind this seems to resolve only Electron hanging. Or at least it still keeps nodejs process hanging if stopped with ctrl+c.
i use this workaround in dev:
import shell from 'node-powershell';
app.on('window-all-closed', async () => { if (isDevelopment) { await killElectronWebpackDevServer(); } app.quit(); });
const killElectronWebpackDevServer = async () => { let ps = new shell({ executionPolicy: 'Bypass', noProfile: true }); ps.addCommand('wmic Path win32_process Where "CommandLine Like \'%webpack-dev-server%\'" Call Terminate'); try { await ps.invoke(); } // eslint-disable-next-line no-empty catch (e) { } }
For development on Windows I use this alias in .bashrc:
kill() {
local name="${1}"
if [ -z "$name" ];
then
echo ">> Enter a process name and press [ENTER] (partial matching)"
read name
fi
wmic process where "name like '%$name%'" delete
}
Then I go kill electron && kill cmd after stopping via Ctrl+C. (The cmd processes get stuck as well somehow, and I think it is more than one)
At least with the current versions of NodeJS (12.4.0), I no longer have this issue if I modify run in ChildProcessManager.ts (just use a normal spawn call instead of routing through runnerw.exe) and remove the child.stdin!!.end(Buffer.from([5, 5])) and simply call child.kill('SIGINT') normally.
I can confirm what @glek said.
Dropping the isWin conditionals in ChildProcessManager and using the simple way - spawn(program, args, options) and child.kill("SIGINT") - solves the issues and leads to a much improved performance and responsiveness. The processes are closed properly.
Not sure whether this can lead to other problems - I've read somewhere that child.kill on windows doesn't emit any signals/messages but just kills immediatly, not allowing listeners/observers to handle it,
@develar do you remember the motivation behind using runnerw.exe for launching and child.stdin!!.end(Buffer.from([5, 5])) for exiting?
Dropping them would simplify the codebase in a nice way and properly kill the background processes at the right time. Seems like a win-win?
emit any signals/messages but just kills immediatly, not allowing listeners/observers to handle it,
yep, runnerw adds signal handling.
So the question is how relevant signal handling in development on windows is..
Right now, frequently starting and stopping dev environment eats up resources. When I start noticing my notebook fan spinning loudly, I go kill the processes and continue working, but it does suck..
Maybe I'll find something about using runnerw.exe in a better way, maybe we can keep it and get rid of the problem.
Otherwise, I'd suggest..we make it's usage opt-in. Disable by default, and if some use case requires it to avoid e.g. data corruption, the user can enable it via some env variable, e.g. ELECTRON_WEBPACK_USE_RUNNERW
That sounds like a reasonable approach, @loopmode
By "adds signal handling" what does this practically mean?
If we don't have runnerw signal handling what does that mean for us. Does that mean that we have to add some more code to electron-webpack to handle the signals ourselves?
I'm also a little bit confused where runnerw.exe comes from. From my searches all I can tell is that it was developed by Jetbrains for use in their products. Is this true?
It would just be a little bit helpful to have some context about this @develar @loopmode
Thanks!
PS as a sidenote...
My current workaround is having two scripts in package.json as follows:
"clean-dev-win": "taskkill /f /im node.exe",
"clean-dev-mac": "sudo killall -9 node",
Which I run depending on which OS I'm on. This could be turned into a JS script easily but it works well enough for me.
@b-zurg I am as confused as you are :) I was also searching for runnerw via Google and didn't find anything solid (besides what you report).
In the "naive" tests that I made - actual dev workflow on a real project - I couldn't see any downsides from removing runnerw. I was happy to see that Ctrl+C (in Git bash, that is) worked like a charm - real fast and snappy, and also killing all child processes.
What I did not try out tho, now that I think about it, was using Windows CMD or Powershell. Maybe runnerw is some kind of broker for cmd.exe, so that it can work with the signals. Maybe worth checking.
I'm just a regular user of electron-webpack, but if it was my decision, I'd drop this runnerw thing entirely out of the project. That resolves a bunch of issues. Then wait if new issues arise..
Btw @b-zurg , in my experience, many cmd processes stay alive (webpack I believe), and just killing node doesn't help in many cases. Killing cmd in addition does help then.
I also think perhaps runnerw could be entirely replaced. It just requires some time and some good test cases, neither of which I have at the moment 🤷♂️
I have a pretty good temporary solution though for now, though it requires the use of tree-kill and execa both of which are pretty well-maintained libraries.
const execa = require("execa");
const treeKill = require("tree-kill");
const subProcess = execa("electron-webpack", ["dev"]);
subProcess.stdout.pipe(process.stdout);
subProcess.stderr.pipe(process.stderr);
process.on("SIGINT", async () => {
console.log("Caught interrupt. Killing process tree");
treeKill(subProcess.pid, (e) => {
console.error(`Tree Kill failed because of ${e}`);
});
});
This code is saved in (for example) ./scripts/devrunner.js.
Then in my package.json I have the following line in scripts:
"dev": "node ./scripts/devrunner.js"
At this point all you run is yarn dev as per usual.
This script is tested on windows git bash but should work with mac just fine as well. This will catch an interrupt signal successfully and propogate that kill to the rest of the process tree from the original electron-webpack dev command.
I'm not sure if this is related to this problem, or another problem, but whenever something goes wrong with the compile (an error or a warning -- since #305 has yet to be released) the electron process doesn't get launched until after I ctrl+c out of the dev runner. It never successfully launches however so I must force kill it (on OS X) which results in a leftover shell process (in my case zsh) using 100% of one core which I have to manually kill.
This problem does not happen if the electron process launches normally and I don't have to force quit it. Presumably because when I ctrl+c the dev runner it has closed by the time the electron process starts so it's orphaned.
I have the same issue on Windows 10, [email protected], and [email protected].
When closing the app (launched in development mode), only the electron processes properly close.
Then the cli.js script hangs (blocking the terminal) for 10 seconds before stopping, orphaning the webpack dev server process, which stays alive, locking the port untill I manually kill it.
Here is the full command that launched the webpack dev server :
node "C:\workspace\electron-webpack\node_modules\.bin\\..\webpack-dev-server\bin\webpack-dev-server.js" --color --env.autoClean=false --config C:\workspace\electron-webpack\node_modules\electron-webpack\webpack.renderer.config.js
Runnerw is not an issue here since I still have the issue without using it.
On a side note, I managed to make the process killing workaround work, but I really don't want to rely on that.
I can confirm what @glek said.
Dropping the isWin conditionals in
ChildProcessManagerand using the simple way -spawn(program, args, options)andchild.kill("SIGINT")- solves the issues and leads to a much improved performance and responsiveness. The processes are closed properly.Not sure whether this can lead to other problems - I've read somewhere that child.kill on windows doesn't emit any signals/messages but just kills immediatly, not allowing listeners/observers to handle it,
@develar do you remember the motivation behind using
runnerw.exefor launching andchild.stdin!!.end(Buffer.from([5, 5]))for exiting? Dropping them would simplify the codebase in a nice way and properly kill the background processes at the right time. Seems like a win-win?
@loopmode Does this solution still work for you? If so, do you mind sharing some code if you still have it.
Hi. I haven't worked with electron in a long time, but I guess it's still working. And I don't have actual code - all I did was hack the line inside the installed node_modules. But it's about line 7 of this source file: https://github.com/electron-userland/electron-webpack/blob/master/packages/electron-webpack/src/dev/ChildProcessManager.ts