-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
std::io doesn't work when stdout/stderr has O_NONBLOCK set #100673
Comments
C++ stdio streams don't support nonblocking IO. I suspect they silently drop the writes because fail() wasn't checked? re @famz (#100594 (comment)): I think it's better to continue the discussion here since changing behavior as in that PR is not necessarily the right outcome
If something manipulates the properties of the stdio file descriptors then it must have exclusive access to those descriptors because it's obviously not compatible with libraries that assume blocking IO. Which in turn means other things should not be printing to that descriptor because it's in exclusive use. For example if something is printing JSON output then stray stdout writes by another thread will break the json output. Patching the non-blocking issue does not fix the ownership assumptions the code is making.
Which means he's assuming that's ok to do, which usually is only the case when being the sole user of an FD. |
Nominating for discussion. Do we want a) document that nonblocking stdio is unsupported b) add polling fallbacks. In the latter case the question is whether it should be added only for the stdio wrappers or for all FD wrappers. Recent changes to windows (#98950) point in the direction of adding a fallback? |
I just tried the example in stack overflow and cout.fail is 0, but as mentioned in the article, it seems to only affect macOS and not linux, and I am testing on linux. The output for me looks like: stdout O_NONBLOCK is: 2048 cerr.fail():0 cout.fail():0 |
A couple of points for considering between stdio fd only vs all fd:
|
Whether or not sharing the same stdio is usually a sysadmin decision rather than program developers', e.g.:
In this case 3 subprocesses in the subshell share the same stdout. If So, who is here to blame? The sysadmin who wrote this script? The developer who used Rust println!, or the developer of |
I think we could make println do polling if it gets an |
Hi Josh! From your reply I assume it's not a bad idea if we make Or, such a wrapper can be a good addition to std too? By then, why is it so important that we don't just call it I understand changing behaviour of what's widely used is sometimes not desired, Hyrum's Law and all that. But in this specific case Or am I wrong and we cannot do anything to stop println!() from panic in this "unsupported" case? |
I agree with this, and I would say not only for stdio but maybe for all fds, If a write returns -EAGAIN, it seems like an extreme reaction to panic!, when kernel is requesting to try again. |
That would be a backwards-incompatible change. If you want to handle errors, call
I would suggest that having stdout/stderr in non-blocking mode is an unusual configuration that the user shouldn't do without expecting to handle it. @uarif1 |
That said, a PR adding documentation to both the |
Well, this is not that hard to hit. Just start this in the background (
Then start a tmux session in foreground. Then the hello world program crashes. One would think a relatively quiet app may be fine to run in the background where tmux runs in foreground, expecting a line or two outputs to slightly clutter the tmux display (easy to fix with Ctrl-L), but that is not the case here, the background rust program would just panic due to O_NONBLOCK. |
If tmux wants to use O_NONBLOCK and at the same time support other processes accessing the underlying TTY then it should reopen the stdio file descriptors so it has separate file descriptions which don't share the fcntl state. Concidentally someone submitted a patch to qemu that would have protected the caller process from such confusion, but it looks like it wasn't applied. GNU coreutils don't seem to support O_NONBLOCK either. And we definitely can't modify
Easy to fix is still slightly broken. So something is wrong and we shouldn't be silently papering over that issue. The panic you're seeing surfaces that error. |
I have created a PR with the documentation https://github.com/rust-lang/rust/pull/101416/commits |
I got bitten by this too, by running Would it be a good idea to add a warning to |
When stdout/stderr uses tty and has O_NONBLOCK set, the kernel driver returns -EAGAIN (https://elixir.bootlin.com/linux/v5.19/source/drivers/tty/tty_io.c#L952). This causes rust to panic, rather than try to print the same buffer again.
An example of when O_NONBLOCK is set on stdio is when using qemu serial.
I made an attempt to handle EAGAIN for std::io(#100594), but it was on generic code so not the right approach. Maybe there is a better approach to handle EAGAIN specifically for std::io and not panic?
To test this just change the stdout to O_NONBLOCK and then create 10 threads to print to stdout, C++ is able to print and finish to completion, but rust panics and crashes, so I think glibc also probably has similar support for O_NONBLOCK for stdout.
Thanks
The text was updated successfully, but these errors were encountered: