Nodejs - process hangs on exit (Ctrl+C)
Asked Answered
F

1

25

I have a node.js project which does many things, it spawns child processes, it opens an http and socket.io server, etc..

When I run it from the console, closing it with Ctrl+C, it just hangs. From webstorm, stopping the process is a two-step process, first I hit stop, then I need to hit the button again, only the second time the button is a skull icon.

Now, I understand it leaves something open or hanging, but I just can't figure out what, I tried to track all the places where I start a process and made sure I'm killing them properly.

Is there a way to debug this and find out what's making my process hang? Could it be logging that open a write stream and never closes? I'm not even sure what kind of things will make a process hang on SIGINT.

EDIT: I've downloaded pstree to see if any of the child processes that the main process spawns stay alive. It looks like they all terminate properly - the main node process is the only one left.

Floorwalker answered 18/2, 2014 at 20:3 Comment(4)
Try out node-inspectorDefense
This is usually because some event listeners haven't been unregistered. That's pretty broad, but for any more information you'll need to post some code.Mackle
@MarkS.Everitt - any kind of pointer would be appreciated - what kind of event listeners could cause the process to stay alive after SIGINT?Floorwalker
Without seeing code, there's no way I can know. Perhaps try closing server objects on the signal, although this should be automatic.Mackle
V
45

Scripts are themselves responsible for properly shutting down once they listen to the SIGINT event, as the default handler (killing the process) is disabled then.

Check out this example program:

process.on('SIGINT', function() {
    console.log('SIGINT');
});
console.log('PID: ', process.pid);

var http = require('http'); // HTTP server to keep the script up long enough
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

Execute it and then try killing it: It won't work. The SIGINT signal will always get passed to your custom build signal handler. In order to properly shut the process down you will have to manually call process.exit():

process.on('SIGINT', function() {
    console.log('SIGINT');
    process.exit();
});
console.log('PID: ', process.pid);

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

process.exit() will:

  1. Set some internal flags
  2. Call the process.on('exit') handlers
  3. Call process.reallyExit
  4. Which will call the C++ exit() function, therefore process.exit() is final and will cause a shutdown (unless you block execution by an endless loop in your on('exit') handler).

Long story short: Your code probably listens to SIGINT somewhere. You can fetch a list of those listeners via:

var listeners = process.listeners('SIGINT');

You can even pretty print them on the console:

for (var i = 0; i < listeners.length; i++) {
    console.log(listeners[i].toString());
}

Using the information I gave above you can easily compile yet another SIGINT-handler that will list all the handlers and then cleanly exit the process, hopefully leading your path to the naughty ones:

process.on('SIGINT', function() {
    console.log('Nice SIGINT-handler');
    var listeners = process.listeners('SIGINT');
    for (var i = 0; i < listeners.length; i++) {
        console.log(listeners[i].toString());
    }

    process.exit();
});

Complete program for testing:

process.on('SIGINT', function() {
    console.log('Naughty SIGINT-handler');
});
process.on('exit', function () {
    console.log('exit');
});
console.log('PID: ', process.pid);

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

process.on('SIGINT', function() {
    console.log('Nice SIGINT-handler');
    var listeners = process.listeners('SIGINT');
    for (var i = 0; i < listeners.length; i++) {
        console.log(listeners[i].toString());
    }

    process.exit();
});
Viburnum answered 22/2, 2014 at 0:59 Comment(2)
huh, amazingly simple. I had a SIGINT catcher that closed services and processes. I must've had process.exit in the code before and I guess I just assumed it wasn't needed so I removed that :) Thank youFloorwalker
This is great. I wish that nodejs.org/api/process.html had this information.Olimpiaolin

© 2022 - 2024 — McMap. All rights reserved.