From ac799ba0afa9731e281bf23886d1a3f5adec1784 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Tue, 12 Nov 2013 10:57:57 -0800 Subject: [PATCH] uv: Upgrade to v0.10.19 --- deps/uv/AUTHORS | 2 + deps/uv/ChangeLog | 23 ++++- deps/uv/README.md | 4 +- deps/uv/build.mk | 2 + deps/uv/checksparse.sh | 2 + deps/uv/{gyp_uv => gyp_uv.py} | 0 deps/uv/src/unix/core.c | 22 +++- deps/uv/src/unix/darwin-proctitle.c | 42 +++++++- deps/uv/src/unix/darwin.c | 19 ++++ deps/uv/src/unix/fsevents.c | 4 +- deps/uv/src/unix/internal.h | 1 + deps/uv/src/unix/kqueue.c | 13 ++- deps/uv/src/unix/linux-core.c | 59 ++++++++++- deps/uv/src/unix/sunos.c | 28 ++++++ deps/uv/src/unix/udp.c | 48 +++++---- deps/uv/src/version.c | 2 +- deps/uv/test/test-list.h | 5 + deps/uv/test/test-tcp-close-accept.c | 177 +++++++++++++++++++++++++++++++++ deps/uv/test/test-watcher-cross-stop.c | 108 ++++++++++++++++++++ deps/uv/uv.gyp | 2 + deps/uv/vcbuild.bat | 2 +- 21 files changed, 528 insertions(+), 37 deletions(-) rename deps/uv/{gyp_uv => gyp_uv.py} (100%) create mode 100644 deps/uv/test/test-tcp-close-accept.c create mode 100644 deps/uv/test/test-watcher-cross-stop.c diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index 119773a..3263146 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -85,3 +85,5 @@ Miroslav Bajtoš Elliot Saba Wynn Wilkes Andrei Sedoi +Chris Bank +Geert Jansen diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 794764e..e5d1a56 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,4 +1,25 @@ -2013.10.19, Version 0.10.18 (Stable) +2013.11.13, Version 0.10.19 (Stable) + +Changes since version 0.10.18: + +* darwin: avoid calling GetCurrentProcess (Fedor Indutny) + +* unix: update events from pevents between polls (Fedor Indutny) + +* fsevents: support japaneese characters in path (Chris Bank) + +* linux: don't turn on SO_REUSEPORT socket option (Ben Noordhuis) + +* build: fix windows smp build with gyp (Geert Jansen) + +* linux: handle EPOLLHUP without EPOLLIN/EPOLLOUT (Ben Noordhuis) + +* unix: fix reopened fd bug (Fedor Indutny) + +* core: fix fake watcher list and count preservation (Fedor Indutny) + + +2013.10.19, Version 0.10.18 (Stable), 9ec52963b585e822e87bdc5de28d6143aff0d2e5 Changes since version 0.10.17: diff --git a/deps/uv/README.md b/deps/uv/README.md index 54740ca..1e1aa25 100644 --- a/deps/uv/README.md +++ b/deps/uv/README.md @@ -91,12 +91,12 @@ Or: Unix users run - ./gyp_uv -f make + ./gyp_uv.py -f make make -C out Macintosh users run - ./gyp_uv -f xcode + ./gyp_uv.py -f xcode xcodebuild -project uv.xcodeproj -configuration Release -target All Note for UNIX users: compile your project with `-D_LARGEFILE_SOURCE` and diff --git a/deps/uv/build.mk b/deps/uv/build.mk index 6cba169..2f2dabe 100644 --- a/deps/uv/build.mk +++ b/deps/uv/build.mk @@ -114,6 +114,7 @@ TESTS= \ test/test-tcp-bind6-error.o \ test/test-tcp-bind-error.o \ test/test-tcp-close.o \ + test/test-tcp-close-accept.o \ test/test-tcp-close-while-connecting.o \ test/test-tcp-connect6-error.o \ test/test-tcp-connect-error-after-write.o \ @@ -142,6 +143,7 @@ TESTS= \ test/test-udp-send-and-recv.o \ test/test-util.o \ test/test-walk-handles.o \ + test/test-watcher-cross-stop.o \ .PHONY: all bench clean clean-platform distclean test diff --git a/deps/uv/checksparse.sh b/deps/uv/checksparse.sh index 7412c21..43c9441 100755 --- a/deps/uv/checksparse.sh +++ b/deps/uv/checksparse.sh @@ -133,6 +133,7 @@ test/test-stdio-over-pipes.c test/test-tcp-bind-error.c test/test-tcp-bind6-error.c test/test-tcp-close-while-connecting.c +test/test-tcp-close-accept.c test/test-tcp-close.c test/test-tcp-connect-error-after-write.c test/test-tcp-connect-error.c @@ -161,6 +162,7 @@ test/test-udp-options.c test/test-udp-send-and-recv.c test/test-util.c test/test-walk-handles.c +test/test-watcher-cross-stop.c " case `uname -s` in diff --git a/deps/uv/gyp_uv b/deps/uv/gyp_uv.py similarity index 100% rename from deps/uv/gyp_uv rename to deps/uv/gyp_uv.py diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index e8e5f9e..22c48dd 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -595,20 +595,33 @@ static unsigned int next_power_of_two(unsigned int val) { static void maybe_resize(uv_loop_t* loop, unsigned int len) { uv__io_t** watchers; + void* fake_watcher_list; + void* fake_watcher_count; unsigned int nwatchers; unsigned int i; if (len <= loop->nwatchers) return; - nwatchers = next_power_of_two(len); - watchers = realloc(loop->watchers, nwatchers * sizeof(loop->watchers[0])); + /* Preserve fake watcher list and count at the end of the watchers */ + if (loop->watchers != NULL) { + fake_watcher_list = loop->watchers[loop->nwatchers]; + fake_watcher_count = loop->watchers[loop->nwatchers + 1]; + } else { + fake_watcher_list = NULL; + fake_watcher_count = NULL; + } + + nwatchers = next_power_of_two(len + 2) - 2; + watchers = realloc(loop->watchers, + (nwatchers + 2) * sizeof(loop->watchers[0])); if (watchers == NULL) abort(); - for (i = loop->nwatchers; i < nwatchers; i++) watchers[i] = NULL; + watchers[nwatchers] = fake_watcher_list; + watchers[nwatchers + 1] = fake_watcher_count; loop->watchers = watchers; loop->nwatchers = nwatchers; @@ -700,6 +713,9 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { void uv__io_close(uv_loop_t* loop, uv__io_t* w) { uv__io_stop(loop, w, UV__POLLIN | UV__POLLOUT); ngx_queue_remove(&w->pending_queue); + + /* Remove stale events for this file descriptor */ + uv__platform_invalidate_fd(loop, w->fd); } diff --git a/deps/uv/src/unix/darwin-proctitle.c b/deps/uv/src/unix/darwin-proctitle.c index c3171a6..037acff 100644 --- a/deps/uv/src/unix/darwin-proctitle.c +++ b/deps/uv/src/unix/darwin-proctitle.c @@ -36,14 +36,22 @@ int uv__set_process_title(const char* title) { CFStringRef, CFStringRef, CFDictionaryRef*); + typedef CFDictionaryRef (*LSApplicationCheckInType)(int, CFDictionaryRef); + typedef OSStatus (*SetApplicationIsDaemonType)(int); + typedef void (*LSSetApplicationLaunchServicesServerConnectionStatusType)( + uint64_t, void*); CFBundleRef launch_services_bundle; LSGetCurrentApplicationASNType ls_get_current_application_asn; LSSetApplicationInformationItemType ls_set_application_information_item; CFStringRef* display_name_key; - ProcessSerialNumber psn; CFTypeRef asn; CFStringRef display_name; OSStatus err; + CFBundleRef hi_services_bundle; + LSApplicationCheckInType ls_application_check_in; + SetApplicationIsDaemonType set_application_is_daemon; + LSSetApplicationLaunchServicesServerConnectionStatusType + ls_set_application_launch_services_server_connection_status; launch_services_bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices")); @@ -71,8 +79,36 @@ int uv__set_process_title(const char* title) { if (display_name_key == NULL || *display_name_key == NULL) return -1; - /* Force the process manager to initialize. */ - GetCurrentProcess(&psn); + /* Black 10.9 magic, to remove (Not responding) mark in Activity Monitor */ + hi_services_bundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIServices")); + if (hi_services_bundle == NULL) + return -1; + + set_application_is_daemon = CFBundleGetFunctionPointerForName( + hi_services_bundle, + CFSTR("SetApplicationIsDaemon")); + ls_application_check_in = CFBundleGetFunctionPointerForName( + launch_services_bundle, + CFSTR("_LSApplicationCheckIn")); + ls_set_application_launch_services_server_connection_status = + CFBundleGetFunctionPointerForName( + launch_services_bundle, + CFSTR("_LSSetApplicationLaunchServicesServerConnectionStatus")); + if (set_application_is_daemon == NULL || + ls_application_check_in == NULL || + ls_set_application_launch_services_server_connection_status == NULL) { + return -1; + } + + if (set_application_is_daemon(1) != noErr) + return -1; + + ls_set_application_launch_services_server_connection_status(0, NULL); + + /* Check into process manager?! */ + ls_application_check_in(-2, + CFBundleGetInfoDictionary(CFBundleGetMainBundle())); display_name = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); asn = ls_get_current_application_asn(); diff --git a/deps/uv/src/unix/darwin.c b/deps/uv/src/unix/darwin.c index 77e662f..0310254 100644 --- a/deps/uv/src/unix/darwin.c +++ b/deps/uv/src/unix/darwin.c @@ -102,6 +102,25 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct kevent* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct kevent*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events == NULL) + return; + + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].ident == fd) + events[i].ident = -1; +} + + static void uv__cf_loop_runner(void* arg) { uv_loop_t* loop; diff --git a/deps/uv/src/unix/fsevents.c b/deps/uv/src/unix/fsevents.c index b6d2746..d860324 100644 --- a/deps/uv/src/unix/fsevents.c +++ b/deps/uv/src/unix/fsevents.c @@ -223,9 +223,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) { handle->realpath_len = strlen(handle->realpath); /* Initialize paths array */ - path = CFStringCreateWithCString(NULL, - handle->filename, - CFStringGetSystemEncoding()); + path = CFStringCreateWithFileSystemRepresentation(NULL, handle->filename); paths = CFArrayCreate(NULL, (const void**)&path, 1, NULL); latency = 0.15; diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 61cb1ec..35aa894 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -183,6 +183,7 @@ uint64_t uv__hrtime(void); int uv__kqueue_init(uv_loop_t* loop); int uv__platform_loop_init(uv_loop_t* loop, int default_loop); void uv__platform_loop_delete(uv_loop_t* loop); +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd); /* various */ void uv__async_close(uv_async_t* handle); diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c index 378903a..0256459 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -162,11 +162,18 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nevents = 0; + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { ev = events + i; fd = ev->ident; w = loop->watchers[fd]; + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + if (w == NULL) { /* File descriptor that we've stopped watching, disarm it. */ /* TODO batch up */ @@ -191,7 +198,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { revents = 0; if (ev->filter == EVFILT_READ) { - if (w->events & UV__POLLIN) { + if (w->pevents & UV__POLLIN) { revents |= UV__POLLIN; w->rcount = ev->data; } else { @@ -205,7 +212,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } if (ev->filter == EVFILT_WRITE) { - if (w->events & UV__POLLOUT) { + if (w->pevents & UV__POLLOUT) { revents |= UV__POLLOUT; w->wcount = ev->data; } else { @@ -227,6 +234,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->cb(loop, w, revents); nevents++; } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { diff --git a/deps/uv/src/unix/linux-core.c b/deps/uv/src/unix/linux-core.c index e4c34a1..7c34ea1 100644 --- a/deps/uv/src/unix/linux-core.c +++ b/deps/uv/src/unix/linux-core.c @@ -97,6 +97,25 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct uv__epoll_event* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct uv__epoll_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events == NULL) + return; + + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].data == fd) + events[i].data = -1; +} + + void uv__io_poll(uv_loop_t* loop, int timeout) { struct uv__epoll_event events[1024]; struct uv__epoll_event* pe; @@ -189,10 +208,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nevents = 0; + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { pe = events + i; fd = pe->data; + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); @@ -208,9 +234,38 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { continue; } - w->cb(loop, w, pe->events); - nevents++; + /* Give users only events they're interested in. Prevents spurious + * callbacks when previous callback invocation in this loop has stopped + * the current watcher. Also, filters out events that users has not + * requested us to watch. + */ + pe->events &= w->pevents | UV__POLLERR | UV__POLLHUP; + + /* Work around an epoll quirk where it sometimes reports just the + * EPOLLERR or EPOLLHUP event. In order to force the event loop to + * move forward, we merge in the read/write events that the watcher + * is interested in; uv__read() and uv__write() will then deal with + * the error or hangup in the usual fashion. + * + * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user + * reads the available data, calls uv_read_stop(), then sometime later + * calls uv_read_start() again. By then, libuv has forgotten about the + * hangup and the kernel won't report EPOLLIN again because there's + * nothing left to read. If anything, libuv is to blame here. The + * current hack is just a quick bandaid; to properly fix it, libuv + * needs to remember the error/hangup event. We should get that for + * free when we switch over to edge-triggered I/O. + */ + if (pe->events == UV__EPOLLERR || pe->events == UV__EPOLLHUP) + pe->events |= w->pevents & (UV__EPOLLIN | UV__EPOLLOUT); + + if (pe->events != 0) { + w->cb(loop, w, pe->events); + nevents++; + } } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index 3fbb50c..e2a72db 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -87,6 +87,25 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct port_event* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct port_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events == NULL) + return; + + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].portev_object == fd) + events[i].portev_object = -1; +} + + void uv__io_poll(uv_loop_t* loop, int timeout) { struct port_event events[1024]; struct port_event* pe; @@ -173,10 +192,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nevents = 0; + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { pe = events + i; fd = pe->portev_object; + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); @@ -193,6 +219,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (w->pevents != 0 && ngx_queue_empty(&w->watcher_queue)) ngx_queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index 3fb8af9..ea437a3 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -286,6 +286,31 @@ static void uv__udp_sendmsg(uv_loop_t* loop, } +/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional + * refinements for programs that use multicast. + * + * Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that + * are different from the BSDs: it _shares_ the port rather than steal it + * from the current listener. While useful, it's not something we can emulate + * on other platforms so we don't enable it. + */ +static int uv__set_reuse(int fd) { + int yes; + +#if defined(SO_REUSEPORT) && !defined(__linux__) + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) + return -errno; +#else + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) + return -errno; +#endif + + return 0; +} + + static int uv__bind(uv_udp_t* handle, int domain, struct sockaddr* addr, @@ -293,6 +318,7 @@ static int uv__bind(uv_udp_t* handle, unsigned flags) { int saved_errno; int status; + int err; int yes; int fd; @@ -321,28 +347,12 @@ static int uv__bind(uv_udp_t* handle, } fd = handle->io_watcher.fd; - yes = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) { - uv__set_sys_error(handle->loop, errno); + err = uv__set_reuse(fd); + if (err) { + uv__set_sys_error(handle->loop, -err); goto out; } - /* On the BSDs, SO_REUSEADDR lets you reuse an address that's in the TIME_WAIT - * state (i.e. was until recently tied to a socket) while SO_REUSEPORT lets - * multiple processes bind to the same address. Yes, it's something of a - * misnomer but then again, SO_REUSEADDR was already taken. - * - * None of the above applies to Linux: SO_REUSEADDR implies SO_REUSEPORT on - * Linux and hence it does not have SO_REUSEPORT at all. - */ -#ifdef SO_REUSEPORT - yes = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof yes) == -1) { - uv__set_sys_error(handle->loop, errno); - goto out; - } -#endif - if (flags & UV_UDP_IPV6ONLY) { #ifdef IPV6_V6ONLY yes = 1; diff --git a/deps/uv/src/version.c b/deps/uv/src/version.c index e272d5f..8048efd 100644 --- a/deps/uv/src/version.c +++ b/deps/uv/src/version.c @@ -34,7 +34,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 18 +#define UV_VERSION_PATCH 19 #define UV_VERSION_IS_RELEASE 1 diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 7a9b1a2..f1e950e 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -64,6 +64,7 @@ TEST_DECLARE (tcp_connect_error_fault) TEST_DECLARE (tcp_connect_timeout) TEST_DECLARE (tcp_close_while_connecting) TEST_DECLARE (tcp_close) +TEST_DECLARE (tcp_close_accept) TEST_DECLARE (tcp_flags) TEST_DECLARE (tcp_write_to_half_open_connection) TEST_DECLARE (tcp_unexpected_read) @@ -106,6 +107,7 @@ TEST_DECLARE (idle_starvation) TEST_DECLARE (loop_handles) TEST_DECLARE (get_loadavg) TEST_DECLARE (walk_handles) +TEST_DECLARE (watcher_cross_stop) TEST_DECLARE (ref) TEST_DECLARE (idle_ref) TEST_DECLARE (async_ref) @@ -297,6 +299,7 @@ TASK_LIST_START TEST_ENTRY (tcp_connect_timeout) TEST_ENTRY (tcp_close_while_connecting) TEST_ENTRY (tcp_close) + TEST_ENTRY (tcp_close_accept) TEST_ENTRY (tcp_flags) TEST_ENTRY (tcp_write_to_half_open_connection) TEST_ENTRY (tcp_unexpected_read) @@ -385,6 +388,8 @@ TASK_LIST_START TEST_ENTRY (loop_handles) TEST_ENTRY (walk_handles) + TEST_ENTRY (watcher_cross_stop) + TEST_ENTRY (active) TEST_ENTRY (embed) diff --git a/deps/uv/test/test-tcp-close-accept.c b/deps/uv/test/test-tcp-close-accept.c new file mode 100644 index 0000000..f5336a5 --- /dev/null +++ b/deps/uv/test/test-tcp-close-accept.c @@ -0,0 +1,177 @@ +/* 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" + +#include +#include + +static struct sockaddr_in addr; +static uv_tcp_t tcp_server; +static uv_tcp_t tcp_outgoing[2]; +static uv_tcp_t tcp_incoming[ARRAY_SIZE(tcp_outgoing)]; +static uv_connect_t connect_reqs[ARRAY_SIZE(tcp_outgoing)]; +static uv_tcp_t tcp_check; +static uv_connect_t tcp_check_req; +static uv_write_t write_reqs[ARRAY_SIZE(tcp_outgoing)]; +static unsigned int got_connections; +static unsigned int close_cb_called; +static unsigned int write_cb_called; +static unsigned int read_cb_called; + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + +static void write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + write_cb_called++; +} + +static void connect_cb(uv_connect_t* req, int status) { + unsigned int i; + uv_buf_t buf; + uv_stream_t* outgoing; + + if (req == &tcp_check_req) { + ASSERT(status != 0); + + /* Close check and incoming[0], time to finish test */ + uv_close((uv_handle_t*) &tcp_incoming[0], close_cb); + uv_close((uv_handle_t*) &tcp_check, close_cb); + return; + } + + ASSERT(status == 0); + ASSERT(connect_reqs <= req); + ASSERT(req <= connect_reqs + ARRAY_SIZE(connect_reqs)); + i = req - connect_reqs; + + buf = uv_buf_init("x", 1); + outgoing = (uv_stream_t*) &tcp_outgoing[i]; + ASSERT(0 == uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb)); +} + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char buf[1]; + + return uv_buf_init(buf, sizeof(buf)); +} + +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t b) { + uv_loop_t* loop; + unsigned int i; + + /* Only first stream should receive read events */ + ASSERT(stream == (uv_stream_t*) &tcp_incoming[0]); + ASSERT(0 == uv_read_stop(stream)); + ASSERT(1 == nread); + + loop = stream->loop; + read_cb_called++; + + /* Close all active incomings, except current one */ + for (i = 1; i < got_connections; i++) + uv_close((uv_handle_t*) &tcp_incoming[i], close_cb); + + /* Create new fd that should be one of the closed incomings */ + ASSERT(0 == uv_tcp_init(loop, &tcp_check)); + ASSERT(0 == uv_tcp_connect(&tcp_check_req, &tcp_check, addr, connect_cb)); + ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb)); + + /* Close server, so no one will connect to it */ + uv_close((uv_handle_t*) &tcp_server, close_cb); +} + +static void connection_cb(uv_stream_t* server, int status) { + unsigned int i; + uv_tcp_t* incoming; + + ASSERT(server == (uv_stream_t*) &tcp_server); + + /* Ignore tcp_check connection */ + if (got_connections == ARRAY_SIZE(tcp_incoming)) + return; + + /* Accept everyone */ + incoming = &tcp_incoming[got_connections++]; + ASSERT(0 == uv_tcp_init(server->loop, incoming)); + ASSERT(0 == uv_accept(server, (uv_stream_t*) incoming)); + + if (got_connections != ARRAY_SIZE(tcp_incoming)) + return; + + /* Once all clients are accepted - start reading */ + for (i = 0; i < ARRAY_SIZE(tcp_incoming); i++) { + incoming = &tcp_incoming[i]; + ASSERT(0 == uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb)); + } +} + +TEST_IMPL(tcp_close_accept) { + unsigned int i; + uv_loop_t* loop; + uv_tcp_t* client; + + /* + * A little explanation of what goes on below: + * + * We'll create server and connect to it using two clients, each writing one + * byte once connected. + * + * When all clients will be accepted by server - we'll start reading from them + * and, on first client's first byte, will close second client and server. + * After that, we'll immediately initiate new connection to server using + * tcp_check handle (thus, reusing fd from second client). + * + * In this situation uv__io_poll()'s event list should still contain read + * event for second client, and, if not cleaned up properly, `tcp_check` will + * receive stale event of second incoming and invoke `connect_cb` with zero + * status. + */ + + loop = uv_default_loop(); + addr = uv_ip4_addr("0.0.0.0", TEST_PORT); + + ASSERT(0 == uv_tcp_init(loop, &tcp_server)); + ASSERT(0 == uv_tcp_bind(&tcp_server, addr)); + ASSERT(0 == uv_listen((uv_stream_t*) &tcp_server, + ARRAY_SIZE(tcp_outgoing), + connection_cb)); + + for (i = 0; i < ARRAY_SIZE(tcp_outgoing); i++) { + client = tcp_outgoing + i; + + ASSERT(0 == uv_tcp_init(loop, client)); + ASSERT(0 == uv_tcp_connect(&connect_reqs[i], client, addr, connect_cb)); + } + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(ARRAY_SIZE(tcp_outgoing) == got_connections); + ASSERT((ARRAY_SIZE(tcp_outgoing) + 2) == close_cb_called); + ASSERT(ARRAY_SIZE(tcp_outgoing) == write_cb_called); + ASSERT(1 == read_cb_called); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-watcher-cross-stop.c b/deps/uv/test/test-watcher-cross-stop.c new file mode 100644 index 0000000..5b53320 --- /dev/null +++ b/deps/uv/test/test-watcher-cross-stop.c @@ -0,0 +1,108 @@ +/* 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" + +#include +#include + +#if !defined(_WIN32) +#include +#include /* setrlimit() */ +#endif + +/* NOTE: Number should be big enough to trigger this problem */ +static uv_udp_t sockets[2500]; +static uv_udp_send_t reqs[ARRAY_SIZE(sockets)]; +static char buf[1]; +static unsigned int recv_cb_called; +static unsigned int send_cb_called; +static unsigned int close_cb_called; + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + return uv_buf_init(buf, sizeof(buf)); +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + recv_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + send_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +TEST_IMPL(watcher_cross_stop) { + uv_loop_t* loop = uv_default_loop(); + unsigned int i; + struct sockaddr_in addr; + uv_buf_t buf; + char big_string[1024]; + +#if !defined(_WIN32) + { + struct rlimit lim; + lim.rlim_cur = ARRAY_SIZE(sockets) + 32; + lim.rlim_max = ARRAY_SIZE(sockets) + 32; + if (setrlimit(RLIMIT_NOFILE, &lim)) + RETURN_SKIP("File descriptor limit too low."); + } +#endif + + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + memset(big_string, 'A', sizeof(big_string)); + buf = uv_buf_init(big_string, sizeof(big_string)); + + for (i = 0; i < ARRAY_SIZE(sockets); i++) { + ASSERT(0 == uv_udp_init(loop, &sockets[i])); + ASSERT(0 == uv_udp_bind(&sockets[i], addr, 0)); + ASSERT(0 == uv_udp_recv_start(&sockets[i], alloc_cb, recv_cb)); + ASSERT(0 == uv_udp_send(&reqs[i], &sockets[i], &buf, 1, addr, send_cb)); + } + + while (recv_cb_called == 0) + uv_run(loop, UV_RUN_ONCE); + + for (i = 0; i < ARRAY_SIZE(sockets); i++) + uv_close((uv_handle_t*) &sockets[i], close_cb); + + ASSERT(0 < recv_cb_called && recv_cb_called <= ARRAY_SIZE(sockets)); + ASSERT(ARRAY_SIZE(sockets) == send_cb_called); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(ARRAY_SIZE(sockets) == close_cb_called); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index f61ebb5..96a5760 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -310,6 +310,7 @@ 'test/test-loop-handles.c', 'test/test-loop-stop.c', 'test/test-walk-handles.c', + 'test/test-watcher-cross-stop.c', 'test/test-multiple-listen.c', 'test/test-osx-select.c', 'test/test-pass-always.c', @@ -334,6 +335,7 @@ 'test/test-tcp-bind-error.c', 'test/test-tcp-bind6-error.c', 'test/test-tcp-close.c', + 'test/test-tcp-close-accept.c', 'test/test-tcp-close-while-connecting.c', 'test/test-tcp-connect-error-after-write.c', 'test/test-tcp-shutdown-after-write.c', diff --git a/deps/uv/vcbuild.bat b/deps/uv/vcbuild.bat index 1b2f865..0b7ea48 100644 --- a/deps/uv/vcbuild.bat +++ b/deps/uv/vcbuild.bat @@ -91,7 +91,7 @@ exit /b 1 :have_gyp if not defined PYTHON set PYTHON="python" -%PYTHON% gyp_uv -Dtarget_arch=%target_arch% -Dlibrary=%library% +%PYTHON% gyp_uv.py -Dtarget_arch=%target_arch% -Dlibrary=%library% if errorlevel 1 goto create-msvs-files-failed if not exist uv.sln goto create-msvs-files-failed echo Project files generated. -- 2.7.4