I read the man
pages, and my understanding is that if write()
fails and sets the errno
to EAGAIN
or EINTR
, I may
Yes, there are cleaner ways to use write()
: the class of write functions taking a FILE*
as an argument. That is, most importantly, fprintf()
and fwrite()
. Internally, these library functions use the write()
syscall to do their job, and they handle stuff like EAGAIN
and EINTR
.
If you only have a file descriptor, you can always wrap it into a FILE*
by means of fdopen()
, so you can use it with the functions above.
However, there is one pitfall: FILE*
streams are usually buffered. This can be a problem if you are communicating with some other program and are waiting for its response. This may deadlock both programs even though there is no logical error, simply because fprintf()
decided to defer the corresponding write()
a bit. You can switch the buffering off, or fflush()
output streams whenever you actually need the write()
calls to be performed.
EINTR
and EAGAIN
handling should often be slightly different. EAGAIN
is always some kind of transient error representing the state of the socket buffer (or perhaps, more precisely, that your operation may block).
Once you've hit an EAGAIN
you'd likely want to sleep a bit or return control to an event loop (assuming you're using one).
With EINTR
the situation is a bit different. If your application is receiving signals non-stop, then it may be an issue in your application or environment, and for that reason I tend to have some kind of internal eintr_max
counter so I am not stuck in the theoretical situation where I just continue infinitely looping on EINTR
.
Alnitak's answer (sufficient for most cases) should also be saving errno
somewhere, as it may be clobbered by perror()
(although it may have been omitted for brevity).
You have a bit of a "don't repeat yourself" problem there - there's no need for two separate calls to write
, nor for two nested loops.
My normal loop would look something like this:
for (int n = 0; n < count; ) {
int ret = write(fd, (char *)buf + n, count - n);
if (ret < 0) {
if (errno == EINTR || errno == EAGAIN) continue; // try again
perror("write");
break;
} else {
n += ret;
}
}
// if (n < count) here some error occurred
I would prefer to poll
the descriptor in case of EAGAIN
instead of just busy looping and burning up CPU for no good reason. This is kind of a "blocking wrapper" for a non-blocking write
I use:
ssize_t written = 0;
while (written < to_write) {
ssize_t result;
if ((result = write(fd, buffer, to_write - written)) < 0) {
if (errno == EAGAIN) {
struct pollfd pfd = { .fd = fd, .events = POLLOUT };
if (poll(&pfd, 1, -1) <= 0 && errno != EAGAIN) {
break;
}
continue;
}
return written ? written : result;
}
written += result;
buffer += result;
}
return written;
Note that I'm not actually checking the results of poll
other than the return value; I figure the following write
will fail if there is a permanent error on the descriptor.
You may wish to include EINTR
as a retryable error as well by simply adding it to the conditions with EAGAIN
, but I prefer it to actually interrupt I/O.