diff --git a/deps/uv/msvs/libuv-test.vcxproj b/deps/uv/msvs/libuv-test.vcxproj index da93bd99a8..cb64f194e3 100644 --- a/deps/uv/msvs/libuv-test.vcxproj +++ b/deps/uv/msvs/libuv-test.vcxproj @@ -160,6 +160,7 @@ + diff --git a/deps/uv/src/uv-unix.c b/deps/uv/src/uv-unix.c index ff1f80d824..52ce5b2c9b 100644 --- a/deps/uv/src/uv-unix.c +++ b/deps/uv/src/uv-unix.c @@ -88,6 +88,7 @@ static int uv__nonblock(int fd, int set) __attribute__((unused)); static int uv__socket(int domain, int type, int protocol); static int uv__accept(int sockfd, struct sockaddr* saddr, socklen_t len); +static int uv__close(int fd); size_t uv__strlcpy(char* dst, const char* src, size_t size); @@ -329,7 +330,7 @@ int uv__bind(uv_tcp_t* tcp, int domain, struct sockaddr* addr, int addrsize) { if (uv__stream_open((uv_stream_t*)tcp, fd)) { status = -2; - close(fd); + uv__close(fd); goto out; } } @@ -464,7 +465,7 @@ int uv_accept(uv_handle_t* server, uv_stream_t* client) { if (uv__stream_open(streamClient, streamServer->accepted_fd)) { /* TODO handle error */ streamServer->accepted_fd = -1; - close(streamServer->accepted_fd); + uv__close(streamServer->accepted_fd); goto out; } @@ -494,7 +495,7 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { } if (uv__stream_open((uv_stream_t*)tcp, fd)) { - close(fd); + uv__close(fd); return -1; } } @@ -554,11 +555,11 @@ void uv__finish_close(uv_handle_t* handle) { assert(!ev_is_active(&stream->read_watcher)); assert(!ev_is_active(&stream->write_watcher)); - close(stream->fd); + uv__close(stream->fd); stream->fd = -1; if (stream->accepted_fd >= 0) { - close(stream->accepted_fd); + uv__close(stream->accepted_fd); stream->accepted_fd = -1; } break; @@ -678,12 +679,14 @@ static uv_write_t* uv__write(uv_stream_t* stream) { * inside the iov each time we write. So there is no need to offset it. */ - if (iovcnt == 1) { - n = write(stream->fd, iov[0].iov_base, iov[0].iov_len); - } - else { - n = writev(stream->fd, iov, iovcnt); + do { + if (iovcnt == 1) { + n = write(stream->fd, iov[0].iov_base, iov[0].iov_len); + } else { + n = writev(stream->fd, iov, iovcnt); + } } + while (n == -1 && errno == EINTR); if (n < 0) { if (errno != EAGAIN) { @@ -799,7 +802,10 @@ static void uv__read(uv_stream_t* stream) { iov = (struct iovec*) &buf; - nread = read(stream->fd, buf.base, buf.len); + do { + nread = read(stream->fd, buf.base, buf.len); + } + while (nread == -1 && errno == EINTR); if (nread < 0) { /* Error */ @@ -964,7 +970,7 @@ static int uv__connect(uv_connect_t* req, } if (uv__stream_open(stream, sockfd)) { - close(sockfd); + uv__close(sockfd); return -2; } } @@ -987,7 +993,10 @@ static int uv__connect(uv_connect_t* req, stream->connect_req = req; - r = connect(stream->fd, addr, addrlen); + do { + r = connect(stream->fd, addr, addrlen); + } + while (r == -1 && errno == EINTR); stream->delayed_error = 0; @@ -1839,7 +1848,7 @@ out: /* unlink() before close() to avoid races. */ unlink(name); } - close(sockfd); + uv__close(sockfd); free((void*)name); } @@ -1875,6 +1884,7 @@ int uv_pipe_connect(uv_connect_t* req, int saved_errno; int sockfd; int status; + int r; saved_errno = errno; sockfd = -1; @@ -1892,9 +1902,14 @@ int uv_pipe_connect(uv_connect_t* req, /* We don't check for EINPROGRESS. Think about it: the socket * is either there or not. */ - if (connect(sockfd, (struct sockaddr*)&sun, sizeof sun) == -1) { + do { + r = connect(sockfd, (struct sockaddr*)&sun, sizeof sun); + } + while (r == -1 && errno == EINTR); + + if (r == -1) { uv_err_new((uv_handle_t*)handle, errno); - close(sockfd); + uv__close(sockfd); goto out; } @@ -1971,7 +1986,7 @@ static int uv__socket(int domain, int type, int protocol) { } if (uv__nonblock(sockfd, 1) == -1 || uv__cloexec(sockfd, 1) == -1) { - close(sockfd); + uv__close(sockfd); return -1; } @@ -1981,22 +1996,40 @@ static int uv__socket(int domain, int type, int protocol) { static int uv__accept(int sockfd, struct sockaddr* saddr, socklen_t slen) { -#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) - return accept4(sockfd, saddr, &slen, SOCK_NONBLOCK | SOCK_CLOEXEC); -#else int peerfd; - if ((peerfd = accept(sockfd, saddr, &slen)) == -1) { - return -1; + do { +#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) + peerfd = accept4(sockfd, saddr, &slen, SOCK_NONBLOCK | SOCK_CLOEXEC); +#else + if ((peerfd = accept(sockfd, saddr, &slen)) != -1) { + if (uv__cloexec(peerfd, 1) == -1 || uv__nonblock(peerfd, 1) == -1) { + uv__close(peerfd); + return -1; + } + } +#endif } + while (peerfd == -1 && errno == EINTR); - if (uv__cloexec(peerfd, 1) == -1 || uv__nonblock(peerfd, 1) == -1) { - close(peerfd); - return -1; + return peerfd; +} + + +static int uv__close(int fd) { + int status; + + /* + * Retry on EINTR. You may think this is academic but on linux + * and probably other Unices too, close(2) is interruptible. + * Failing to handle EINTR is a common source of fd leaks. + */ + do { + status = close(fd); } + while (status == -1 && errno == EINTR); - return peerfd; -#endif + return status; } diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index ed870efebe..21470f9c91 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -145,7 +145,7 @@ int uv_run() { uv_prepare_invoke(); - uv_poll(LOOP->idle_handles == NULL); + uv_poll(LOOP->idle_handles == NULL && LOOP->refs > 0); uv_check_invoke(); } diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 8b9ecfcca7..de9f1d3add 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -53,6 +53,7 @@ TEST_DECLARE (idle_ref) TEST_DECLARE (async_ref) TEST_DECLARE (prepare_ref) TEST_DECLARE (check_ref) +TEST_DECLARE (unref_in_prepare_cb) TEST_DECLARE (async) TEST_DECLARE (get_currentexe) TEST_DECLARE (hrtime) @@ -121,6 +122,7 @@ TASK_LIST_START TEST_ENTRY (async_ref) TEST_ENTRY (prepare_ref) TEST_ENTRY (check_ref) + TEST_ENTRY (unref_in_prepare_cb) TEST_ENTRY (loop_handles) diff --git a/deps/uv/test/test-loop-handles.c b/deps/uv/test/test-loop-handles.c index 2e858c1d7b..8db6c8c174 100644 --- a/deps/uv/test/test-loop-handles.c +++ b/deps/uv/test/test-loop-handles.c @@ -359,53 +359,3 @@ TEST_IMPL(loop_handles) { return 0; } - - -TEST_IMPL(ref) { - uv_init(); - uv_run(); - return 0; -} - - -TEST_IMPL(idle_ref) { - uv_idle_t h; - uv_init(); - uv_idle_init(&h); - uv_idle_start(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} - - -TEST_IMPL(async_ref) { - uv_async_t h; - uv_init(); - uv_async_init(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} - - -TEST_IMPL(prepare_ref) { - uv_prepare_t h; - uv_init(); - uv_prepare_init(&h); - uv_prepare_start(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} - - -TEST_IMPL(check_ref) { - uv_check_t h; - uv_init(); - uv_check_init(&h); - uv_check_start(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} diff --git a/deps/uv/test/test-ref.c b/deps/uv/test/test-ref.c new file mode 100644 index 0000000000..d61b3a6f50 --- /dev/null +++ b/deps/uv/test/test-ref.c @@ -0,0 +1,91 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + + +TEST_IMPL(ref) { + uv_init(); + uv_run(); + return 0; +} + + +TEST_IMPL(idle_ref) { + uv_idle_t h; + uv_init(); + uv_idle_init(&h); + uv_idle_start(&h, NULL); + uv_unref(); + uv_run(); + return 0; +} + + +TEST_IMPL(async_ref) { + uv_async_t h; + uv_init(); + uv_async_init(&h, NULL); + uv_unref(); + uv_run(); + return 0; +} + + +TEST_IMPL(prepare_ref) { + uv_prepare_t h; + uv_init(); + uv_prepare_init(&h); + uv_prepare_start(&h, NULL); + uv_unref(); + uv_run(); + return 0; +} + + +TEST_IMPL(check_ref) { + uv_check_t h; + uv_init(); + uv_check_init(&h); + uv_check_start(&h, NULL); + uv_unref(); + uv_run(); + return 0; +} + + +static void prepare_cb(uv_prepare_t* handle, int status) { + ASSERT(handle != NULL); + ASSERT(status == 0); + + uv_unref(); +} + + +TEST_IMPL(unref_in_prepare_cb) { + uv_prepare_t h; + uv_init(); + uv_prepare_init(&h); + uv_prepare_start(&h, prepare_cb); + uv_run(); + return 0; +}