Fix blocking / non-blocking stdio woes
piscisaureus opened this issue · 49 comments
Currently process.stdin / stdout / stderr is blocking, except when it is a pipe on windows. Weird and surprising. Very unpractical in cases where stdio is used as an IPC mechanism between node processes.
Discussion:http://logs.nodejs.org/libuv/2012-06-29#00:40:38.256
Preliminary results: have process.stdout.setBlocking(true|false)
or process.stdout.writeSync.
Feel free to post thoughts here.
I've experienced a similar issue with stdio in Windows, and have somewhat worked around it in the meantime with this horrible, horrible hack.
While my mini exit
lib works well in place of process.exit
in some cases, it completely fails to help in this case:
var exitHack = require('./exit').exit;
// If there are pending stdout / stderr writes when "condition" is met...
if (condition) {
exitHack();
}
// ...this code will still do something. Whoops!
doSomething();
I haven't seen this problem in OS X or Linux. It would be great if Node.js behaved the same cross-OS.
Any update? Will this land in 0.10?
This issue has been stagnant for months. In order for this to happen, it will need a champion.
Are you that champion? Would you like to see stdio have a more consistently blocking/nonblocking interface, which works the same on Windows and Unix?
Heed the call. Build the bits that need to go into libuv. Sketch out an API in node for it.
It seems that it won't make the 0.10 cut-off, I'm afraid. Maybe next pass.
I have started to look into this issue. Here is what I have found so far.
If stdout is not piped, then the output goes through libuv tty code which is blocking as expected.
If stdout is piped, the output goes through libuv pipe code.
The pipe code checks to see if the pipe supports overlapped IO.
In the case of piped stdout it does not - NtQueryInformationFile returns a mode of FILE_SYNCHRONOUS_IO_NONALERT, which means:
All operations on the file are performed synchronously.
Wait requests in the system that must synchronize I/O queuing and completion are not subject to alerts
In this case the pipe code uses the thread pool to perform all IO.
So when the process exits, the write requests are queued up for the thread pool to execute, but they are never executed.
I made a quick hack to simply let the Write operations execute synchronously when the pipe is in this mode.
In the tests that I have, this fixes the problem of data being lost when the process exits.
This approach means that stdout would have blocking IO behavior even when piped, without requiring any new API.
Other pipes that have mode FILE_SYNCHRONOUS_IO_NONALERT or FILE_SYNCHRONOUS_IO_ALERT would also end up being blocking.
These modes may be set on a pipe or file when creating it, but libuv never sets it.
Does this seem like an acceptable way of fixing this issue?
Excellent! Any chance of getting this into node 0.10.x? This fix will allow me to get rid of ugly workarounds I need to use currently to not loose process output.
@piscisaureus and/or @sblom: request for comment, please.
Second pull request fixes commit message problems.
@HenryRawas works with me. I'll look at thiS.
Updated the proposed fix. Added API uv_pipe_setblocking() to libuv and changed windows pipe to use synchronous writes if this is set. Updated node code to call uv_pipe_setblocking() for stdio over pipe.
@HenryRawas is it realistic to get this fix in for 0.10.x?
I am still trying to get the change accepted. I don’t know where it will land.
From: Benjamin Pasero [mailto:notifications@github.com]
Sent: Friday, June 14, 2013 2:07 AM
To: joyent/node
Cc: Henry Rawas
Subject: Re: [node] Fix blocking / non-blocking stdio woes (#3584)
@HenryRawashttps://github.com/HenryRawas is it realistic to get this fix in for 0.10.x?
—
Reply to this email directly or view it on GitHubhttps://github.com//issues/3584#issuecomment-19446612.
Just wanted to 👍 this issue. Was just bit by it in Windows 8 with Node v0.10.12. I'm still looking for a workaround atm.
@dcherman No I haven't. Did I miss it already covered?
Here is what I'm trying to do. I noticed that the stdout output
was truncated:
var foo = cp.spawn(fooPath, fooOptions);
var output = '';
foo.stdout.on('data', function(data) {
output += data;
});
foo.on('exit', function() {
fs.writeFileSync(outputPath, output, 'utf8');
});
No matter what, don't do string concat like that. Use Buffer
instances and .concat
them.
@dcherman Woo doing:
var logStream = fs.createWriteStream(outputPath);
foo.stdout.pipe(logStream);
foo.on('exit', function() {
// do some cleanup
});
seems to work :)
Actually nix my issue. I tried it manually via the console and for some reason >
isn't capturing all the stdout that's normally being displayed to the console. Looks like it's not a node issue.
Anyone got any updates on this?
Will this fix Land in 0.12? I hear 0.12 is nearing completion.
I promise to make grand offerings I'll never follow through on for whoever resolves this. Anybody else who experiences this in the meantime, fs.writeSync()
worked well for me in my CLI tool. I just do this when I'm running my tests now on Windows and I always get my stdout/stderr back from exec()
and spawn()
:
console.log = console.info = console.warn = function(m) {
fs.writeSync(1, m);
};
console.error = function(m) {
fs.writeSync(2, m);
};
I ran into the issue when trying to execute Node code from Emacs on Windows. Apparently EShell, the built in shell, always uses piping for executing commands. So I didn't get output from ordinary Node apps, and I had to write wrappers, using temporary files.
Test case for cmd.exe
(i.e. not Emacs' EShell):
-
hello.js
:console.log('Hello world!'); process.exit(5);
-
Run as:
C:\>node hello.js | MORE
-
No output.
I got the same problem today and after a while of searching the nets, I got here. I'm confused though. Is this fixed? If yes, why is the patch not included in the release? If not, what is missing?
@elieux the issue is still open, so it's not likely fixed.
Please test https://npmjs.org/package/exit from @cowboy for a possible solution to this issue. Thanks!
I don't see how that module would help unless all modules would adopt it.
Yeah, https://npmjs.org/package/exit is not a solution to the issue, it's just a possible workaround. I'm not even sure it works everywhere yet, but hey--that's what testing is for.
👍
exit 0.1.1 was just published: https://npmjs.org/package/exit
0.1.1 addresses some exit code issues has a few fixes.
Help test this workaround if you get a chance, thanks!
node-exit is not working here. Win 7, node 0.10.20. Seems like the drain event has never been triggered, so the script never exits.
@zhiwww same here (win8x64 / node 0.10.20x64)
I added a PR that solves it for me: cowboy/node-exit#3
@HenryRawas any news with this one?
@isaacs, @sblom, @HenryRawas - sorry for spamming - any updates on this? Thanks! :)
So any updates?
So is it known that writing to stdout in unix/linux is blocking even when it refers to pipes?
After analyzing a logging issue in our code, I found out this behavior and today I wrote a post in the node google group: https://groups.google.com/d/msg/nodejs/Ua4nmiNPZXY/Vq3pepnM9Q8J
That behaviour is not consistent with the API documentation http://nodejs.org/api/process.html#process_process_stdout that says it is always non-blocking when stdout refers to a pipe.
I've just seen that the libuv code was fixed to make stdout writings non-blocking in linux joyent/libuv@8fe4ca6 but then it was reverted again to blocking mode joyent/libuv@8c9cbee
I suppose it was reverted because, as I can see in older comments, your are trying to make it work blocking in windows, but meanwhile I think the API documentation should be changed to reflect the actual behaviour, because the current one causes much confusion.
Well this is exciting, thanks @orangemocha
And there was much rejoicing
omg
omg yay? Is it really happening?
Glorious
Sweet! ✨
Calm down, it's just a pull request :) Not merged yet.
On the contrary a different fix was landed. So there is reason to rejoice
Hi, is this considered resolved? Is the use of https://github.com/cowboy/node-exit still recommended for some versions of node?
Yes, it is resolved. It was fixed with 20176a9.
You shouldn't need the workaround anymore.
Great, thanks!
Issue still exists for me in 0.12.4, Windows 8.1:
var child = require('child_process').spawn('php',
['-S', 'localhost:8007']);
child.stdout.on('data', function(data) {
console.log("child: "+data.toString());
});
Not sure if this is a bug, but if the process is infinite - then the child.stdout's callback is never triggered.
I wrapped php process in another child script, now it works fine.