darwin: fix thread cancellation fd leak

The close() system call tests for thread cancellation first. That has
the unfortunate side effect of aborting the sytem call with EINTR
without actually closing the file descriptor when the thread is in
the "cancel" state. Work around that by calling close$NOCANCEL().

This might well qualify as an academic bug because approximately no one
uses thread cancellation but let's aim for correctness anyway.

PR-URL: https://github.com/libuv/libuv/pull/2291
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Ben Noordhuis 2019-05-04 13:46:14 +02:00 committed by cjihrig
parent c4e9657d59
commit 9063c27f92
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
3 changed files with 20 additions and 2 deletions

View File

@ -510,6 +510,23 @@ skip:
}
/* close() on macos has the "interesting" quirk that it fails with EINTR
* without closing the file descriptor when a thread is in the cancel state.
* That's why libuv calls close$NOCANCEL() instead.
*/
int uv__close_nocancel(int fd) {
#if defined(__APPLE__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
extern int close$NOCANCEL(int);
return close$NOCANCEL(fd);
#pragma GCC diagnostic pop
#else
return close(fd);
#endif
}
int uv__close_nocheckstdio(int fd) {
int saved_errno;
int rc;
@ -517,7 +534,7 @@ int uv__close_nocheckstdio(int fd) {
assert(fd > -1); /* Catch uninitialized io_watcher.fd bugs. */
saved_errno = errno;
rc = close(fd);
rc = uv__close_nocancel(fd);
if (rc == -1) {
rc = UV__ERR(errno);
if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS))

View File

@ -145,7 +145,7 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
static int uv__fs_close(int fd) {
int rc;
rc = close(fd);
rc = uv__close_nocancel(fd);
if (rc == -1)
if (errno == EINTR || errno == EINPROGRESS)
rc = 0; /* The close is in progress, not an error. */

View File

@ -183,6 +183,7 @@ int uv__nonblock_ioctl(int fd, int set);
int uv__nonblock_fcntl(int fd, int set);
int uv__close(int fd); /* preserves errno */
int uv__close_nocheckstdio(int fd);
int uv__close_nocancel(int fd);
int uv__socket(int domain, int type, int protocol);
ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
void uv__make_close_pending(uv_handle_t* handle);