bin/164947: tee looses data when writing to non-blocking file
brde at optusnet.com.au
Sat Feb 11 08:32:17 UTC 2012
On Sat, 11 Feb 2012, David Xu wrote:
> [... excessive quoting removed]
> >> Description:
> > When tee(1) tries to write to a file descriptor that has been set to non-blocking mode the write(2) call may fail with EAGAIN. Instead of retrying the operation, tee will throw that chunk of data away.
> so tee should also work with non-blocking read, your patch is incomplete.
Not just for tee :-). Just about every filter utility starting with cat(1)
doesn't even dream of EAGAIN.
I once helped implement a stdio that retried unconditionally after
EAGAIN. This isn't quite right, but it automatically fixes any utility
that makes the mistake of using stdio for anything important, starting
with filter utilities that make this mistake (fortunately, not cat(1)
in FreeBSD). The stdio interface also doesn't even dream of EAGAIN.
Unless stdio's internals know about it and retries automatically, it
has to treat EAGAIN as a fatal error like EIO, and return an error and
set ferror() on the stream, and it should set errno to EAGAIN. This
is what FreeBSD stdio does (no retry). Applications can check for
EAGAIN and retry, but this is not portable and applications that use
stdio are probably doing it because they want to be portable and/or
don't want to handle the details, so they are further from dreaming
about EAGAIN that the average application.
The correct behaviour for stdio is probably to retry conditionally
depending on a flag set by ffcntl(3new) that defaults to on. You
would clear the flag in the unusual case where you actual want to use
non-blocking i/o and see EAGAIN. Stdio doesn't really support non-
blocking i/o, but it might work.
ffcntl() could also use ordinary fcntl() to clear O_NONBLOCK. The old
stdio might have retried because the system didn't support fcntl().
But even with fcntl(), clearing O_NONBLOCK should be a separate option,
since this action might have side effects on the file, so you shouldn't
do it unless you know what the file is, and in particular you shouldn't
do it in filters. Side effects are more common than they should be,
since most implementations of fcntl(... O_NONBLOCK) are buggy. They
tend to set O_NONBLOCK on the device (when the file is for a device)
where they should set it only on the open file. Thus they affect other
opens of the device.
The old stdio also couldn't have used select() to avoid possibly spinning
doing i/o's. Yet another ffcntl() flag could be used to control whether
select()/poll()/kqueue is used. This would be even more unportable and
device-dependent (for best results).
More information about the freebsd-bugs