Austin Group Defect Tracker

Aardvark Mark IV


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
related to 0000498Closedajosey During process termination, open file descriptors should be closed as if by appropriate calls to close() 
related to 0000590Closedajosey dup2 and signals 
related to 0000614Closedajosey Behavior of close() as a cancellation point is unspecified 
related to 0000632Closedajosey Side effects of pclose on cancellation are not specified 

-  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
Powered by Mantis Bugtracker