User: Password:
Subscribe / Log in / New account

Returning EINTR from close()

This article brought to you by LWN subscribers

Subscribers to made this article — and everything that surrounds it — possible. If you appreciate our content, please buy a subscription and make the next set of articles possible.

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.

(Log in to post comments)

Returning EINTR from close()

Posted Dec 12, 2013 10:21 UTC (Thu) by sorokin (subscriber, #88478) [Link]

I think the problem is that close() attempts to do two different things:

1. flush pending writes
2. return used file descriptor

First operation may fail and should be handled the same way errors in write() are handled.
The second operation may fail only due to error in program.

This separation is handy when I'm writing file and write() returns error. I should free file descriptor, but I don't want to flush pending data (and handle corresponding errors).

WRT to EINTR: first operation may return EINTR (if necessary), second operator should never return EINTR.

Returning EINTR from close()

Posted Dec 14, 2013 19:26 UTC (Sat) by giraffedata (subscriber, #1954) [Link]

I agree, and I bristle every time I see someone make close() do anything other than declare that a session of file operations has ended. (It's not normal, by the way - a typical close() does leave pending writes pending). Declaring is something that cannot logically fail.

I think the philosophy of the passive destructor was not well known when Unix close() was defined, so I won't call the fact that close() has a return code a design mistake, but we should all understand that our direction should be consistently away from that.

There shouldn't even be a failure for bad file descriptor. It works better if that causes a signal.

There's an even more important philosophy of failures at issue here: when a call fails, it should fail completely; have no effect. EINTR failures in particular always respect that. If there is a close() anywhere that fails with EINTR after releasing the file descriptor, that's just a bug.

Returning EINTR from close()

Posted Dec 19, 2013 22:08 UTC (Thu) by silly_sad (guest, #94576) [Link]

> the POSIX-suggested handling of an EINTR, which is to retry the close()

this is even bigger trouble than flushing writes in the close()
why in the first place they think that the close() or flush() failure is of application's concern?!

they suggested retry...
shall i wrap each syscall in a cycle?!
just do it yourself, and don't bother the application -- this is a philosophy of ROUTINE itself.

ERROR message must tell the application what it did wrong.
suggestion of "retry" implies that application did NOTHING WRONG.

So, those who vote for "always return success" are totally right, there is absolutely nothing an application can do in case of a close()'s error.

Returning EINTR from close()

Posted Dec 25, 2013 23:19 UTC (Wed) by nix (subscriber, #2304) [Link]

they suggested retry... shall i wrap each syscall in a cycle?!
For syscalls that may return -EINTR like read() and write() this has always been true. That on local storage you can often get away without it doesn't make it any less necessary (e.g. on NFS both EINTRed and short reads and writes can and do happen).

Returning EINTR from close()

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

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:

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.

2) just kidding :)

Returning EINTR from close()

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

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:

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