Dealing with process signals with Node.js

JavaScript is asynchronous but we sometimes forget that it’s single threaded. This can be a problem when dealing with signals.

Today I stumbled a problem while developping cat-hex, a small command line tool for Node.js.

Cat-hex shows hexadecimal dump of any binary (or text) file:

While this works as expected, I quickly noticed a little problem when using pipe to pipe cat-hex’s output to other commands, like more:

It worked, but pressing q to exit more won’t return to the command prompt, as expected.

My first guess was that I needed to handle some signal SIGPIPE so I added these lines:

Unfortunately, this did not fix the problem, the app still appeared to be stuck when I exited more.

While leaving the command line as is, I then noticed that after some long delay, the command actually exited, and returned to the bash prompt.

As I’m quite new to Unix command line and native programming in general – I did some C++ apps but that was ages ago! – I wasn’t sure what was going on after using pipe to send the content of an app to another one.

What seems to happen is that the app’s output – so is its execution – is kinda paused by more, and when exiting, the pipe is broken, which makes the app’s execution resume.

That’s why cat-hex didn’t immediately return to bash, as it was still reading the file’s content.

What was misleading is the fact that while it’s running, the output doesn’t appear.

Back to the Node.js code, the problem lies in this piece of code:

This code is executed until the end of the file is reached.

The problem is that since the JavaScript is a single threaded language, when my app receives a SIGPIPE signal, it’s actually put inside the message queue.

And this message queue is only flushed after the code in the main (and only) thread has been executed.

This explains why I didn’t receive the message right after exiting more and why my app only exited after a long delay (which depended on the size of the file I was reading).

To fix the problem, I had to rely on setImmediate:

By calling this function, cat-hex’s execution will be postponed until the message queue is empty and the SIGPIPE handle that was added us actually called:


Update: Under Windows the pipe handling is a bit different. I had to add a special case because the SIGPIPE isn’t sent, and an error is thrown instead:

Source: Stack Overflow

More information about cat-hex can be found on GitHub.