|This article brought to you by LWN subscribers|
Subscribers to LWN.net 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.
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:
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:
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.
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