Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: ubuntu loop waiting for sub processes #1635

Merged
merged 1 commit into from Nov 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2019 Remy Sharp, https://remysharp.com <remy@remysharp.com>
Copyright (c) 2010 - present, Remy Sharp, https://remysharp.com <remy@remysharp.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
95 changes: 50 additions & 45 deletions lib/monitor/run.js
@@ -1,4 +1,4 @@
var debug = require('debug')('nodemon');
var debug = require('debug')('nodemon:run');
const statSync = require('fs').statSync;
var utils = require('../utils');
var bus = utils.bus;
Expand All @@ -10,34 +10,12 @@ var watch = require('./watch').watch;
var config = require('../config');
var child = null; // the actual child process we spawn
var killedAfterChange = false;
var noop = function () { };
var noop = () => {};
var restart = null;
var psTree = require('pstree.remy');
var path = require('path');
var signals = require('./signals');

function waitForSubprocesses(subprocesses, callback) {
if (Array.isArray(subprocesses) && subprocesses.length > 0) {
// check if all old subprocesses have been terminated
exec('kill -0 ' + subprocesses.join(' '), (error) => {
const returnCode = error ? error.code : 0;
if (returnCode < 126) { // ignore command not found error
const stillRunning = subprocesses.length - returnCode;
if (stillRunning > 0) {
utils.log.status('still waiting for ' + stillRunning +
' subprocess(es) to finish...');
setTimeout(waitForSubprocesses.bind(this, subprocesses, callback),
100);
return;
}
}
callback();
});
} else {
callback();
}
}

function run(options) {
var cmd = config.command.raw;

Expand All @@ -55,11 +33,11 @@ function run(options) {
var stdio = ['pipe', 'pipe', 'pipe'];

if (config.options.stdout) {
stdio = ['pipe', process.stdout, process.stderr];
stdio = ['pipe', process.stdout, 'pipe'];
}

if (config.options.stdin === false) {
stdio = [process.stdin, process.stdout, process.stderr];
stdio = [process.stdin, process.stdout, 'pipe'];
}

var sh = 'sh';
Expand Down Expand Up @@ -161,6 +139,20 @@ function run(options) {
bus.emit('message', message, sendHandle);
});
}
} else { // else if not required…

// this swallows a shell message that happens because we kill the sh -c
// more details here: https://git.io/Je6d0
child.stderr.on('data', s => {
s = s.toString();

if (child.__nodemonRestart) { // this flag is set right before the kill
utils.log.detail('stderr: ' + s);
return;
}

process.stderr.write(s);
});
}

bus.emit('start');
Expand Down Expand Up @@ -284,6 +276,7 @@ function run(options) {
/* Now kill the entire subtree of processes belonging to nodemon */
var oldPid = child.pid;
if (child) {
child.__nodemonRestart = true;
kill(child, config.signal, function () {
// this seems to fix the 0.11.x issue with the "rs" restart command,
// though I'm unsure why. it seems like more data is streamed in to
Expand Down Expand Up @@ -340,6 +333,19 @@ function run(options) {
}
}

function waitForSubProcesses(pid, callback) {
debug('checking ps tree for pids of ' + pid);
psTree(pid, (err, pids) => {
if (!pids.length) {
return callback();
}

utils.log.status(`still waiting for ${pids.length} sub-process${
pids.length > 2 ? 'es' : ''} to finish...`);
setTimeout(() => waitForSubProcesses(pid, callback), 1000);
});
}

function kill(child, signal, callback) {
if (!callback) {
callback = function () { };
Expand All @@ -362,26 +368,25 @@ function kill(child, signal, callback) {
// an array of PIDs that have spawned under nodemon, and we send each the
// configured signal (default: SIGUSR2) signal, which fixes #335
// note that psTree also works if `ps` is missing by looking in /proc
const sig = signal.replace('SIG', '');
psTree(child.pid, function (err, subprocesses) {
if (psTree.hasPS) {
spawn('kill', ['-s', sig].concat(subprocesses))
.on('close', waitForSubprocesses.bind(this, subprocesses,
function () {
spawn('kill', ['-s', sig, child.pid])
.on('close', callback);
}));
} else {
// make sure we kill from smallest to largest
const pids = subprocesses.slice().sort();
pids.forEach(pid => {
exec('kill -' + signals[signal] + ' ' + pid, () => { });
});
waitForSubprocesses(subprocesses, function () {
exec('kill -' + signals[signal] + ' ' + child.pid, () => { });
callback();
})
let sig = signal.replace('SIG', '');

psTree(child.pid, function (err, pids) {
// if ps isn't native to the OS, then we need to send the numeric value
// for the signal during the kill, `signals` is a lookup table for that.
if (!psTree.hasPS) {
sig = signals[signal];
}

// the sub processes need to be killed from smallest to largest
debug('sending kill signal to ' + pids.join(', '));

pids.sort().forEach(pid => exec(`kill -${sig} ${pid}`, noop));

waitForSubProcesses(child.pid, () => {
// finally kill the main user process
exec(`kill -${sig} ${child.pid}`, callback);
});

});

}
Expand Down