Skip to content

Commit e29357e

Browse files
committed
make EINTR/EINPROGRESS real errors
They should never happen on Linux, but if they do then this implies that whatever guarantees would normally have been obtained by a successful close have gone out the window, which is a genuine error. (This mostly matters on network filesystems)
1 parent c6d50b0 commit e29357e

File tree

1 file changed

+31
-17
lines changed

1 file changed

+31
-17
lines changed

src/abstract_socket.cc

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -121,29 +121,43 @@ NAN_METHOD(Close) {
121121
assert(info.Length() == 1);
122122
fd = info[0]->Int32Value();
123123

124-
// Suppress EINTR and EINPROGRESS. EINTR means that the close() system call
125-
// was interrupted by a signal. According to POSIX, the file descriptor is
126-
// in an undefined state afterwards. It's not safe to try closing it again
127-
// because it may have been closed, despite the signal. If we call close()
128-
// again, then it would either:
124+
// POSIX 2008 states that it unspecified what the state of a file descriptor
125+
// is if close() is interrupted by a signal and fails with EINTR. This is a
126+
// problem for multi-threaded programs since if the fd was actually closed
127+
// it may already be reused by another thread hence it is unsafe to attempt
128+
// to close it again. In 2012, POSIX approved a clarification that aimed
129+
// to deal with this mess: http://austingroupbugs.net/view.php?id=529#c1200
129130
//
130-
// a) fail with EBADF, or
131+
// The short summary is that if the fd is never valid after close(), as is
132+
// the case in Linux, then <unistd.h> should add:
131133
//
132-
// b) close the wrong file descriptor if another thread or a signal handler
133-
// has reused it in the mean time.
134+
// #define POSIX_CLOSE_RESTART 0
134135
//
135-
// Neither is what we want but scenario B is particularly bad. Not retrying
136-
// the close() could, in theory, lead to file descriptor leaks but, in
137-
// practice, operating systems do the right thing and close the file
138-
// descriptor, regardless of whether the operation was interrupted by
139-
// a signal.
136+
// and the new posix_close() should be implemented as something like:
140137
//
141-
// EINPROGRESS is benign. It means the close operation was interrupted but
142-
// that the file descriptor has been closed or is being closed in the
143-
// background. It's informative, not an error.
138+
// int posix_close(int fd, int flags) {
139+
// int r = close(fd);
140+
// if (r < 0 && errno == EINTR)
141+
// return 0 or set errno to EINPROGRESS;
142+
// return r;
143+
// }
144+
//
145+
// In contrast, on systems where EINTR means the close() didn't happen (like
146+
// HP-UX), POSIX_CLOSE_RESTART should be non-zero and if passed as flag to
147+
// posix_close() it should automatically retry close() on EINTR.
148+
//
149+
// Of course this is nice and all, but apparently adding one constant and
150+
// a trivial wrapper was way too much effort for the glibc project:
151+
// https://sourceware.org/bugzilla/show_bug.cgi?id=16302
152+
//
153+
// But since we actually only care about Linux, we don't have to worry about
154+
// all this anyway. close() always means the fd is gone, even if an error
155+
// occurred. This elevates EINTR to the status of real error, since it
156+
// implies behaviour associated with close (e.g. flush) was aborted and can
157+
// not be retried since the fd is gone.
158+
144159
err = 0;
145160
if (close(fd))
146-
if (errno != EINTR && errno != EINPROGRESS)
147161
err = -errno;
148162

149163
info.GetReturnValue().Set(err);

0 commit comments

Comments
 (0)