D. J. Bernstein
UNIX
Non-blocking I/O
UNIX kernel designers should provide
sys/nonblock.h with nonblock_read() and nonblock_write()
syscalls that don't block (and that don't generate SIGPIPE).
Adding nonblock_read() and nonblock_write()
would save a considerable amount of time and effort
in typical select/poll libraries such as my
io library.
It would eliminate bugs in quite a few programs
that don't go to the same amount of effort.
Here are the alternatives to nonblock_read() and nonblock_write():
- O_NONBLOCK (aka O_NDELAY)
at the beginning of the program.
The problem with O_NONBLOCK is that it's too heavy:
it hits an entire ofile, not just an fd.
For example,
suppose I set O_NONBLOCK on a pty on stdout,
and another program has the same stdout.
That other program then has O_NONBLOCK set too.
Many programs can't handle O_NONBLOCK and will break horribly
in this situation.
- O_NONBLOCK immediately before the operation,
and turn it off immediately after.
This is clumsy, and fails horribly if more than one program is active at once.
- An imaginary per-fd (rather than per-ofile) version of O_NONBLOCK.
This would work,
but it's clumsier than nonblock_read() and nonblock_write(),
and there are no benefits to the indirection.
- MSG_DONTWAIT.
This isn't portable, and it works only for sockets.
- Checking readability a moment before the operation.
This is popular,
but can fail horribly if more than one program is active at once.
For example, after select() says a pipe is writable,
a program that tries writing to the pipe might nevertheless block,
because the pipe was filled by another program after select() returned.
- Interval timers that interrupt blocking calls every 10 milliseconds.
This works (aside from the 10-millisecond delay),
at least in programs that don't use SIGALRM for anything else,
but it's clumsy.
Here's what I'm doing now:
- Call read() or write() without further ado
if the descriptor was originally provided in O_NONBLOCK mode
or refers to a regular file.
(Of course, it's possible that somebody subsequently turned O_NONBLOCK off;
every program relying on O_NONBLOCK is taking this risk.)
- Use poll(), if it's available and functioning,
to check whether the descriptor is ready.
- Set an interval timer.
- Call read() or write().
- Turn the interval timer off.
I'd much rather just call nonblock_read() or nonblock_write().