| |
Log in / Subscribe / Register

Returning EINTR from close()

LWN.net needs you!

Without subscribers, LWN would simply not exist. Please consider signing up for a subscription and helping to keep LWN publishing.

By Jake Edge
December 11, 2013

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 when close() returns an error—to the point where some say most close() 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 from close() 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 a flush() 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 to EINPROGRESS 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 EINPROGRESS or, 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 the close(), 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 EINTR return 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 retrying close() 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.



Returning EINTR from close()

Posted Dec 12, 2013 10:33 UTC (Thu) by roc (subscriber, #30627) [Link] (2 responses)

AFS used to return errors on close() in some situations. It was a nightmare because applications didn't expect it and couldn't deal with it. I expect the situation is even worse now.

Returning EINTR from close()

Posted Dec 13, 2013 6:13 UTC (Fri) by geofft (subscriber, #59789) [Link]

I believe under certain circumstances that current OpenAFS will still return an error on close() if you're out of quota. MIT still carries the following patch to nmh:
http://debathena.mit.edu/trac/browser/trunk/debathena/thi...

Without it, it's possible for you to download mail from the mail server, fail to write it to your home directory because you're out of quota, have the error from close() be ignored, and just lose those messages.

Returning EINTR from close()

Posted Dec 14, 2013 19:19 UTC (Sat) by nix (subscriber, #2304) [Link]

I'm surprised Russ didn't bring it up, then, since he works for Stanford, which uses AFS in anger on a large scale (or did once, anyway).

Returning EINTR from close()

Posted Dec 12, 2013 13:47 UTC (Thu) by jschrod (subscriber, #1646) [Link]

Wasn't close() on a pipe one of the problematic use cases, as there could be errors in the last write flush more often than in other cases?

Returning EINTR from close()

Posted Dec 13, 2013 0:25 UTC (Fri) by tstover (guest, #56283) [Link]

On Apple Unix(1), two simultaneous calls to close() on the the same fd from two different processes with the same controlling terminal would return the number of disk sectors marked as bad during all the calls to write() for the lifetime of fd.

1) http://en.wikipedia.org/?title=A/UX
2) just kidding :)

Returning EINTR from close()

Posted Dec 13, 2013 12:42 UTC (Fri) by rwmj (subscriber, #5474) [Link] (1 responses)

Did POSIX really do the wrong thing here? I cannot find the actual resolution of the bug, but we tried to stand our ground during the process as you can see from eblake and my responses:

http://austingroupbugs.net/view.php?id=529

Returning EINTR from close()

Posted Dec 16, 2013 6:37 UTC (Mon) by jrn (subscriber, #64214) [Link]

See the link to 'Final Accepted Text' on the bugtracker you linked to.

As I understand it, Linux should never return EINTR from close(). No one seems to disagree with this, and it sounds like soon glibc will guard against it (yay!). So I don't see how POSIX advocating one or another behavior on EINTR from close() could be harmful on Linux, given that it would never happen.


Copyright © 2013, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds