
On 17/12/2010, at 12:03 PM, Mitar wrote:
Yes, this is the same problem as when designing an operating system: what should happen to a system call if it is interrupted. And I agree with elegance of simply returning EINTR errno. This makes system calls much easier to implement and for user it is really simple to wrap it in a loop retrying it. So it is quite often to see such function:
static int xioctl( int fd, int request, void *arg) { int r;
do { r = ioctl(fd, request, arg); } while (-1 == r && EINTR == errno);
return r; }
And _this_ you call "simple elegance"? It may be simple, but the user has to know to do it, and the user has to do it over and over and over again. There are gotchas when this kind of thing propagates into higher layers. For example, in another window I have the source code for fwrite() from OpenSolaris. (Actually, there are three fwrite.c files, and I'm not at all sure that the one I'm looking at, /usr/src/lib/libc/port/stdio/fwrite.c, is the correct one.) In the _IONBF case there are loops that look like while ((n = write(fileno(iop), dptr, s)) != s) { if (n == -1) { if (!cancel_active()) iop->_flag |= _IOERR; return written/size; } else { dptr += n; s -= n; written += n; } } Do you see an EINTR loop there? Neither do I. This means that it is possible for fwrite() to return -1 with ferror() true and errno == EINTR. If you are writing to the ANSI C standard, there is no way you can respond appropriately to this because EINTR is not part of the C standard. It _is_ part of the POSIX standard that file operations are defined to set errno when they go wrong. However, there is a really nasty gotcha here. The loop do { r = fwrite(p, sizeof *p, n, output); } while (r == 0 && errno == EINTR); is simply wrong. There may have been successful partial writes before the final interrupted one. So let's try again. r is the number of complete items written. If the write is interrupted, this need not be zero. So for (;;) { r = fwrite(p, sizeof *p, n, output); if (!ferror(output)) return SUCCESS; if (errno != EINTR) return FAILURE; p += r; n -= r; } Not quite so easy to write. I may have made any number of mistakes, but it doesn't matter, because this still has a problem. fwrite() returns the number of complete items written (written/size), but in the case of a partial write, it may have written *more* than that. Normally this doesn't matter, because almost every possible error from fwrite() is something you would not want to continue. The only two you might want to continue are EAGAIN and EINTR. EAGAIN can only arise if you've explicitly asked for non-blocking I/O, which you can't with fopen(). Now I'm not saying that you _couldn't_ design something like fwrite() that _would_ be resumable after EINTR. My point is that apparently cute hacks like EINTR have a nasty way of propagating into higher level interfaces and breaking them.