Node.js process.exit() will not exit with a createReadStream open

11.4k views Asked by At

I have a program that communicates with Asterisk via EAGI. Asterisk opens up my Node.js application and sends it data via STDIN and the program sends Asterisk commands via STDOUT. When a user hangs up, the Node.js process gets sent a SIGHUP command. This is intercepted for cleaner exiting. This functionality is working.

Asterisk also sends RAW audio data on fd 3 (STDERR+1). The Node.js process intercepts the data properly, and is able to read the audio, convert it, or anything else that needs to be done. However, when the createReadStream is created on fd 3, the Node.js process will NOT exit and quickly becomes a Zombie. If I comment out the createReadStream code, Node.js exits as expected.

How can I get Node.js to exit with the process.exit() function like it's supposed to? I'm using Node.js version v0.10.30.

Node.js createReadStream code:

// It was success
this.audioInStream = fs.createReadStream( null, { 'fd' : 3 } );

// Pipe the audio stream to a blackhole for now so it doesn't get queued up
this.audioInStream.pipe( blackhole() );

SIGHUP code:

process
.on( 'SIGHUP', function() {
    log.message.info( "[%s] Asterisk process hung up.", that.callerid );
    that.exitWhenReady();
} );

exitWhenReady function

Index.prototype.exitWhenReady = function() {
    if( !this.canExit )
        return;

    log.message.info( "[%s] Exiting program successfully.", this.callerid );

    // Get rid of our streams
    this.audioInStream.unpipe();
    this.audioInStream.close();
    this.audioInStream.destroy();

    process.exit( 0 );
};

Blackhole module:

var inherits = require( 'util' ).inherits;
var Writable = require( 'stream' ).Writable;
var process = require( 'process' );

function Blackhole( opts ) {
    if( !(this instanceof Blackhole) )
        return( new Blackhole( opts ) );

    if( !opts )
        opts = {};

    Writable.call( this, opts );
}

inherits( Blackhole, Writable );

Blackhole.prototype._write = function( chunk, encoding, done ) {
    process.nextTick( done );
};

module.exports = Blackhole;

It is notable that

Asterisk process hung up

And

Exiting program successfully.

Never show up in log file when createReadStream is reading fd 3, but when it isn't they do.

2

There are 2 answers

2
Mikey A. Leonetti On BEST ANSWER

I found that hooking the SIGHUP AND having fd 3 open caused the program to not close even when process.exit() was called. This was really strange.

What I did to fix this issue was listen to the process' "exit" event. In the "exit" event I manually killed my own process with a SIGTERM. This was sufficient for stopping the whole program. I found that this actually worked well with the Winston logger exception logger even. Winston would be able to write the exception to a log file and then exit successfully.

The result code:

process
.on( 'SIGHUP', function() {
    log.message.info( "[%s] Asterisk process hung up.", that.callerid );
    that.exitWhenReady( true );
} )
.on( 'exit', function() {
    process.kill( process.pid, 'SIGTERM' );
} );

The above function basically calls the exitWhenReady() when a SIGHUP is sent. That checks to see that all tasks are complete and once all tasks are complete it'll call "process.exit()" which calls the function on the above event.

I hope this helps somebody.

0
Victor Welling On

Came across this question looking for an answer myself. Wasn't really happy with the "forced exit", as it stops any other processes from wrapping up their work as well.

After some extensive trial & error, here's what I've found. Instead of createReadStream(), create a socket instead:

const audio = new net.Socket({ fd: 3, readable: true });

Second, create an event listener on SIGHUP which destroys your stdin and audio streams:

process.addListener("SIGHUP", () => {
  process.stdin.destroy();
  audio.destroy();
});

This should allow Node.js to exit cleanly.