12

Some system calls can be restarted transparently by the Kernel if the SA_RESTART flag is used when installing the signal handler, according to man signal(7):

If a blocked call to one of the following interfaces is interrupted by a signal handler, then the call will be automatically restarted after the signal handler returns if the SA_RESTART flag was used; otherwise the call will fail with the error EINTR:

Then it mentions some system calls that can (and can not) be restarted, but does not mention close() in either places, how would I know if close(), or any other function, is restartable or not ? does POSIX specify it or is it a Linux-specific behaviour ? where can I find more info ?

iabdalkader
  • 17,009
  • 4
  • 47
  • 74

2 Answers2

14

close is a rather special case. Not only is it not restartable on Linux; when close returns with EINTR on Linux, it has actually already succeeded, and making another call to close will fail with EBADF in single-threaded processes and cause extremely dangerous file-descriptor races in multi-threaded processes.

As of the published POSIX 2008, this behavior is permitted:

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.

This issue was raised with the Austin Group (as Issue #529) and it was resolved to revise the specification such that returning with EINTR means the file descriptor is still open; this is contrary to the current Linux behavior. If the file descriptor has already been closed at the time the signal is handled, the close function is now required to return with EINPROGRESS instead of EINTR. This can be fixed in userspace on Linux, and there is an open glibc bug report, #14627 for it, but as of this writing it has not received any response.

This issue also has serious implications for POSIX thread cancellation, the side effects of which are specified in terms of the side effects upon returning with EINTR. There is a related issue on the Austin Group tracker, Issue #614.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 3
    Note: this behaviour is not specific to linux. `close()` must not be retried on linux, Solaris, Darwin (MacOS), and BSD. HP-UX is the only(?) system to require that close be retried on EINTR. There's zero chance that any platform is going to change its behaviour. – Nicholas Wilson Mar 14 '13 at 15:50
  • 2
    Well hopefully there's a nonzero chance that they'll stop returning `EINTR` and start returning either `EINPROGRESS` or success; the fixes in POSIX will allow either. I think success may be preferable so as not to break existing applications that consider any value other than `EINTR` a fatal error. – R.. GitHub STOP HELPING ICE Jul 02 '13 at 02:37
  • Many implementations don't adhere to POSIX when close() is interrupted by a signal: Linux close() returns 0 and doesn't set errno. FreeBSD close() returns -1 and sets errno to EBADF (which may happen to coincide with some other error code). I am not aware of any UNIX where interrupting close() results is setting errno to EINTR. – Heiko Bloch Oct 28 '16 at 08:02
  • 1
    @HeikoBloch: That's not entirely accurate; see my question http://stackoverflow.com/questions/6811039/trying-to-make-close-sleep-on-linux that addresses the counter-intuitive way `EINTR` arises on Linux `close`. If FreeBSD is behaving the way you expect, the syscall is probably getting restarted after the fd is closed, and it's probably a serious bug in multithreaded applications (double-close race). – R.. GitHub STOP HELPING ICE Oct 28 '16 at 22:12
  • Reviewing this 4 years later and passing it on to FreeBSD folks, indeed there were actual bugs here! See https://reviews.freebsd.org/D27310 – R.. GitHub STOP HELPING ICE Nov 22 '20 at 20:02
10

As per POSIX.1-2008, the SA_RESTART flag applies to all interruptible functions (all function which are documented to fail with EINTR):

SA_RESTART

This flag affects the behavior of interruptible functions; that is, those specified to fail with errno set to [EINTR]. If set, and a function specified as interruptible is interrupted by this signal, the function shall restart and shall not fail with [EINTR] unless otherwise specified. If an interruptible function which uses a timeout is restarted, the duration of the timeout following the restart is set to an unspecified value that does not exceed the original timeout value. If the flag is not set, interruptible functions interrupted by this signal shall fail with errno set to [EINTR].

That is, the list of functions which are not restarted is Linux-specific (and probably counts as a bug).

Maxim Dounin
  • 6,365
  • 1
  • 26
  • 27
  • 4
    Note the wording "unless otherwise specified". That's what is the case, therefore it's rather "implementation detail" than "bug" (which does not make it less annoying, especially since the docs are, as often with Linux, rather vague). – Damon Nov 13 '12 at 09:08