According to this NodeJS issue report #7256, if a thread in the underlying libuv I/O thread pool attempts to read from a file that blocks, the thread will hang until the device or process backing the target file yields control to the thread, ie the read call returns.
Furthermore, NodeJS v0.10 on *nix systems relies on a fixed size thread pool with a default size of 4, according to this libuv issue report #649. This explains why the system appeared to lock-up after ~5 read attempts.
The situation can be worked around in several ways:
Increase the size of underlying libuv thread pool using the UV_THREADPOOL_SIZE environment variable. This glosses over the issue by simply allowing more opportunities to read before the Node process begins to suffer.
Open the blocking files using the recently exported O_NONBLOCK flag. This change is a part of the Node 0.10 code stream and I have confirmed that it works as expected (having compiled Node from source with _XOPEN_SOURCE set) but is not in the latest 0.10.28 release at the time of this comment. It seems reasonable to believe that the nonblock functionality will be a part of the 0.10.29 release. The device backing the file must also respect the O_NONBLOCK flag for this to work properly.
Some code to demonstrate option #2
var constants = process.binding('constants');
fs.open('/path/to/file', constants.O_NONBLOCK, function(err,fd){
fs.read(fd, buf, 0, 10, null, function(err, bytesRead, buffer){
// Read should return immediately
if(err.code === 'EAGAIN'){
// Not ready to read, try again later
} else {
// Do something
}
});
});