uv: Upgrade to v0.10.19
authorTimothy J Fontaine <tjfontaine@gmail.com>
Tue, 12 Nov 2013 18:57:57 +0000 (10:57 -0800)
committerTimothy J Fontaine <tjfontaine@gmail.com>
Tue, 12 Nov 2013 18:57:57 +0000 (10:57 -0800)
21 files changed:
deps/uv/AUTHORS
deps/uv/ChangeLog
deps/uv/README.md
deps/uv/build.mk
deps/uv/checksparse.sh
deps/uv/gyp_uv.py [moved from deps/uv/gyp_uv with 100% similarity]
deps/uv/src/unix/core.c
deps/uv/src/unix/darwin-proctitle.c
deps/uv/src/unix/darwin.c
deps/uv/src/unix/fsevents.c
deps/uv/src/unix/internal.h
deps/uv/src/unix/kqueue.c
deps/uv/src/unix/linux-core.c
deps/uv/src/unix/sunos.c
deps/uv/src/unix/udp.c
deps/uv/src/version.c
deps/uv/test/test-list.h
deps/uv/test/test-tcp-close-accept.c [new file with mode: 0644]
deps/uv/test/test-watcher-cross-stop.c [new file with mode: 0644]
deps/uv/uv.gyp
deps/uv/vcbuild.bat

index 119773a..3263146 100644 (file)
@@ -85,3 +85,5 @@ Miroslav Bajtoš <miro.bajtos@gmail.com>
 Elliot Saba <staticfloat@gmail.com>
 Wynn Wilkes <wynnw@movenetworks.com>
 Andrei Sedoi <bsnote@gmail.com>
+Chris Bank <cbank@adobe.com>
+Geert Jansen <geertj@gmail.com>
index 794764e..e5d1a56 100644 (file)
@@ -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:
 
index 54740ca..1e1aa25 100644 (file)
@@ -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
index 6cba169..2f2dabe 100644 (file)
@@ -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
 
index 7412c21..43c9441 100755 (executable)
@@ -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
similarity index 100%
rename from deps/uv/gyp_uv
rename to deps/uv/gyp_uv.py
index e8e5f9e..22c48dd 100644 (file)
@@ -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);
 }
 
 
index c3171a6..037acff 100644 (file)
@@ -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();
index 77e662f..0310254 100644 (file)
@@ -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;
 
index b6d2746..d860324 100644 (file)
@@ -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;
index 61cb1ec..35aa894 100644 (file)
@@ -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);
index 378903a..0256459 100644 (file)
@@ -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) {
index e4c34a1..7c34ea1 100644 (file)
@@ -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) {
index 3fbb50c..e2a72db 100644 (file)
@@ -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) {
index 3fb8af9..ea437a3 100644 (file)
@@ -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;
index e272d5f..8048efd 100644 (file)
@@ -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
 
 
index 7a9b1a2..f1e950e 100644 (file)
@@ -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 (file)
index 0000000..f5336a5
--- /dev/null
@@ -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 <stdio.h>
+#include <string.h>
+
+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 (file)
index 0000000..5b53320
--- /dev/null
@@ -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 <string.h>
+#include <errno.h>
+
+#if !defined(_WIN32)
+#include <sys/time.h>
+#include <sys/resource.h>  /* 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;
+}
index f61ebb5..96a5760 100644 (file)
         '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',
         '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',
index 1b2f865..0b7ea48 100644 (file)
@@ -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.