Anonymous | Login | 2024-12-12 01:11 UTC |
Main | My View | View Issues | Change Log | Docs |
Viewing Issue Simple Details [ Jump to Notes ] | [ Issue History ] [ Print ] | ||||||
ID | Category | Severity | Type | Date Submitted | Last Update | ||
0000529 | [1003.1(2008)/Issue 7] System Interfaces | Objection | Error | 2011-12-18 01:26 | 2024-06-11 08:53 | ||
Reporter | Love4Boobies | View Status | public | ||||
Assigned To | ajosey | ||||||
Priority | normal | Resolution | Accepted As Marked | ||||
Status | Closed | ||||||
Name | Bogdan Barbu | ||||||
Organization | |||||||
User Reference | |||||||
Section | close - close a file descriptor | ||||||
Page Number | 676 | ||||||
Line Number | 22871 | ||||||
Interp Status | Approved | ||||||
Final Accepted Text | Note: 0001200 | ||||||
Summary | 0000529: fildes unspecified on close()'s [EINTR] | ||||||
Description |
The standard currently reads If close() is interrupted by a signal that is to be caught, it shall return -1 with errno set to [EINTR] and the state of fildes is unspecified. If a signal occurs and we reissue close(), we might get an [EBADF]. Worse, if a different thread opens a file at the wrong time and gets our file descriptor, we might end up closing the wrong file. |
||||||
Desired Action | We should discuss what current systems do before we reach a conclusion. | ||||||
Tags | issue8 | ||||||
Attached Files | |||||||
|
Relationships | |||||||||||||||||||||
|
Notes | |
(0001084) Love4Boobies (reporter) 2011-12-31 00:50 |
All OSes I looked at, including Linux, close the file descriptor (meaning that there is no need to reissue close()). Is there any historical reason why the standard's text doesn't reflect this? So far, there's no portable way to ensure that close() worked. |
(0001085) joerg (reporter) 2011-12-31 13:24 |
Did you really check whether all implementations can grant this even for remote NFS files? Note that close() flushes the local kernel cache to the remote server in the NFS case and that this may be a slow action. |
(0001107) geoffclare (manager) 2012-01-26 17:15 edited on: 2012-02-10 16:41 |
Interpretation response ------------------------ The standard is unclear on this issue, and no conformance distinction can be made between alternative implementations based on this. This is being referred to the sponsor. Rationale: ------------- Currently implementations can choose whether to close the file descriptor when close() returns an [EINTR] error. This is unsatisfactory since multithreaded applications need to know whether the file descriptor has been closed in order to know whether it is safe to call close() again. Notes to the Editor (not part of this interpretation): ------------------------------------------------------- Change: If close() is interrupted by a signal that is to be caught, it shall return -1 with errno set to [EINTR] and the state of fildes is unspecified. If an I/O error occurred while reading from or writing to the file system during close( ), it may return -1 with errno set to [EIO]; if this error is returned, the state of fildes is unspecified. to: If close() is interrupted by a signal that is to be caught, it shall return -1 with errno set to [EINTR] and fildes shall remain open; however, it is unspecified whether fildes can subsequently be passed to any function except close() without error. If any error other than [EINTR] and [EBADF] occurs, close() shall return -1 with errno set to the corresponding error number and shall close fildes. After line 22924 add a new paragraph to the end of the ERRORS section: The close() function shall not return an [EAGAIN] or [EWOULDBLOCK] error. After line 22966 add the following new paragraphs in the RATIONALE section: Earlier versions of this standard left the state of fildes unspecified after [EINTR] and [EIO] errors. This was unsatisfactory once threads were introduced, since multithreaded applications need to know whether fildes has been closed. They cannot simply call close() again regardless, because if fildes was closed by the first call another thread could have been allocated a file descriptor with the same value as fildes. The alternative of never retrying close() would lead to a file descriptor leak whenever close() did not close fildes. This standard now requires that close() leaves the file descriptor open on [EINTR] errors and closes the file descriptor for all other errors (except [EBADF]). Leaving the file descriptor open on [EINTR] is necessary so that the application can call close() again to continue to wait for the close operation to complete; in some cases there is no other portable way for the application to know when the event that close() was waiting for when it was interrupted has completed (for example, a tape drive rewinding). Although the file descriptor is left open on [EINTR], it might no longer be usable; that is, passing it to any function except close() might result in an error such as [EIO]. The standard developers considered introducing a thread-local variable that close() would set to indicate whether it had closed fildes when returning -1. However, this was rejected in favor of the simpler solution of requiring close() to close fildes for all errors except [EINTR] (and [EBADF]). Application writers then have a simple rule to follow when close() returns -1: it is safe to retry the close() if errno is [EINTR], otherwise it is not safe. Requiring or allowing the file descriptor to be left open after an [EIO] error could lead to the situation where it is uncloseable because all subsequent repeat attempts just produce another [EIO] error. Another consideration was whether implementations might return [EAGAIN] as an extension and whether close() should be required to leave the file descriptor open in this case, since [EAGAIN] normally implies an operation should be retried. It seemed very unlikely that any implementation would have a legitimate reason to return [EAGAIN] and therefore this requirement would mean applications have to include code for the [EAGAIN] case that will never be used. Therefore close() is now forbidden from returning [EAGAIN] errors. Likewise for [EWOULDBLOCK]. |
(0001108) eblake (manager) 2012-01-26 20:31 |
The Linux kernel currently always frees the file descriptor (no chance for a retry; the filedes can immediately be reused by another open()), for both EINTR and EIO. Maybe it is safer to state that the fd is _always_ closed, even if failure is reported? http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=fs/open.c;h=77becc04114908fae2a45f655ed22996264723ad;hb=HEAD#l1063 [^] |
(0001109) lacos (reporter) 2012-01-26 20:50 |
The following is an argument of why POSIX should mandate the Linux behavior of always closing the fd, in parallel with any error reporting, where retry after error is not a viable coding pattern. It is a verbatim paste from an internal mailing list; please excuse the tone. It should not detract from the validity of the point. Thank you. --------o-------- I think this is akin to the C++ question "what happens when a destructor fails". ... It can only swallow (and possibly log) an error. Since a destructor is executed automatically, there's simply no way to report an error from it. Throwing an exception is out of question, because we can already be on an exception (stack unwind) path. This is why putting close() / fclose() in destructors and doing nothing else is a completely busted idea. Such calls in destructors should be responsible for nothing else than reclaiming resources. Any application caring about data safety should call at least fflush() still on the normal path (and check the retval), to make sure the destructor's fclose() will not have to call write(). Similarly after fflush(), applications that care deeply should call fsync() (and check the retval), so that the destructor's close() -- underlying fclose() or just plainly -- doesn't attempt to flush kernel buffers. See for example fclose(): "Whether or not the call succeeds, the stream shall be disassociated from the file and any buffer set by the setbuf() or setvbuf() function shall be disassociated from the stream." When a destructor fails, there's nothing to do. So, a destructor should only attempt to do things that can't fail. The Lnux manpage on close() singles out NFS. I think that's misguided (and also irrelevant a bit here, because write() on NFS is not POSIX-compliant anyway AFAICT). People should call fsync() if they care about the data hitting the (remote) platters. (If NFS provides that at all.) People probably find fsync() overkill. However, currently there's no portable way that (1) is less strict than fsync() and (2) ensures that the data reaches the "target" kernel's buffer cache. Because that's what they would like to have: ensuring that the data reaches the kernel that has physical control over the platters (id est, forcing it out of the process and all through the network), but they don't actually want to spend time on the remote platters (otherwise they'd just call fsync()). If close() fails for NFS, how does an application recover? (Not even considering the file descriptor itself, just the data.) It must restart from a known good state. I think the best thing is to kill the fd for good, even if it returned with an error only because it was interrupted by a signal. Similarly, the last close() on a TCP socket (that may linger, according to the socket option) is no guarantee for anything. I very seriously doubt it would wait for all ACKs to arrive -- in that case it would offer a capability that's not available to a process in any other way. So it will perhaps dump the last bytes from the socket buffer to the wire, and return then with "success". Not a guarantee for anything; the process will need application level ACKs from the other side. Another example: munmap(). Suppose we map a sparse file with MAP_SHARED and run into an ENOSPC while the kernel tries to flush (and allocate) in the background. SIGBUS is allowed (asynchronously) in this case. The same could happen even if the file is dense, but we run into an EIO. All fine this far. Now, does munmap() try to report anything like this? No, even SIGBUS got forbidden in SUSv2, even though in practice most writeable shared mappings will be flushed at munmap() time. In summary: the main purpose of close() is to remove a reference, and the last close() should trigger the kernel to reclaim the resource. Any related flushing is "best effort", and even though the failure of that best effort may be reported via the retval, it should not affect the main goal. Therefore it should be clear to an application how to proceed: - Consider the resource released, - assign no relevance whatsoever to a retval of zero, - assign some "recovery hints" to a nonzero retval at most. That "recovery strategy" is commonly: - print an error and exit with failure, - print an error and ignore the failure, - call "(void)close(fd)" to begin with. This is consistent with the automatic closure of all file descriptors at process exit time (... that are referred to by the exiting process only). |
(0001110) eblake (manager) 2012-01-26 23:23 |
More interesting reading: https://news.ycombinator.com/item?id=3363819 [^] with quotes like these: http://programming.itags.org/unix-linux-programming/79468/ [^] HP-UX 11.22: """[EINTR] An attempt to close a slow device or connection or file with pending aio requests was interrupted by a signal. The file descriptor still points to an open device or connection or file.""" AIX 5.3: """If the close subroutine is interrupted by a signal that is caught, it returns a value of -1, the errno global variable is set to EINTR and the state of the FileDescriptor parameter is closed.""" So, here we have two Unix implementations--both of which predated POSIX--that have incompatible definitions of close(). I do not feel it is reasonable to demand POSIX solve this, and sure enough: it didn't. Mac OS X: Here you should not retry, but you should also be careful; depending on whether you are UNIX2003 you will get different behavior from the close() function from libSystem, directing you to either the syscall close() or close_nocancel(). If you end up with close(), then the first thing that happens is __pthread_testcancel(1) is called, and if the thread is cancelled it will return EINTR before doing anything else: in this case, you would need to retry. However, I think close_nocancel(), which calls closef_locked(), might be capable of returning EINTR, which will be held and only returned from close_internal_locked() after _fdrelse() has already removed the descriptor. So, if it is the case that EINTR is capable of being returned from the closef_locked() call, you would need to /not/ retry, which thereby means that the close() version of this call is impossible to use safely on Mac OS X: if I were you I'd avoid it to use close_nocancel() (explicitly if warranted). I think you're missing the broader point. We don't actually have a problem with the close() behavior differing between implementations, we have a problem because there is no way to determine the correct course of action afterward. For HP-UX, it's "close it again". For AIX, it's apparently "everything is fine, don't close it again". Linux, too. That could be OK, even when undocumented, IF we had a way to discover what had occurred. But we don't. close() isn't broken because it can behave in multiple ways that can't be predicted in advance, it's broken because there's no safe way to proceed. |
(0001111) Love4Boobies (reporter) 2012-01-27 00:06 |
Right. So I see two options here: Somewhat of a hack: To introduce isclosed, a local similar to errno. Probably better: To deprecate close in favour of something else. |
(0001114) geoffclare (manager) 2012-01-27 10:02 edited on: 2012-01-27 10:25 |
I strongly disagree with the suggestions that close() should close the fd on EINTR. If close() is interrupted it is because it was waiting for completion of something time-consuming before it could close the fd and return, such as terminal output draining or a tape drive rewinding. If the fd is closed, the application then has no way of knowing when completion has occurred. If the fd stays open it can call close() again, and close() will again wait for completion. In some cases there are other ways the application can wait for completion before calling close() (the first time), such as using tcdrain() to wait for terminal output. However, for others such as tape drive rewinding there is no portable way to do that. HP got it right with HP-UX; AIX and Linux do the wrong thing. If requiring the HP-UX behaviour in POSIX is felt to be too controversial for TC2 then it could be deferred to Issue 8. The suggestion in Note: 0001111 of a (thread-local) variable that close() would set to indicate whether it closed the fd still has the problem that if close() closed the fd then the application has no way of knowing when the thing close() was waiting for has completed. (The other suggestion of deprecating close() does not, I believe, stand any chance of achieving consensus.) |
(0001115) Love4Boobies (reporter) 2012-01-28 04:09 edited on: 2012-02-03 15:04 |
(Edited because the original solution was too complicated.) The state of the file descriptor will remain unspecified. However, if it does remain open, close() will tell the kernel to signal when whatever it was waiting on is done. The default handler would be ignored. |
(0001120) geoffclare (manager) 2012-02-10 11:14 |
Note: 0001107 has been updated to reflect the decisions made in the Feb 9th teleconference, and this bug is now tagged for Issue 8 instead of TC2. |
(0001123) Richard Jones (reporter) 2012-02-11 16:01 |
I'd like to speak up for a large amount of code that does this:if (close (fd) == -1) { perror ("close"); // handle the error } and assumes that after that is executed, 'fd' is always closed. Which is true at least on Linux. If the standard is changed so that EINTR could leave the fd open, potentially all of this code must be changed. For what reason? So that a miniscule number of close calls involving tape drives and terminals can wait for tapes to rewind or terminal output to be drained. But these programs that care about tapes and terminals can easily just call the right ioctl to rewind the tape or flush the terminal. (Granted POSIX does not apparently cover tape rewinding -- that's something that could be usefully standardized in POSIX). This is a poor trade-off and will cause a large amount of work for numerous programs, to benefit a very small number of programs. Close should always close the file descriptor, as is done on the most popular POSIX platform today. |
(0001124) jrn (reporter) 2012-02-11 19:23 |
On Linux, if close() ever returns EINTR, that is most likely a (kernel) bug. If you care about file descriptor leaks and about portability to other platforms, you always had to do something about EINTR. |
(0001125) eblake (manager) 2012-02-13 20:00 |
The Linux community is adamantly opposed to any proposal that keeps the fd open on EINTR failure; quoting from Alexander Vino:This code is no more disastrous than far more convoluted variant they claim to be superior. Again, "close(2) by itself is not sufficient to close a file descriptor on HP-UX" is HP-UX bug. The workaround for that bug happens to break on saner-behaving kernels - sad, but again, this is no reason to duplicate their bug, breaking the userland code that worked correctly all along. Let me make it very, very clear - no matter how many times these guys assert HP-UX insane behaviour correct, no "fixes" to Linux one are going to be accepted. Consider it vetoed. By me, in role of Linux VFS maintainer. And I'm _very_ certain that getting Linus to agree will be a matter of minutes. Moreover, have fun getting *BSD (including MacOS X) to agree - I very much doubt that they'll like it more. How does Solaris behave, BTW? |
(0001126) geoffclare (manager) 2012-02-14 09:58 |
(Response to Note: 0001125) We need more information about Alexander's objection. If his primary concern is regular files, then maybe this can be solved by forbidding EINTR for regular files. |
(0001128) Konrad_Schwarz (reporter) 2012-02-15 10:53 |
(Response to Note: 0001114) The tape rewinding situation is resolved if close() completes immediately, without waiting for the tape to rewind. Rewinding could take several minutes, but is performed autonomously by the tape drive unit and does not require intervention by the kernel. If close() were to block, waiting for the tape drive to complete, the kernel would similarly be forced to wait on the implicit close() of the tape drive when the process exits. This would delay freeing up the process's resources unnecessarily. Applications wishing to ensure the tape has rewound can open() the tape device file after closing it. This should block until the tape drive is ready for operation. (If not, then at least the first read() will.) More generally, I can see little precedent that close() on a file descriptor has any sort of synchronous effect on devices. E.g., for asynchronous I/O, the standard makes clear that cancellation vs. completion of outstanding operations is entirely up to the implementation. For sockets, close() can block up to the linger time, but again, no guarantee of successful transmission is given. Still, for consistency, I think that on EINTR, the standard should specify the file descriptor remains open, so that the usual semantics of EINTR apply. Kernels wishing to close the file descriptor always must refrain from producing EINTR on an unsuccessful close(). EAGAIN is a related case. For reasons discussed in the comparison to the C++ destructor analogy above, I think the standard should disallow an unsuccessful close() from producing EAGAIN. |
(0001131) eblake (manager) 2012-02-16 17:00 |
This was reopened on the 16 Feb meeting; action to Eric to propose a wording for Issue 8 that mandates Linux and Solaris behavior on close(), as well as add a new posix_close(int fd, int flags) which can be used to give HP-UX behavior. |
(0001133) Love4Boobies (reporter) 2012-02-17 02:45 |
Wouldn't that break legacy HP-UX applications which already use close() and make certain assumptions about it? Perhaps it would be best to just make the kernel/VFS/driver/whatever signal when whatever close() was waiting on is finished, and say that the default behavior of the handler is to either close the descriptor or leave it opened. The legacy code's assumptions would still hold on the UNIX implementations they were designed for, and new code would just define a handler that does something portable. Note that no assumptions made by legacy applications would be broken because these signals don't occur on legacy systems. Nor would they be broken because they *do* occur on new systems. Thoughts? |
(0001134) eblake (manager) 2012-02-17 05:15 edited on: 2012-02-23 17:00 |
Interpretation response ------------------------ The standard is unclear on this issue, and no conformance distinction can be made between alternative implementations based on this. This is being referred to the sponsor. Rationale: ------------- Currently implementations can choose whether to close the file descriptor when close() returns an [EINTR] error. This is unsatisfactory since multithreaded applications need to know whether the file descriptor has been closed in order to know whether it is safe to call close() again. Notes to the Editor (not part of this interpretation): ------------------------------------------------------- After line 15033 [XBD <unistd.h> Constants for Functions], add: The <unistd.h> header shall define the following symbolic constant as a value for the flag used by posix_close( ): POSIX_CLOSE_RESTART Allows restarts if a signal interrupts a close operation. After line 15094 [XBD <unistd.h> Declarations], add: int posix_close(int, int); At line 23862 [XSH close NAME], change: close - close a file descriptor to: close, posix_close - close a file descriptor After line 22865 [XSH close SYNOPSIS], add: int posix_close(int fildes, int flag); At line 22871 [XSH close DESCRIPTION], change: If close( ) is interrupted by a signal that is to be caught, it shall return -1 with errno set to [EINTR] and the state of fildes is unspecified. If an I/O error occurred while reading from or writing to the file system during close( ), it may return -1 with errno set to [EIO]; if this error is returned, the state of fildes is unspecified. to: The close( ) function shall close fildes, even if an error occurs (except for [EBADF]). If close( ) is interrupted by a signal that is to be caught, then close( ) shall either return 0 to indicate that the close operation has completed successfully, or shall return -1 with errno set to [EINPROGRESS] to indicate that the close operation is incomplete and is continuing asynchronously, and the process shall have no further ability to track the completion or final status of the close operation. After line 22915 [XSH close DESCRIPTION], add: The posix_close( ) function shall be equivalent to the close( ) function, except that if flag is non-zero, the behavior shall be modified as described below. If flag includes POSIX_CLOSE_RESTART, and posix_close( ) is interrupted by a signal that is to be caught, then posix_close( ) may return -1 with errno set to [EINTR], in which case fildes shall be left open; however, it is unspecified whether fildes can subsequently be passed to any function except close( ) or posix_close( ) without error. If flag is invalid, posix_close( ) may fail with errno set to [EINVAL], but shall otherwise behave as if flag had been 0 and close fildes. At line 22920 [XSH close ERRORS], change: The close( ) function shall fail if: [EBADF] The fildes argument is not a valid file descriptor. [EINTR] The close( ) function was interrupted by a signal. The close( ) function may fail if: [EIO] An I/O error occurred while reading from or writing to the file system. to: The close( ) and posix_close( ) functions shall fail if: [EBADF] The fildes argument is not a valid file descriptor. [EINPROGRESS] The function was interrupted by a signal and fildes was closed but the close operation is continuing asynchronously. The close( ) and posix_close( ) functions may fail if: [EIO] An I/O error occurred while reading from or writing to the file system. The posix_close( ) function may fail if: [EINTR] The posix_close( ) function was interrupted by a signal and the flag argument included POSIX_CLOSE_RESTART, in which case fildes is still open. [EINVAL] The value of the flag argument is invalid. The close( ) and posix_close( ) functions shall not return an [EAGAIN] or [EWOULDBLOCK] error. The close( ) function shall not return an [EINTR] error, and the posix_close( ) function shall not return an [EINTR] error unless flag includes POSIX_CLOSE_RESTART. At line 22964 [XSH close RATIONALE], change: The use of interruptible device close routines should be discouraged to avoid problems with the implicit closes of file descriptors by exec and exit( ). This volume of POSIX.1-2008 only intends to permit such behavior by specifying the [EINTR] error condition. to: The use of interruptible device close routines should be discouraged to avoid problems with the implicit closes of file descriptors, such as by exec, process termination, or dup2( ). This volume of <POSIX> only intends to permit such behavior by specifying the [EINTR] error condition for posix_close( ) with POSIX_CLOSE_RESTART, to allow applications a portable way to know when the event that posix_close( ) was waiting for when it was interrupted has completed (for example, a tape drive rewinding). Although the file descriptor is left open on [EINTR], it might no longer be usable; that is, passing it to any function except close( ) or posix_close( ) might result in an error such as [EIO]. If an application must guarantee that data will not be lost, it is recommended that the application use fsync( ) or fdatasync( ) prior to the close operation, rather than leaving the close operation to deal with pending I/O and risk an interrupt. Earlier versions of this standard left the state of fildes unspecified after errors such as [EINTR] and [EIO]; and implementations differed on whether close( ) left fildes open after [EINTR]. This was unsatisfactory once threads were introduced, since multithreaded applications need to know whether fildes has been closed. Applications cannot blindly call close( ) again, because if fildes was closed by the first call another thread could have been allocated a file descriptor with the same value as fildes, which must not be closed by the first thread. On the other hand, the alternative of never retrying close( ) would lead to a file descriptor leak in implementations where close( ) did not close fildes. This standard now requires that close( ) must close the file descriptor regardless of errors (except [EBADF]), and further requires that close( ) must not fail with [EINTR]; this allows code that previously retried close( ) after [EINTR] to avoid closing the wrong fildes because the retry loop will no longer be reached, while avoiding file descriptor leaks in code that does not check for failures, thus making code that assumed either implementation portable. It should also be noted that the requirement for close( ) to always close fildes, even if an error is reported, is similar to the requirements on fclose( ) to always release the stream, even if an error is encountered while flushing data. Implementations that previously always closed fildes can meet the new requirements by translating [EINTR] to [EINPROGRESS] in close( ); and it is the intent of this standard to allow such an implementation to ignore POSIX_CLOSE_RESTART and never fail with [EINTR], rather than having to add restart semantics. On the other hand, implementations that previously left fildes open on [EINTR] can map that to posix_close( ) with POSIX_CLOSE_RESTART, and must add the semantics of posix_close( ) when flag is 0; one possibility is by wrapping the original close( ) implementation with a check of errno, and on [EINTR], using actions similar to dup2( ) to replace the incomplete close operation with another file descriptor that can be closed immediately by the original close( ), all before returning to the application. Such an implementation is encouraged to use versioned symbols or symbol translations within the compiler, where applications compiled against a previous version of the standard treat close( ) as if it were like posix_close( ) with POSIX_CLOSE_RESTART, so as not to break implementation-specific expectations of those older applications about waiting for restartable device closes, while applications compiled against this version of the standard must be explicitly rewritten to use posix_close( ) for [EINTR] to work, in the rare cases where a retry loop is desired. The standard developers considered introducing a thread-local variable that close( ) would set to indicate whether it had closed fildes when returning -1. However, this was rejected in favor of the simpler solution of requiring close( ) to close fildes for all errors (except [EBADF]), and adding posix_close( ) as the way to expose the retry semantics. Additionally, while the name posix_close( ) is new to this standard, it is reminiscent of at least one implementation that introduced an alternate system call named close_nocancel( ) in order to allow an application to choose whether restart semantics were desired. Another consideration was whether implementations might return [EAGAIN] as an extension and whether close( ) should be required to leave the file descriptor open in this case, since [EAGAIN] normally implies an operation should be retried. It seemed very unlikely that any implementation would have a legitimate reason to return [EAGAIN] or [EWOULDBLOCK], and therefore this requirement would mean applications have to include code for an error case that will never be used. Therefore close( ) is now forbidden from returning [EAGAIN] and [EWOULDBLOCK] errors. Before page 1410 [XSH posix_fadvise], add a new redirect page: NAME posix_close - close a file descriptor SYNOPSIS #include <unistd.h> int posix_close(int fildes, int flag); DESCRIPTION Refer to close( ). |
(0001200) eblake (manager) 2012-04-12 16:00 edited on: 2012-04-19 15:18 |
Interpretation response ------------------------ The standard is unclear on this issue, and no conformance distinction can be made between alternative implementations based on this. This is being referred to the sponsor. Rationale: ------------- Currently implementations can choose whether to close the file descriptor when close() returns an [EINTR] error. This is unsatisfactory since multithreaded applications need to know whether the file descriptor has been closed in order to know whether it is safe to call close() again. Notes to the Editor (not part of this interpretation): ------------------------------------------------------- After line 15033 [XBD <unistd.h> Constants for Functions], add: The <unistd.h> header shall define the following symbolic constant as a value for the flag used by posix_close( ): POSIX_CLOSE_RESTART Allows restarts if a signal interrupts a close operation. This constant shall not be 0 unless posix_close( ) never returns -1 with errno set to EINTR. After line 15094 [XBD <unistd.h> Declarations], add: int posix_close(int, int); At line 23862 [XSH close NAME], change: close - close a file descriptor to: close, posix_close - close a file descriptor After line 22865 [XSH close SYNOPSIS], add: int posix_close(int fildes, int flag); At line 22871 [XSH close DESCRIPTION], change: If close( ) is interrupted by a signal that is to be caught, it shall return -1 with errno set to [EINTR] and the state of fildes is unspecified. If an I/O error occurred while reading from or writing to the file system during close( ), it may return -1 with errno set to [EIO]; if this error is returned, the state of fildes is unspecified. to: If close( ) is interrupted by a signal that is to be caught, then it is unspecified whether it returns -1 with errno set to [EINTR] with fildes remaining open, or returns -1 with errno set to [EINPROGRESS] with fildes being closed, or returns 0 to indicate successful completion; except that if POSIX_CLOSE_RESTART is defined as 0, then the option of returning -1 with errno set to [EINTR] and fildes remaining open shall not occur. If close() returns -1 with errno set to [EINTR], it is unspecified whether fildes can subsequently be passed to any function except close( ) or posix_close( ) without error. For all other error situations (except for [EBADF] where fildes was invalid), fildes shall be closed. If fildes was closed even though the close operation is incomplete, the close operation shall continue asynchronously and the process shall have no further ability to track the completion or final status of the close operation. After line 22915 [XSH close DESCRIPTION], add: The posix_close( ) function shall be equivalent to the close( ) function, except with the modifications based on the flag argument as described below. If flag is 0, then posix_close shall not return -1 with errno set to [EINTR], which implies that fildes will always be closed (except for [EBADF], where fildes was invalid). If flag includes POSIX_CLOSE_RESTART and POSIX_CLOSE_RESTART is defined as a non-zero value, and posix_close( ) is interrupted by a signal that is to be caught, then posix_close( ) may return -1 with errno set to [EINTR], in which case fildes shall be left open; however, it is unspecified whether fildes can subsequently be passed to any function except close( ) or posix_close( ) without error. If flag is invalid, posix_close( ) may fail with errno set to [EINVAL], but shall otherwise behave as if flag had been 0 and close fildes. At line 22920 [XSH close ERRORS], change: The close( ) function shall fail if: [EBADF] The fildes argument is not a valid file descriptor. [EINTR] The close( ) function was interrupted by a signal. The close( ) function may fail if: [EIO] An I/O error occurred while reading from or writing to the file system. to: The close( ) and posix_close( ) functions shall fail if: [EBADF] The fildes argument is not a valid file descriptor. [EINPROGRESS] The function was interrupted by a signal and fildes was closed but the close operation is continuing asynchronously. The close( ) and posix_close( ) functions may fail if: [EINTR] The function was interrupted by a signal, POSIX_CLOSE_RESTART is defined as non-zero, and (in the case of posix_close( )) the flag argument included POSIX_CLOSE_RESTART, in which case fildes is still open. [EIO] An I/O error occurred while reading from or writing to the file system. The posix_close( ) function may fail if: [EINVAL] The value of the flag argument is invalid. The close( ) and posix_close( ) functions shall not return an [EAGAIN] or [EWOULDBLOCK] error. If POSIX_CLOSE_RESTART is zero, the close( ) function shall not return an [EINTR] error. The posix_close( ) function shall not return an [EINTR] error unless flag includes a non-zero POSIX_CLOSE_RESTART. At line 22964 [XSH close RATIONALE], change: The use of interruptible device close routines should be discouraged to avoid problems with the implicit closes of file descriptors by exec and exit( ). This volume of POSIX.1-2008 only intends to permit such behavior by specifying the [EINTR] error condition. to: The use of interruptible device close routines should be discouraged to avoid problems with the implicit closes of file descriptors, such as by exec, process termination, or dup2( ). This volume of <POSIX> only intends to permit such behavior by specifying the [EINTR] error condition for close( ) and posix_close( ) with non-zero POSIX_CLOSE_RESTART, to allow applications a portable way to resume waiting for an event associated with the close operation (for example, a tape drive rewinding) after receiving an interrupt. This standard also permits implementations to define POSIX_CLOSE_RESTART to 0 if they do not choose to provide a way to restart an interrupted close action. Although the file descriptor is left open on [EINTR], it might no longer be usable; that is, passing it to any function except close( ) or posix_close( ) might result in an error such as [EIO]. If an application must guarantee that data will not be lost, it is recommended that the application use fsync( ) or fdatasync( ) prior to the close operation, rather than leaving the close operation to deal with pending I/O and risk an interrupt. Earlier versions of this standard left the state of fildes unspecified after errors such as [EINTR] and [EIO]; and implementations differed on whether close( ) left fildes open after [EINTR]. This was unsatisfactory once threads were introduced, since multithreaded applications need to know whether fildes has been closed. Applications cannot blindly call close( ) again, because if fildes was closed by the first call another thread could have been allocated a file descriptor with the same value as fildes, which must not be closed by the first thread. On the other hand, the alternative of never retrying close( ) would lead to a file descriptor leak in implementations where close( ) did not close fildes, although such a leak may be harmless if the process is about to exit or the file descriptor is marked FD_CLOEXEC and the process is about to be replaced by exec. This standard introduced posix_close( ) with a flag argument that allows a choice between the two possible error behaviors, and leaves it unspecified which of the two behaviors is implemented by close( ) (although it is guaranteed to be one of the two behaviors of posix_close( ), rather than leaving things completely unspecified as in earlier versions of the standard). Note that the standard requires that close( ) and posix_close( ) must leave fildes open after [EINTR] (in the cases where [EINTR] is permitted) and must close the file descriptor regardless of all other errors (except [EBADF], where fildes is already invalid). In general, portable applications should only retry a close( ) after checking for [EINTR] (and on implementations where POSIX_CLOSE_RESTART is defined to be zero, this retry loop will be dead code), and risk a file descriptor leak if a retry loop is not attempted. It should also be noted that [EINTR] is only possible if close( ) can be interrupted; if no signal handlers are installed, then close( ) will not be interrupted. Conversely, if a single-threaded application can guarantee that no file descriptors are opened or closed in signal handlers, then a retry loop without checking for [EINTR] will be harmless (since the retry will fail with [EBADF]), but guaranteeing that no external libraries introduce the use of threading can be difficult. There are additional guarantees for applications which will only ever be used on systems where POSIX_CLOSE_RESTART is defined as 0. These observations should help in determining whether an application needs to have its close( ) calls audited for replacement with posix_close( ). It should also be noted that the requirement for posix_close( ) with a flag of 0 to always close fildes, even if an error is reported, is similar to the requirements on fclose( ) to always release the stream, even if an error is encountered while flushing data. Implementations that previously always closed fildes can meet the new requirements by translating [EINTR] to [EINPROGRESS] in close( ); and may define POSIX_CLOSE_RESTART to 0 rather than having to add restart semantics. On the other hand, implementations that previously left fildes open on [EINTR] can map that to posix_close( ) with POSIX_CLOSE_RESTART, and must add the semantics of posix_close( ) when flag is 0; one possibility is by calling the original close( ) implementation, checking for failure, and on [EINTR], using actions similar to dup2( ) to replace the incomplete close operation with another file descriptor that can be closed immediately by another call to the original close( ), all before returning to the application. Either way, close( ) should always map to one of the two behaviors of posix_close( ), and implementations are encouraged to keep the behavior of close( ) unchanged so as not to break implementation-specific expectations of older applications that were relying on behavior not specified by older versions of this standard. The standard developers considered introducing a thread-local variable that close( ) would set to indicate whether it had closed fildes when returning -1. However, this was rejected in favor of the simpler solution of tightening close( ) to guarantee that fildes is closed except for [EINTR], and exposing a choice of whether to expect [EINTR] by adding posix_close( ). Additionally, while the name posix_close( ) is new to this standard, it is reminiscent of at least one implementation that introduced an alternate system call named close_nocancel( ) in order to allow an application to choose whether restart semantics were desired. Another consideration was whether implementations might return [EAGAIN] as an extension and whether close( ) should be required to leave the file descriptor open in this case, since [EAGAIN] normally implies an operation should be retried. It seemed very unlikely that any implementation would have a legitimate reason to return [EAGAIN] or [EWOULDBLOCK], and therefore this requirement would mean applications have to include code for an error case that will never be used. Therefore close( ) is now forbidden from returning [EAGAIN] and [EWOULDBLOCK] errors. Before page 1410 [XSH posix_fadvise], add a new redirect page: NAME posix_close - close a file descriptor SYNOPSIS #include <unistd.h> int posix_close(int fildes, int flag); DESCRIPTION Refer to close( ). |
(0001255) Love4Boobies (reporter) 2012-06-12 16:10 |
The standard doesn't say what the state of the file descriptor is in the case of dup/fcntl(fildes, F_DUPFD, fildes2) or dup2 being interrupted by a signal. |
(0001290) ajosey (manager) 2012-06-29 16:16 |
Interpretation proposed 29 June 2012 for final 45 day review |
(0001353) ajosey (manager) 2012-08-30 09:14 |
Interpretation approved 30 Aug 2012 |
(0002567) eblake (manager) 2015-02-26 16:49 |
In response to Note: 0001255, dup/fcntl(F_DUPFD) do not document EINTR [fcntl only documents EINTR for F_SETLKW], and it's fairly obvious what happens: if that action is interrupted, returning -1 for failure means no fd was duplicated. dup2() does document EINTR, but that would be in the case of attempting to close the existing fd prior to duplicating the given fd in its place. And there, it DOES say what happens (line 25126): "If the close operation fails to close fildes2, dup2( ) shall return −1 without changing the open file description to which fildes2 refers." That is, if dup2() is interrupted and fails with EINTR, the fd is still open to its original state, and the new fd was not duplicated. |
Issue History | |||
Date Modified | Username | Field | Change |
2011-12-18 01:26 | Love4Boobies | New Issue | |
2011-12-18 01:26 | Love4Boobies | Status | New => Under Review |
2011-12-18 01:26 | Love4Boobies | Assigned To | => ajosey |
2011-12-18 01:26 | Love4Boobies | Name | => Bogdan Barbu |
2011-12-18 01:26 | Love4Boobies | Section | => close - close a file descriptor |
2011-12-18 01:26 | Love4Boobies | Page Number | => http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html [^] |
2011-12-18 01:26 | Love4Boobies | Line Number | => Description |
2011-12-31 00:50 | Love4Boobies | Note Added: 0001084 | |
2011-12-31 13:24 | joerg | Note Added: 0001085 | |
2012-01-26 16:04 | msbrown | Project | 2008-TC1 => 1003.1(2008)/Issue 7 |
2012-01-26 17:15 | geoffclare | Page Number | http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html [^] => 676 |
2012-01-26 17:15 | geoffclare | Line Number | Description => 22871 |
2012-01-26 17:15 | geoffclare | Interp Status | => Pending |
2012-01-26 17:15 | geoffclare | Note Added: 0001107 | |
2012-01-26 17:15 | geoffclare | Status | Under Review => Interpretation Required |
2012-01-26 17:15 | geoffclare | Resolution | Open => Accepted As Marked |
2012-01-26 17:15 | geoffclare | Final Accepted Text | => Note: 0001107 |
2012-01-26 17:16 | geoffclare | Tag Attached: tc2-2008 | |
2012-01-26 20:31 | eblake | Note Added: 0001108 | |
2012-01-26 20:50 | lacos | Note Added: 0001109 | |
2012-01-26 23:23 | eblake | Note Added: 0001110 | |
2012-01-27 00:06 | Love4Boobies | Note Added: 0001111 | |
2012-01-27 10:02 | geoffclare | Note Added: 0001114 | |
2012-01-27 10:25 | geoffclare | Note Edited: 0001114 | |
2012-01-28 04:09 | Love4Boobies | Note Added: 0001115 | |
2012-01-28 04:10 | Love4Boobies | Note Edited: 0001115 | |
2012-01-28 04:11 | Love4Boobies | Note Edited: 0001115 | |
2012-01-28 04:12 | Love4Boobies | Note Edited: 0001115 | |
2012-01-28 04:17 | Love4Boobies | Note Edited: 0001115 | |
2012-02-03 15:04 | Love4Boobies | Note Edited: 0001115 | |
2012-02-09 16:35 | eblake | Tag Detached: tc2-2008 | |
2012-02-09 16:35 | eblake | Tag Attached: issue8 | |
2012-02-10 11:11 | geoffclare | Note Edited: 0001107 | |
2012-02-10 11:14 | geoffclare | Note Added: 0001120 | |
2012-02-10 16:41 | eblake | Note Edited: 0001107 | |
2012-02-11 16:01 | Richard Jones | Note Added: 0001123 | |
2012-02-11 19:23 | jrn | Note Added: 0001124 | |
2012-02-13 20:00 | eblake | Note Added: 0001125 | |
2012-02-14 09:58 | geoffclare | Note Added: 0001126 | |
2012-02-15 10:53 | Konrad_Schwarz | Note Added: 0001128 | |
2012-02-16 17:00 | eblake | Note Added: 0001131 | |
2012-02-16 17:00 | eblake | Resolution | Accepted As Marked => Reopened |
2012-02-17 02:18 | eblake | Relationship added | related to 0000498 |
2012-02-17 02:45 | Love4Boobies | Note Added: 0001133 | |
2012-02-17 05:15 | eblake | Note Added: 0001134 | |
2012-02-23 16:34 | eblake | Note Edited: 0001134 | |
2012-02-23 16:57 | Don Cragun | Final Accepted Text | Note: 0001107 => Note: 0001134 |
2012-02-23 17:00 | Don Cragun | Note Edited: 0001134 | |
2012-04-12 16:00 | eblake | Note Added: 0001200 | |
2012-04-19 15:18 | eblake | Note Edited: 0001200 | |
2012-04-19 15:19 | eblake | Final Accepted Text | Note: 0001134 => Note: 0001200 |
2012-04-19 15:19 | eblake | Resolution | Reopened => Accepted As Marked |
2012-06-12 16:10 | Love4Boobies | Note Added: 0001255 | |
2012-06-26 23:15 | eblake | Relationship added | related to 0000590 |
2012-06-29 16:16 | ajosey | Interp Status | Pending => Proposed |
2012-06-29 16:16 | ajosey | Note Added: 0001290 | |
2012-08-30 09:14 | ajosey | Interp Status | Proposed => Approved |
2012-08-30 09:14 | ajosey | Note Added: 0001353 | |
2012-09-24 22:17 | eblake | Relationship added | related to 0000614 |
2012-11-28 17:27 | nick | Relationship added | related to 0000632 |
2015-02-26 16:49 | eblake | Note Added: 0002567 | |
2020-03-18 15:50 | geoffclare | Status | Interpretation Required => Applied |
2024-06-11 08:53 | agadmin | Status | Applied => Closed |
Mantis 1.1.6[^] Copyright © 2000 - 2008 Mantis Group |