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

2

8

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.

Prevail answered 15/6, 2015 at 19:13 Comment(1)
It appears to me this is the underlying issue: github.com/nodejs/node-v0.x-archive/issues/7101 Despite being closed, the issue is not really fixed. This whole read from a tty thing in node is a big mess with reads/writes blocking while they should be async, different stream/socket classes on top of simple fd's ....Coefficient
P
7

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.

Prevail answered 16/6, 2015 at 19:0 Comment(2)
Thanks! just encountered similar case when process.exit didn't work on SIGINT. SIGTERM did the work.Carleecarleen
I am trapping SIGTERM, and process.exit() would not work because I have an input stream reading from /dev/input/event3. Sending another SIGTERM did not help, but a SIGKILL in the on-exit block did the trick. (Although in the end, I decide it was cleaner to do the SIGKILL directly in the on-SIGTERM block after doing the cleanup that was my reason for trapping SIGTERM.) Crazy that this is needed.Retroflexion
B
0

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.

Budge answered 3/2, 2022 at 12:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.