@@ -121,29 +121,43 @@ NAN_METHOD(Close) {
121
121
assert (info.Length () == 1 );
122
122
fd = info[0 ]->Int32Value ();
123
123
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
129
130
//
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:
131
133
//
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
134
135
//
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:
140
137
//
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
+
144
159
err = 0 ;
145
160
if (close (fd))
146
- if (errno != EINTR && errno != EINPROGRESS)
147
161
err = -errno;
148
162
149
163
info.GetReturnValue ().Set (err);
0 commit comments