Returning EINTR from close() (original) (raw)

Benefits for LWN subscribers

The primary benefit from subscribing to LWN is helping to keep us publishing, but, beyond that, subscribers get immediate access to all site content and access to a number of extra site features. Please sign up today!

The close() system call is capable of returning errors, but those error codes are often completely ignored. It's often considered a bad practice to do so, but lots of code has been written that way. Though, for Linux at least, there is a real question about what, if anything, can be done whenclose() returns an error—to the point where some say mostclose() error returns make no sense.

Ondřej Bílka posted to the libc-alpha mailing list (the development mailing list for the GNU C library or glibc) with a suggestion: turn any EINTR (system call interrupted) returns from close() on Linux into EINPROGRESS (operation in progress). It turns out that The Austin Group (which maintains POSIX) added language to clarify the behavior of close() in August 2012. Previously, the state of the file descriptor was undefined if an EINTR was returned, but the new interpretation says that an EINTR return requires that the descriptor still be open. EINPROGRESS should be returned if the system call is interrupted but the file descriptor is closed; thus Bílka's suggestion.

But others are not at all sure that close() should _ever_return an error (except for EBADF when passed an invalid file descriptor). As David Miller noted,close() returning errors is downright hazardous:

At one time many years ago there was a path in the kernel that returned an error from close and it broke so many things. Even emacs crashed.

The widespread overwhelming belief is that close() is just going to always succeed, and there is more harm than good from signalling errors at all from that function.

In fact, it is difficult to even return EINTR fromclose() on Linux, according to Christoph Hellwig. If the driver or filesystem's release()method returns an error, it is explicitly ignored. The only path that would allow a driver to return EINTR is if it provides aflush() method that does so. Hellwig plans to post a patch that would enforce a no-EINTR policy on that path as well.

If EINTR can never be returned, there is no real reason to map it toEINPROGRESS in glibc. But, since glibc may be used on an older kernel that can return EINTR in some rare situations, mapping it to something probably makes sense. That could be EINPROGRESSor, perhaps better still, just zero for success, as suggested by Rich Felker. There really isn't much the application programmer can do if close() returns an error. As Russ Allbery put it in a reply to Felker:

I suppose what I'm saying is that I'm in agreement with you that returning 0 is probably the best approach, since it's not at all clear what we gain from returning -1 with EINPROGRESS and I doubt code deals with it correctly anyway. (It's not even clear to me what "correctly" would entail in that case.) That said, between the other choices, the EINPROGRESS approach which declares the file descriptor closed strikes me as clearly better than the POSIX EINTR semantics (which sound like they're not even possible on Linux).

As Allbery said, the POSIX EINTR semantics are not really possible on Linux. The file descriptor passed to close() is de-allocated early in the processing of the system call and the same descriptor could already have been handed out to another thread by the time close() returns. The Linux behavior could be changed if there were a sufficiently good reason to do so, but, so far, that reason has been elusive.

So the POSIX-suggested handling of an EINTR, which is to retry theclose(), could actually be quite dangerous on Linux. For that reason, Mark Mentovai suggested a change to the glibc manual to avoid recommending retrying close() on Linux.

The topic came up on the linux-kernel mailing list back in 2005; Linus Torvalds was fairly adamant that an EINTRreturn should only be used to show that some other error has occurred (like the data was not flushed to the file), not that the descriptor was still open. In fact, Torvalds said that he didn't believe retryingclose() is right for almost any Unix system, not just Linux. Any application that really needs to catch I/O errors when the data gets flushed, should do so using fsync(), he said.

Perhaps there are POSIX systems out there that have a close() that may not actually de-allocate the file descriptor when it gets interrupted, but it's a little hard to see what the advantage of that would be. In many cases, the return code from close() is completely ignored (for good or ill), so leaving it open would just lead to a file descriptor leak. Even if the error is caught, the application can't really do anything to repair the situation, it can only retry the close(), which seems a little pointless. But, evidently, it wasn't pointless to The Austin Group.