uv: upgrade to 87dbffbd
authorBert Belder <bertbelder@gmail.com>
Fri, 1 Jun 2012 16:07:22 +0000 (18:07 +0200)
committerFedor Indutny <fedor.indutny@gmail.com>
Fri, 1 Jun 2012 16:19:42 +0000 (20:19 +0400)
36 files changed:
deps/uv/include/uv-private/ev.h
deps/uv/include/uv-private/uv-unix.h
deps/uv/include/uv-private/uv-win.h
deps/uv/include/uv.h
deps/uv/src/unix/core.c
deps/uv/src/unix/ev/ev.c
deps/uv/src/unix/internal.h
deps/uv/src/unix/loop.c
deps/uv/src/unix/pipe.c
deps/uv/src/unix/process.c
deps/uv/src/unix/stream.c
deps/uv/src/unix/timer.c
deps/uv/src/unix/uv-eio.c
deps/uv/src/uv-common.c
deps/uv/src/uv-common.h
deps/uv/src/win/async.c
deps/uv/src/win/core.c
deps/uv/src/win/fs-event.c
deps/uv/src/win/handle.c
deps/uv/src/win/internal.h
deps/uv/src/win/loop-watcher.c
deps/uv/src/win/pipe.c
deps/uv/src/win/poll.c
deps/uv/src/win/process.c
deps/uv/src/win/tcp.c
deps/uv/src/win/timer.c
deps/uv/src/win/tty.c
deps/uv/src/win/udp.c
deps/uv/test/benchmark-list.h
deps/uv/test/benchmark-loop-count.c
deps/uv/test/run-tests.c
deps/uv/test/test-list.h
deps/uv/test/test-loop-handles.c
deps/uv/test/test-spawn.c
deps/uv/test/test-walk-handles.c [new file with mode: 0644]
deps/uv/uv.gyp

index 11e81cd..7709bc3 100644 (file)
@@ -623,7 +623,7 @@ enum {
 };
 
 #if EV_PROTOTYPES
-void ev_run (EV_P_ int flags EV_CPP (= 0));
+void ev_run (EV_P_ ev_tstamp waittime);
 void ev_break (EV_P_ int how EV_CPP (= EVBREAK_ONE)); /* break out of the loop */
 
 /*
index 27ed333..ae9f5fb 100644 (file)
@@ -98,18 +98,21 @@ struct uv__io_s {
 # define UV_LOOP_PRIVATE_PLATFORM_FIELDS
 #endif
 
-#define UV_LOOP_PRIVATE_FIELDS \
-  /* Poll result queue */ \
-  eio_channel uv_eio_channel; \
-  struct ev_loop* ev; \
-  /* Various thing for libeio. */ \
-  uv_async_t uv_eio_want_poll_notifier; \
-  uv_async_t uv_eio_done_poll_notifier; \
-  uv_idle_t uv_eio_poller; \
-  uv_handle_t* pending_handles; \
-  ngx_queue_t prepare_handles; \
-  ngx_queue_t check_handles; \
-  ngx_queue_t idle_handles; \
+#define UV_LOOP_PRIVATE_FIELDS                                                \
+  /* Poll result queue */                                                     \
+  eio_channel uv_eio_channel;                                                 \
+  struct ev_loop* ev;                                                         \
+  /* Various thing for libeio. */                                             \
+  uv_async_t uv_eio_want_poll_notifier;                                       \
+  uv_async_t uv_eio_done_poll_notifier;                                       \
+  uv_idle_t uv_eio_poller;                                                    \
+  uv_handle_t* closing_handles;                                               \
+  ngx_queue_t prepare_handles;                                                \
+  ngx_queue_t check_handles;                                                  \
+  ngx_queue_t idle_handles;                                                   \
+  /* RB_HEAD(uv__timers, uv_timer_s) */                                       \
+  struct uv__timers { struct uv_timer_s* rbh_root; } timer_handles;           \
+  uint64_t time;                                                              \
   UV_LOOP_PRIVATE_PLATFORM_FIELDS
 
 #define UV_REQ_BUFSML_SIZE (4)
@@ -144,7 +147,7 @@ struct uv__io_s {
 /* TODO: union or classes please! */
 #define UV_HANDLE_PRIVATE_FIELDS \
   int flags; \
-  uv_handle_t* next_pending; \
+  uv_handle_t* next_closing; \
 
 
 #define UV_STREAM_PRIVATE_FIELDS \
@@ -186,7 +189,7 @@ struct uv__io_s {
   uv__io_t io_watcher;
 
 
-/* UV_PREPARE */ \
+/* UV_PREPARE */
 #define UV_PREPARE_PRIVATE_FIELDS \
   uv_prepare_cb prepare_cb; \
   ngx_queue_t queue;
@@ -211,9 +214,17 @@ struct uv__io_s {
 
 
 /* UV_TIMER */
-#define UV_TIMER_PRIVATE_FIELDS \
-  ev_timer timer_watcher; \
-  uv_timer_cb timer_cb;
+#define UV_TIMER_PRIVATE_FIELDS                                               \
+  /* RB_ENTRY(uv_timer_s) node; */                                            \
+  struct {                                                                    \
+    struct uv_timer_s* rbe_left;                                              \
+    struct uv_timer_s* rbe_right;                                             \
+    struct uv_timer_s* rbe_parent;                                            \
+    int rbe_color;                                                            \
+  } tree_entry;                                                               \
+  uv_timer_cb timer_cb;                                                       \
+  uint64_t timeout;                                                           \
+  uint64_t repeat;
 
 #define UV_GETADDRINFO_PRIVATE_FIELDS \
   uv_getaddrinfo_cb cb; \
index ad3e965..3b091b9 100644 (file)
@@ -454,7 +454,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
   struct uv_process_close_s {             \
     UV_REQ_FIELDS                         \
   } close_req;                            \
-  HANDLE child_stdio[3];                  \
+  void* child_stdio_buffer;               \
   int exit_signal;                        \
   DWORD spawn_errno;                      \
   HANDLE wait_handle;                     \
index f9ffbaf..2ddcfa7 100644 (file)
@@ -301,6 +301,7 @@ typedef void (*uv_exit_cb)(uv_process_t*, int exit_status, int term_signal);
 typedef void (*uv_fs_cb)(uv_fs_t* req);
 typedef void (*uv_work_cb)(uv_work_t* req);
 typedef void (*uv_after_work_cb)(uv_work_t* req);
+typedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg);
 
 /*
 * This will be called repeatedly after the uv_fs_event_t is initialized.
@@ -382,6 +383,7 @@ struct uv_shutdown_s {
   /* read-only */                                                             \
   uv_handle_type type;                                                        \
   /* private */                                                               \
+  ngx_queue_t handle_queue;                                                   \
   UV_HANDLE_PRIVATE_FIELDS                                                    \
 
 /* The abstract base class of all handles.  */
@@ -408,6 +410,12 @@ UV_EXTERN size_t uv_req_size(uv_req_type type);
 UV_EXTERN int uv_is_active(const uv_handle_t* handle);
 
 /*
+ * Walk the list of open handles.
+ */
+UV_EXTERN void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg);
+
+
+/*
  * Request handle to be closed. close_cb will be called asynchronously after
  * this call. This MUST be called on each handle before memory is released.
  *
@@ -1157,15 +1165,17 @@ UV_EXTERN void uv_freeaddrinfo(struct addrinfo* ai);
 
 /* uv_spawn() options */
 typedef enum {
-  UV_IGNORE        = 0x00,
-  UV_CREATE_PIPE   = 0x01,
-  /*
-   * UV_READABLE_PIPE and UV_WRITABLE_PIPE flags are set from
-   * the child process perspective.
+  UV_IGNORE         = 0x00,
+  UV_CREATE_PIPE    = 0x01,
+  UV_INHERIT_FD     = 0x02,
+  UV_INHERIT_STREAM = 0x04,
+
+  /* When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE
+   * determine the direction of flow, from the child process' perspective. Both
+   * flags may be specified to create a duplex data stream.
    */
-  UV_READABLE_PIPE = 0x02,
-  UV_WRITABLE_PIPE = 0x04,
-  UV_RAW_FD        = 0x08
+  UV_READABLE_PIPE  = 0x10,
+  UV_WRITABLE_PIPE  = 0x20
 } uv_stdio_flags;
 
 typedef struct uv_stdio_container_s {
@@ -1211,10 +1221,16 @@ typedef struct uv_process_options_s {
   uv_gid_t gid;
 
   /*
-   * A container of stdio streams (stdin/stdout/stderr)
+   * The `stdio` field points to an array of uv_stdio_container_t structs that
+   * describe the file descriptors that will be made available to the child
+   * process. The convention is that stdio[0] points to stdin, fd 1 is used for
+   * stdout, and fd 2 is stderr.
+   *
+   * Note that on windows file descriptors greater than 2 are available to the
+   * child process only if the child processes uses the MSVCRT runtime.
    */
-  uv_stdio_container_t* stdio;
   int stdio_count;
+  uv_stdio_container_t* stdio;
 } uv_process_options_t;
 
 /*
@@ -1238,7 +1254,15 @@ enum uv_process_flags {
    * converting the argument list into a command line string. This option is
    * only meaningful on Windows systems. On unix it is silently ignored.
    */
-  UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2)
+  UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2),
+  /*
+   * Spawn the child process in a detached state - this will make it a process
+   * group leader, and will effectively enable the child to keep running after
+   * the parent exits.  Note that the child process will still keep the
+   * parent's event loop alive unless the parent process calls uv_unref() on
+   * the child's process handle.
+   */
+  UV_PROCESS_DETACHED = (1 << 3)
 };
 
 /*
@@ -1668,6 +1692,7 @@ struct uv_loop_s {
   uv_err_t last_err;
   /* Loop reference counting */
   unsigned int active_handles;
+  ngx_queue_t handle_queue;
   ngx_queue_t active_reqs;
   /* User data - use this for whatever. */
   void* data;
index b3f2321..1802d2f 100644 (file)
@@ -59,8 +59,6 @@
 static uv_loop_t default_loop_struct;
 static uv_loop_t* default_loop_ptr;
 
-static void uv__finish_close(uv_handle_t* handle);
-
 
 void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
   handle->close_cb = close_cb;
@@ -116,7 +114,72 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
   }
 
   handle->flags |= UV_CLOSING;
-  uv__make_pending(handle);
+
+  handle->next_closing = handle->loop->closing_handles;
+  handle->loop->closing_handles = handle;
+}
+
+
+static void uv__finish_close(uv_handle_t* handle) {
+  assert(!uv__is_active(handle));
+  assert(handle->flags & UV_CLOSING);
+  assert(!(handle->flags & UV_CLOSED));
+  handle->flags |= UV_CLOSED;
+
+  switch (handle->type) {
+    case UV_PREPARE:
+    case UV_CHECK:
+    case UV_IDLE:
+    case UV_ASYNC:
+    case UV_TIMER:
+    case UV_PROCESS:
+      break;
+
+    case UV_NAMED_PIPE:
+    case UV_TCP:
+    case UV_TTY:
+      assert(!uv__io_active(&((uv_stream_t*)handle)->read_watcher));
+      assert(!uv__io_active(&((uv_stream_t*)handle)->write_watcher));
+      assert(((uv_stream_t*)handle)->fd == -1);
+      uv__stream_destroy((uv_stream_t*)handle);
+      break;
+
+    case UV_UDP:
+      uv__udp_finish_close((uv_udp_t*)handle);
+      break;
+
+    case UV_FS_EVENT:
+      break;
+
+    case UV_POLL:
+      break;
+
+    default:
+      assert(0);
+      break;
+  }
+
+  uv__handle_unref(handle);
+  ngx_queue_remove(&handle->handle_queue);
+
+  if (handle->close_cb) {
+    handle->close_cb(handle);
+  }
+}
+
+
+static void uv__run_closing_handles(uv_loop_t* loop) {
+  uv_handle_t* p;
+  uv_handle_t* q;
+
+  p = loop->closing_handles;
+  loop->closing_handles = NULL;
+
+  while (p) {
+    q = p->next_closing;
+    uv__finish_close(p);
+    p = q;
+  }
 }
 
 
@@ -163,67 +226,44 @@ void uv_loop_delete(uv_loop_t* loop) {
 }
 
 
-static void uv__run_pending(uv_loop_t* loop) {
-  uv_handle_t* p;
-  uv_handle_t* q;
-
-  if (!loop->pending_handles)
-    return;
-
-  for (p = loop->pending_handles, loop->pending_handles = NULL; p; p = q) {
-    q = p->next_pending;
-    p->next_pending = NULL;
-    p->flags &= ~UV__PENDING;
-
-    if (p->flags & UV_CLOSING) {
-      uv__finish_close(p);
-      continue;
-    }
-
-    switch (p->type) {
-    case UV_NAMED_PIPE:
-    case UV_TCP:
-    case UV_TTY:
-      uv__stream_pending((uv_stream_t*)p);
-      break;
-    default:
-      abort();
-    }
-  }
-}
-
-
-static void uv__poll(uv_loop_t* loop, int block) {
+static void uv__poll(uv_loop_t* loop, unsigned int timeout) {
   /* bump the loop's refcount, otherwise libev does
    * a zero timeout poll and we end up busy looping
    */
   ev_ref(loop->ev);
-  ev_run(loop->ev, block ? EVRUN_ONCE : EVRUN_NOWAIT);
+  ev_run(loop->ev, timeout / 1000.);
   ev_unref(loop->ev);
 }
 
 
-static int uv__should_block(uv_loop_t* loop) {
-  return loop->active_handles && ngx_queue_empty(&loop->idle_handles);
+static unsigned int uv__poll_timeout(uv_loop_t* loop) {
+  if (!uv__has_active_handles(loop))
+    return 0;
+
+  if (!ngx_queue_empty(&loop->idle_handles))
+    return 0;
+
+  return uv__next_timeout(loop);
 }
 
 
 static int uv__run(uv_loop_t* loop) {
+  uv_update_time(loop);
+  uv__run_timers(loop);
   uv__run_idle(loop);
-  uv__run_pending(loop);
 
   if (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) {
     uv__run_prepare(loop);
     /* Need to poll even if there are no active handles left, otherwise
      * uv_work_t reqs won't complete...
      */
-    uv__poll(loop, uv__should_block(loop));
+    uv__poll(loop, uv__poll_timeout(loop));
     uv__run_check(loop);
   }
 
-  return uv__has_pending_handles(loop)
-      || uv__has_active_handles(loop)
-      || uv__has_active_reqs(loop);
+  uv__run_closing_handles(loop);
+
+  return uv__has_active_handles(loop) || uv__has_active_reqs(loop);
 }
 
 
@@ -244,66 +284,19 @@ void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle,
 
   handle->loop = loop;
   handle->type = type;
-  handle->flags = UV__REF; /* ref the loop when active */
-  handle->next_pending = NULL;
-}
-
-
-void uv__finish_close(uv_handle_t* handle) {
-  assert(!uv__is_active(handle));
-  assert(handle->flags & UV_CLOSING);
-  assert(!(handle->flags & UV_CLOSED));
-  handle->flags |= UV_CLOSED;
-
-  switch (handle->type) {
-    case UV_PREPARE:
-    case UV_CHECK:
-    case UV_IDLE:
-    case UV_ASYNC:
-    case UV_TIMER:
-    case UV_PROCESS:
-      break;
-
-    case UV_NAMED_PIPE:
-    case UV_TCP:
-    case UV_TTY:
-      assert(!uv__io_active(&((uv_stream_t*)handle)->read_watcher));
-      assert(!uv__io_active(&((uv_stream_t*)handle)->write_watcher));
-      assert(((uv_stream_t*)handle)->fd == -1);
-      uv__stream_destroy((uv_stream_t*)handle);
-      break;
-
-    case UV_UDP:
-      uv__udp_finish_close((uv_udp_t*)handle);
-      break;
-
-    case UV_FS_EVENT:
-      break;
-
-    case UV_POLL:
-      break;
-
-    default:
-      assert(0);
-      break;
-  }
-
-
-  if (handle->close_cb) {
-    handle->close_cb(handle);
-  }
-
-  uv__handle_unref(handle);
+  handle->flags = UV__HANDLE_REF; /* ref the loop when active */
+  handle->next_closing = NULL;
+  ngx_queue_insert_tail(&loop->handle_queue, &handle->handle_queue);
 }
 
 
 void uv_update_time(uv_loop_t* loop) {
-  ev_now_update(loop->ev);
+  loop->time = uv_hrtime() / 1000000;
 }
 
 
 int64_t uv_now(uv_loop_t* loop) {
-  return (int64_t)(ev_now(loop->ev) * 1000);
+  return loop->time;
 }
 
 
index a432bfb..a722981 100644 (file)
@@ -2389,7 +2389,7 @@ time_update (EV_P_ ev_tstamp max_block)
 }
 
 void
-ev_run (EV_P_ int flags)
+ev_run (EV_P_ ev_tstamp waittime)
 {
 #if EV_FEATURE_API
   ++loop_depth;
@@ -2426,15 +2426,6 @@ ev_run (EV_P_ int flags)
           }
 #endif
 
-#if EV_PREPARE_ENABLE
-      /* queue prepare watchers (and execute them) */
-      if (expect_false (preparecnt))
-        {
-          queue_events (EV_A_ (W *)prepares, preparecnt, EV_PREPARE);
-          EV_INVOKE_PENDING;
-        }
-#endif
-
       if (expect_false (loop_done))
         break;
 
@@ -2445,90 +2436,16 @@ ev_run (EV_P_ int flags)
       /* update fd-related kernel structures */
       fd_reify (EV_A);
 
-      /* calculate blocking time */
-      {
-        ev_tstamp waittime  = 0.;
-        ev_tstamp sleeptime = 0.;
-
-        /* remember old timestamp for io_blocktime calculation */
-        ev_tstamp prev_mn_now = mn_now;
-
-        /* update time to cancel out callback processing overhead */
-        time_update (EV_A_ 1e100);
-
-        if (expect_true (!(flags & EVRUN_NOWAIT || idleall || !activecnt)))
-          {
-            waittime = MAX_BLOCKTIME;
-
-            if (timercnt)
-              {
-                ev_tstamp to = ANHE_at (timers [HEAP0]) - mn_now + backend_fudge;
-                if (waittime > to) waittime = to;
-              }
-
-#if EV_PERIODIC_ENABLE
-            if (periodiccnt)
-              {
-                ev_tstamp to = ANHE_at (periodics [HEAP0]) - ev_rt_now + backend_fudge;
-                if (waittime > to) waittime = to;
-              }
-#endif
-
-            /* don't let timeouts decrease the waittime below timeout_blocktime */
-            if (expect_false (waittime < timeout_blocktime))
-              waittime = timeout_blocktime;
-
-            /* extra check because io_blocktime is commonly 0 */
-            if (expect_false (io_blocktime))
-              {
-                sleeptime = io_blocktime - (mn_now - prev_mn_now);
-
-                if (sleeptime > waittime - backend_fudge)
-                  sleeptime = waittime - backend_fudge;
-
-                if (expect_true (sleeptime > 0.))
-                  {
-                    ev_sleep (sleeptime);
-                    waittime -= sleeptime;
-                  }
-              }
-          }
-
 #if EV_FEATURE_API
-        ++loop_count;
-#endif
-        assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */
-        backend_poll (EV_A_ waittime);
-        assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */
-
-        /* update ev_rt_now, do magic */
-        time_update (EV_A_ waittime + sleeptime);
-      }
-
-      /* queue pending timers and reschedule them */
-      timers_reify (EV_A); /* relative timers called last */
-#if EV_PERIODIC_ENABLE
-      periodics_reify (EV_A); /* absolute timers called first */
-#endif
-
-#if EV_IDLE_ENABLE
-      /* queue idle watchers unless other events are pending */
-      idle_reify (EV_A);
-#endif
-
-#if EV_CHECK_ENABLE
-      /* queue check watchers, to be executed first */
-      if (expect_false (checkcnt))
-        queue_events (EV_A_ (W *)checks, checkcnt, EV_CHECK);
+      ++loop_count;
 #endif
+      assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */
+      backend_poll (EV_A_ waittime);
+      assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */
 
       EV_INVOKE_PENDING;
     }
-  while (expect_true (
-    activecnt
-    && !loop_done
-    && !(flags & (EVRUN_ONCE | EVRUN_NOWAIT))
-  ));
+  while (0);
 
   if (loop_done == EVBREAK_ONE)
     loop_done = EVBREAK_CANCEL;
index eb5be60..8a28b2a 100644 (file)
@@ -93,23 +93,9 @@ enum {
   UV_STREAM_WRITABLE  = 0x40,   /* The stream is writable */
   UV_STREAM_BLOCKING  = 0x80,   /* Synchronous writes. */
   UV_TCP_NODELAY      = 0x100,  /* Disable Nagle. */
-  UV_TCP_KEEPALIVE    = 0x200,  /* Turn on keep-alive. */
-  UV_TIMER_REPEAT     = 0x100,
-  UV__PENDING         = 0x800
+  UV_TCP_KEEPALIVE    = 0x200   /* Turn on keep-alive. */
 };
 
-inline static int uv__has_pending_handles(const uv_loop_t* loop) {
-  return loop->pending_handles != NULL;
-}
-
-inline static void uv__make_pending(uv_handle_t* h) {
-  if (h->flags & UV__PENDING) return;
-  h->next_pending = h->loop->pending_handles;
-  h->loop->pending_handles = h;
-  h->flags |= UV__PENDING;
-}
-#define uv__make_pending(h) uv__make_pending((uv_handle_t*)(h))
-
 inline static void uv__req_init(uv_loop_t* loop,
                                 uv_req_t* req,
                                 uv_req_type type) {
@@ -164,8 +150,9 @@ int uv__tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay);
 /* pipe */
 int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
 
-/* poll */
-void uv__poll_close(uv_poll_t* handle);
+/* timer */
+void uv__run_timers(uv_loop_t* loop);
+unsigned int uv__next_timeout(uv_loop_t* loop);
 
 /* various */
 void uv__async_close(uv_async_t* handle);
@@ -173,6 +160,7 @@ void uv__check_close(uv_check_t* handle);
 void uv__fs_event_close(uv_fs_event_t* handle);
 void uv__idle_close(uv_idle_t* handle);
 void uv__pipe_close(uv_pipe_t* handle);
+void uv__poll_close(uv_poll_t* handle);
 void uv__prepare_close(uv_prepare_t* handle);
 void uv__process_close(uv_process_t* handle);
 void uv__stream_close(uv_stream_t* handle);
@@ -180,8 +168,6 @@ void uv__timer_close(uv_timer_t* handle);
 void uv__udp_close(uv_udp_t* handle);
 void uv__udp_finish_close(uv_udp_t* handle);
 
-void uv__stream_pending(uv_stream_t* handle);
-
 #define UV__F_IPC        (1 << 0)
 #define UV__F_NONBLOCK   (1 << 1)
 int uv__make_socketpair(int fds[2], int flags);
index 7005765..08985d6 100644 (file)
@@ -37,12 +37,15 @@ int uv__loop_init(uv_loop_t* loop, int default_loop) {
   memset(loop, 0, sizeof(*loop));
 
   RB_INIT(&loop->ares_handles);
+  RB_INIT(&loop->timer_handles);
   ngx_queue_init(&loop->active_reqs);
   ngx_queue_init(&loop->idle_handles);
   ngx_queue_init(&loop->check_handles);
   ngx_queue_init(&loop->prepare_handles);
-  loop->pending_handles = NULL;
+  ngx_queue_init(&loop->handle_queue);
+  loop->closing_handles = NULL;
   loop->channel = NULL;
+  loop->time = uv_hrtime() / 1000000;
   loop->ev = (default_loop ? ev_default_loop : ev_loop_new)(flags);
   ev_set_userdata(loop->ev, loop);
   eio_channel_init(&loop->uv_eio_channel, loop);
index 666ebec..317ac67 100644 (file)
@@ -218,7 +218,7 @@ out:
   ngx_queue_init(&req->queue);
 
   /* Run callback on next tick. */
-  uv__make_pending(handle);
+  uv__io_feed(handle->loop, &handle->write_watcher, UV__IO_WRITE);
 
   /* Mimic the Windows pipe implementation, always
    * return 0 and let the callback handle errors.
index 2efe896..7584d92 100644 (file)
@@ -140,33 +140,39 @@ int uv__make_pipe(int fds[2], int flags) {
  */
 static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2],
                                   int writable) {
-  if (container->flags == UV_IGNORE) {
-    return 0;
-  } else if (container->flags & UV_CREATE_PIPE) {
-    assert(container->data.stream != NULL);
+  int fd = -1;
+  switch (container->flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
+                              UV_INHERIT_STREAM)) {
+    case UV_IGNORE:
+      return 0;
+    case UV_CREATE_PIPE:
+      assert(container->data.stream != NULL);
+
+      if (container->data.stream->type != UV_NAMED_PIPE) {
+        errno = EINVAL;
+        return -1;
+      }
 
-    if (container->data.stream->type != UV_NAMED_PIPE) {
-      errno = EINVAL;
-      return -1;
-    }
+      return uv__make_socketpair(fds, 0);
+    case UV_INHERIT_FD:
+    case UV_INHERIT_STREAM:
+      if (container->flags & UV_INHERIT_FD) {
+        fd = container->data.fd;
+      } else {
+        fd = container->data.stream->fd;
+      }
 
-    return uv__make_socketpair(fds, 0);
-  } else if (container->flags & UV_RAW_FD) {
-    if (container->data.fd == -1) {
-      errno = EINVAL;
-      return -1;
-    }
+      if (fd == -1) {
+        errno = EINVAL;
+        return -1;
+      }
 
-    if (writable) {
-      fds[1] = container->data.fd;
-    } else {
-      fds[0] = container->data.fd;
-    }
+      fds[writable ? 1 : 0] = fd;
 
-    return 0;
-  } else {
-    assert(0 && "Unexpected flags");
-    return -1;
+      return 0;
+    default:
+      assert(0 && "Unexpected flags");
+      return -1;
   }
 }
 
@@ -240,6 +246,7 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
 
   assert(options.file != NULL);
   assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
+                             UV_PROCESS_DETACHED |
                              UV_PROCESS_SETGID |
                              UV_PROCESS_SETUID)));
 
@@ -301,6 +308,9 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
 
   if (pid == 0) {
     /* Child */
+    if (options.flags & UV_PROCESS_DETACHED) {
+      setsid();
+    }
 
     /* Dup fds */
     for (i = 0; i < options.stdio_count; i++) {
index 7ea4487..9091b90 100644 (file)
@@ -718,11 +718,6 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
 }
 
 
-void uv__stream_pending(uv_stream_t* handle) {
-  uv__stream_io(handle->loop, &handle->write_watcher, UV__IO_WRITE);
-}
-
-
 static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, int events) {
   uv_stream_t* stream;
 
@@ -859,7 +854,7 @@ int uv__connect(uv_connect_t* req, uv_stream_t* stream, struct sockaddr* addr,
   uv__io_start(stream->loop, &stream->write_watcher);
 
   if (stream->delayed_error)
-    uv__make_pending(stream);
+    uv__io_feed(stream->loop, &stream->write_watcher, UV__IO_WRITE);
 
   return 0;
 }
index e50a35a..0d81997 100644 (file)
 #include "internal.h"
 #include <assert.h>
 
-
-static int uv__timer_repeating(const uv_timer_t* timer) {
-  return timer->flags & UV_TIMER_REPEAT;
+static int uv__timer_cmp(const uv_timer_t* a, const uv_timer_t* b) {
+  if (a->timeout < b->timeout)
+    return -1;
+  if (a->timeout > b->timeout)
+    return 1;
+  if (a < b)
+    return -1;
+  if (a > b)
+    return 1;
+  return 0;
 }
 
 
-static void uv__timer_cb(EV_P_ ev_timer* w, int revents) {
-  uv_timer_t* timer = container_of(w, uv_timer_t, timer_watcher);
-
-  if (!uv__is_active(timer))
-    return;
-
-  if (!uv__timer_repeating(timer))
-    uv__handle_stop(timer);
-
-  if (timer->timer_cb)
-    timer->timer_cb(timer, 0);
-}
+RB_GENERATE_STATIC(uv__timers, uv_timer_s, tree_entry, uv__timer_cmp)
 
 
-int uv_timer_init(uv_loop_t* loop, uv_timer_t* timer) {
-  uv__handle_init(loop, (uv_handle_t*)timer, UV_TIMER);
+int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
   loop->counters.timer_init++;
 
-  ev_init(&timer->timer_watcher, uv__timer_cb);
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER);
+  handle->timer_cb = NULL;
 
   return 0;
 }
 
 
-int uv_timer_start(uv_timer_t* timer,
+int uv_timer_start(uv_timer_t* handle,
                    uv_timer_cb cb,
                    int64_t timeout,
                    int64_t repeat) {
-  if (uv__is_active(timer))
-    uv_timer_stop(timer);
+  assert(timeout >= 0);
+  assert(repeat >= 0);
 
-  timer->timer_cb = cb;
+  if (uv__is_active(handle))
+    uv_timer_stop(handle);
 
-  if (repeat)
-    timer->flags |= UV_TIMER_REPEAT;
-  else
-    timer->flags &= ~UV_TIMER_REPEAT;
+  handle->timer_cb = cb;
+  handle->timeout = handle->loop->time + timeout;
+  handle->repeat = repeat;
 
-  ev_timer_set(&timer->timer_watcher, timeout / 1000.0, repeat / 1000.0);
-  ev_timer_start(timer->loop->ev, &timer->timer_watcher);
-  uv__handle_start(timer);
+  RB_INSERT(uv__timers, &handle->loop->timer_handles, handle);
+  uv__handle_start(handle);
 
   return 0;
 }
 
 
-int uv_timer_stop(uv_timer_t* timer) {
-  timer->flags &= ~UV_TIMER_REPEAT;
-  ev_timer_stop(timer->loop->ev, &timer->timer_watcher);
-  uv__handle_stop(timer);
+int uv_timer_stop(uv_timer_t* handle) {
+  if (!uv__is_active(handle))
+    return 0;
+
+  RB_REMOVE(uv__timers, &handle->loop->timer_handles, handle);
+  uv__handle_stop(handle);
+
   return 0;
 }
 
 
-int uv_timer_again(uv_timer_t* timer) {
-  if (!uv__is_active(timer)) {
-    uv__set_artificial_error(timer->loop, UV_EINVAL);
-    return -1;
+int uv_timer_again(uv_timer_t* handle) {
+  if (handle->timer_cb == NULL)
+    return uv__set_artificial_error(handle->loop, UV_EINVAL);
+
+  if (handle->repeat) {
+    uv_timer_stop(handle);
+    uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat);
   }
 
-  assert(uv__timer_repeating(timer));
-  ev_timer_again(timer->loop->ev, &timer->timer_watcher);
   return 0;
 }
 
 
-void uv_timer_set_repeat(uv_timer_t* timer, int64_t repeat) {
-  assert(timer->type == UV_TIMER);
-  timer->timer_watcher.repeat = repeat / 1000.0;
+void uv_timer_set_repeat(uv_timer_t* handle, int64_t repeat) {
+  assert(repeat >= 0);
+  handle->repeat = repeat;
+}
+
+
+int64_t uv_timer_get_repeat(uv_timer_t* handle) {
+  return handle->repeat;
+}
+
+
+unsigned int uv__next_timeout(uv_loop_t* loop) {
+  uv_timer_t* handle;
 
-  if (repeat)
-    timer->flags |= UV_TIMER_REPEAT;
-  else
-    timer->flags &= ~UV_TIMER_REPEAT;
+  handle = RB_MIN(uv__timers, &loop->timer_handles);
+
+  if (handle == NULL)
+    return (unsigned int) -1; /* block indefinitely */
+
+  if (handle->timeout <= loop->time)
+    return 0;
+
+  return handle->timeout - loop->time;
 }
 
 
-int64_t uv_timer_get_repeat(uv_timer_t* timer) {
-  assert(timer->type == UV_TIMER);
-  return (int64_t)(1000 * timer->timer_watcher.repeat);
+void uv__run_timers(uv_loop_t* loop) {
+  uv_timer_t* handle;
+
+  while ((handle = RB_MIN(uv__timers, &loop->timer_handles))) {
+    if (handle->timeout > loop->time)
+      break;
+
+    uv_timer_stop(handle);
+    uv_timer_again(handle);
+    handle->timer_cb(handle, 0);
+  }
 }
 
 
index b36eea3..10dc0a6 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "uv.h"
 #include "eio.h"
+#include "internal.h"
 
 #include <assert.h>
 #include <stdio.h>
@@ -87,13 +88,18 @@ void uv_eio_init(uv_loop_t* loop) {
 
   uv_idle_init(loop, &loop->uv_eio_poller);
   uv_idle_start(&loop->uv_eio_poller, uv_eio_do_poll);
+  loop->uv_eio_poller.flags |= UV__HANDLE_INTERNAL;
 
   loop->uv_eio_want_poll_notifier.data = loop;
-  uv_async_init(loop, &loop->uv_eio_want_poll_notifier,
-      uv_eio_want_poll_notifier_cb);
-
-  uv_async_init(loop, &loop->uv_eio_done_poll_notifier,
-      uv_eio_done_poll_notifier_cb);
+  uv_async_init(loop,
+                &loop->uv_eio_want_poll_notifier,
+                uv_eio_want_poll_notifier_cb);
+  loop->uv_eio_want_poll_notifier.flags |= UV__HANDLE_INTERNAL;
+
+  uv_async_init(loop,
+                &loop->uv_eio_done_poll_notifier,
+                uv_eio_done_poll_notifier_cb);
+  loop->uv_eio_done_poll_notifier.flags |= UV__HANDLE_INTERNAL;
 
   uv_once(&uv__eio_init_once_guard, uv__eio_init);
 }
index e81f7a3..761133a 100644 (file)
@@ -317,6 +317,18 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
 }
 
 
+void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) {
+  ngx_queue_t* q;
+  uv_handle_t* h;
+
+  ngx_queue_foreach(q, &loop->handle_queue) {
+    h = ngx_queue_data(q, uv_handle_t, handle_queue);
+    if (h->flags & UV__HANDLE_INTERNAL) continue;
+    walk_cb(h, arg);
+  }
+}
+
+
 void uv_ref(uv_handle_t* handle) {
   uv__handle_ref(handle);
 }
index 6e484ef..4a721ef 100644 (file)
 
 #ifndef _WIN32
 enum {
-  UV__ACTIVE       = 0x4000,
-  UV__REF          = 0x8000
+  UV__HANDLE_INTERNAL = 0x8000,
+  UV__HANDLE_ACTIVE   = 0x4000,
+  UV__HANDLE_REF      = 0x2000
 };
 #else
-# define UV__REF     0x00000020
-# define UV__ACTIVE  0x00000040
+# define UV__HANDLE_INTERNAL  0x80
+# define UV__HANDLE_ACTIVE    0x40
+# define UV__HANDLE_REF       0x20
 #endif
 
 extern const uv_err_t uv_ok_;
@@ -117,35 +119,35 @@ UNUSED static void uv__active_handle_rm(uv_handle_t* h) {
 #define uv__req_unregister(loop, req) uv__req_unregister((loop), (uv_req_t*)(req))
 
 UNUSED static int uv__is_active(const uv_handle_t* h) {
-  return !!(h->flags & UV__ACTIVE);
+  return !!(h->flags & UV__HANDLE_ACTIVE);
 }
 #define uv__is_active(h) uv__is_active((const uv_handle_t*)(h))
 
 UNUSED static void uv__handle_start(uv_handle_t* h) {
-  if (h->flags & UV__ACTIVE) return;
-  if (h->flags & UV__REF) uv__active_handle_add(h);
-  h->flags |= UV__ACTIVE;
+  if (h->flags & UV__HANDLE_ACTIVE) return;
+  if (h->flags & UV__HANDLE_REF) uv__active_handle_add(h);
+  h->flags |= UV__HANDLE_ACTIVE;
 }
 #define uv__handle_start(h) uv__handle_start((uv_handle_t*)(h))
 
 UNUSED static void uv__handle_stop(uv_handle_t* h) {
-  if (!(h->flags & UV__ACTIVE)) return;
-  if (h->flags & UV__REF) uv__active_handle_rm(h);
-  h->flags &= ~UV__ACTIVE;
+  if (!(h->flags & UV__HANDLE_ACTIVE)) return;
+  if (h->flags & UV__HANDLE_REF) uv__active_handle_rm(h);
+  h->flags &= ~UV__HANDLE_ACTIVE;
 }
 #define uv__handle_stop(h) uv__handle_stop((uv_handle_t*)(h))
 
 UNUSED static void uv__handle_ref(uv_handle_t* h) {
-  if (h->flags & UV__REF) return;
-  if (h->flags & UV__ACTIVE) uv__active_handle_add(h);
-  h->flags |= UV__REF;
+  if (h->flags & UV__HANDLE_REF) return;
+  if (h->flags & UV__HANDLE_ACTIVE) uv__active_handle_add(h);
+  h->flags |= UV__HANDLE_REF;
 }
 #define uv__handle_ref(h) uv__handle_ref((uv_handle_t*)(h))
 
 UNUSED static void uv__handle_unref(uv_handle_t* h) {
-  if (!(h->flags & UV__REF)) return;
-  if (h->flags & UV__ACTIVE) uv__active_handle_rm(h);
-  h->flags &= ~UV__REF;
+  if (!(h->flags & UV__HANDLE_REF)) return;
+  if (h->flags & UV__HANDLE_ACTIVE) uv__active_handle_rm(h);
+  h->flags &= ~UV__HANDLE_REF;
 }
 #define uv__handle_unref(h) uv__handle_unref((uv_handle_t*)(h))
 
index 9df75e0..87f5529 100644 (file)
@@ -58,12 +58,8 @@ void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) {
   if (handle->flags & UV_HANDLE_CLOSING &&
       !handle->async_sent) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
-    handle->flags |= UV_HANDLE_CLOSED;
     uv__handle_stop(handle);
-
-    if (handle->close_cb) {
-      handle->close_cb((uv_handle_t*)handle);
-    }
+    uv__handle_close(handle);
   }
 }
 
index 4f546ee..78c9fac 100644 (file)
@@ -66,6 +66,7 @@ static void uv_loop_init(uv_loop_t* loop) {
 
   uv_update_time(loop);
 
+  ngx_queue_init(&loop->handle_queue);
   ngx_queue_init(&loop->active_reqs);
   loop->active_handles = 0;
 
index 61f9de2..30a571a 100644 (file)
@@ -479,7 +479,6 @@ void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
   if (handle->flags & UV_HANDLE_CLOSING &&
       !handle->req_pending) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
-    handle->flags |= UV_HANDLE_CLOSED;
     uv__handle_stop(handle);
 
     if (handle->buffer) {
@@ -507,8 +506,6 @@ void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
       handle->dirw = NULL;
     }
 
-    if (handle->close_cb) {
-      handle->close_cb((uv_handle_t*)handle);
-    }
+    uv__handle_close(handle);
   }
 }
index b501143..a3abb0a 100644 (file)
@@ -57,13 +57,15 @@ uv_handle_type uv_guess_handle(uv_file file) {
 
 
 int uv_is_active(const uv_handle_t* handle) {
-  return (handle->flags & UV__ACTIVE) && !(handle->flags & UV_HANDLE_CLOSING);
+  return (handle->flags & UV__HANDLE_ACTIVE) &&
+        !(handle->flags & UV_HANDLE_CLOSING);
 }
 
 
 void uv_handle_init(uv_loop_t* loop, uv_handle_t* handle) {
   handle->loop = loop;
-  handle->flags = UV__REF;
+  handle->flags = UV__HANDLE_REF;
+  ngx_queue_insert_tail(&loop->handle_queue, &handle->handle_queue);
 
   loop->counters.handle_init++;
 }
index f594ddd..7876226 100644 (file)
@@ -40,9 +40,8 @@
 #define UV_HANDLE_ENDGAME_QUEUED                0x00000004
 #define UV_HANDLE_ACTIVE                        0x00000010
 
-/* Keep in sync with uv-common.h: */
-#define UV__REF                                 0x00000020
-#define UV__ACTIVE                              0x00000040
+/* uv-common.h: #define UV__HANDLE_ACTIVE       0x00000040 */
+/* uv-common.h: #define UV__HANDLE_REF          0x00000020 */
 /* reserved: #define UV_HANDLE_INTERNAL         0x00000080 */
 
 /* Used by streams and UDP handles. */
@@ -131,6 +130,14 @@ void uv_process_endgames(uv_loop_t* loop);
     uv__req_unregister((loop), (req));                                  \
   } while (0)
 
+#define uv__handle_close(handle)                                        \
+  do {                                                                  \
+    ngx_queue_remove(&(handle)->handle_queue);                          \
+    (handle)->flags |= UV_HANDLE_CLOSED;                                \
+    if ((handle)->close_cb) {                                           \
+      (handle)->close_cb((uv_handle_t*)(handle));                       \
+    }                                                                   \
+  } while (0)
 
 /*
  * Handles
index 46d184c..c573cd7 100644 (file)
@@ -30,10 +30,7 @@ void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
     handle->flags |= UV_HANDLE_CLOSED;
     uv__handle_stop(handle);
-
-    if (handle->close_cb) {
-      handle->close_cb(handle);
-    }
+    uv__handle_close(handle);
   }
 }
 
index 2b48e38..8a64f71 100644 (file)
@@ -164,7 +164,7 @@ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access,
   int err;
   char* ptr = (char*)handle;
 
-  while (TRUE) {
+  for (;;) {
     uv_unique_pipe_name(ptr, name, nameSize);
 
     pipeHandle = CreateNamedPipeA(name,
@@ -358,7 +358,6 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
   if (handle->flags & UV_HANDLE_CLOSING &&
       handle->reqs_pending == 0) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
-    handle->flags |= UV_HANDLE_CLOSED;
     uv__handle_stop(handle);
 
     if (handle->flags & UV_HANDLE_CONNECTION) {
@@ -385,9 +384,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
       handle->accept_reqs = NULL;
     }
 
-    if (handle->close_cb) {
-      handle->close_cb((uv_handle_t*)handle);
-    }
+    uv__handle_close(handle);
   }
 }
 
index c6d68ff..55e0b27 100644 (file)
@@ -94,7 +94,7 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
 }
 
 
-static int uv__fast_poll_cancel_poll_reqs(uv_loop_t* loop, uv_poll_t* handle) {
+static int uv__fast_poll_cancel_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
   AFD_POLL_INFO afd_poll_info;
   DWORD result;
   HANDLE event;
@@ -106,15 +106,15 @@ static int uv__fast_poll_cancel_poll_reqs(uv_loop_t* loop, uv_poll_t* handle) {
     return -1;
   }
 
-  afd_poll_info.Exclusive = TRUE;
+  afd_poll_info.Exclusive = FALSE;
   afd_poll_info.NumberOfHandles = 1;
-  afd_poll_info.Timeout.QuadPart = INT64_MAX;
+  afd_poll_info.Timeout.QuadPart = 0;
   afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket;
   afd_poll_info.Handles[0].Status = 0;
   afd_poll_info.Handles[0].Events = AFD_POLL_ALL;
 
   memset(&overlapped, 0, sizeof overlapped);
-  overlapped.hEvent = (HANDLE) ((uintptr_t) event & 1);
+  overlapped.hEvent = (HANDLE) ((uintptr_t) event | 1);
 
   result = uv_msafd_poll(handle->socket,
                          &afd_poll_info,
@@ -129,6 +129,10 @@ static int uv__fast_poll_cancel_poll_reqs(uv_loop_t* loop, uv_poll_t* handle) {
     }
   }
 
+  if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) {
+    uv_fatal_error(GetLastError(), "WaitForSingleObject");
+  }
+
   CloseHandle(event);
   return 0;
 }
@@ -234,7 +238,7 @@ static void uv__fast_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
         pCancelIoEx((HANDLE) handle->socket, &handle->poll_req_2.overlapped);
     } else if (handle->submitted_events_1 | handle->submitted_events_2) {
       /* Execute another unique poll to force the others to return. */
-      uv__fast_poll_cancel_poll_reqs(loop, handle);
+      uv__fast_poll_cancel_poll_req(loop, handle);
     }
   }
 }
@@ -611,10 +615,6 @@ void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) {
   assert(handle->submitted_events_1 == 0);
   assert(handle->submitted_events_2 == 0);
 
-  handle->flags |= UV_HANDLE_CLOSED;
   uv__handle_stop(handle);
-
-  if (handle->close_cb) {
-    handle->close_cb((uv_handle_t*)handle);
-  }
+  uv__handle_close(handle);
 }
index e3d8a1f..23a3012 100644 (file)
 #include "uv.h"
 #include "internal.h"
 
+#include <assert.h>
 #include <io.h>
 #include <stdio.h>
-#include <assert.h>
 #include <stdlib.h>
 #include <signal.h>
 #include <windows.h>
 
+
 #define SIGKILL         9
 
+
+/* CRT file descriptor mode flags */
+#define FOPEN       0x01
+#define FEOFLAG     0x02
+#define FCRLF       0x04
+#define FPIPE       0x08
+#define FNOINHERIT  0x10
+#define FAPPEND     0x20
+#define FDEV        0x40
+#define FTEXT       0x80
+
+
 typedef struct env_var {
   const char* narrow;
   const wchar_t* wide;
@@ -41,6 +54,7 @@ typedef struct env_var {
 
 #define E_V(str) { str "=", L##str, sizeof(str), 0, 0 }
 
+
 #define UTF8_TO_UTF16(s, t)                               \
   size = uv_utf8_to_utf16(s, NULL, 0) * sizeof(wchar_t);  \
   t = (wchar_t*)malloc(size);                             \
@@ -54,6 +68,37 @@ typedef struct env_var {
   }
 
 
+/* The `child_stdio_buffer` buffer has the following layout:
+ *   int number_of_fds
+ *   unsigned char crt_flags[number_of_fds]
+ *   HANDLE os_handle[number_of_fds]
+ */
+
+#define CHILD_STDIO_SIZE(count)                     \
+    (sizeof(int) +                                  \
+     sizeof(unsigned char) * (count) +              \
+     sizeof(uintptr_t) * (count))
+
+#define CHILD_STDIO_COUNT(buffer)                   \
+    *((unsigned int*) (buffer))
+
+#define CHILD_STDIO_LPRESERVED2(buffer)             \
+    ((LPBYTE) (buffer))
+
+#define CHILD_STDIO_CBRESERVED2(buffer)             \
+    CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer)))
+
+#define CHILD_STDIO_CRT_FLAGS(buffer, fd)           \
+    *((unsigned char*) (buffer) + sizeof(int) + fd)
+
+#define CHILD_STDIO_HANDLE(buffer, fd)              \
+    *((HANDLE*) ((unsigned char*) (buffer) +        \
+                 sizeof(int) +                      \
+                 sizeof(unsigned char) *            \
+                 CHILD_STDIO_COUNT((buffer)) +      \
+                 sizeof(HANDLE) * (fd)))
+
+
 static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) {
   uv_handle_init(loop, (uv_handle_t*) handle);
   handle->type = UV_PROCESS;
@@ -63,9 +108,7 @@ static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) {
   handle->wait_handle = INVALID_HANDLE_VALUE;
   handle->process_handle = INVALID_HANDLE_VALUE;
   handle->close_handle = INVALID_HANDLE_VALUE;
-  handle->child_stdio[0] = INVALID_HANDLE_VALUE;
-  handle->child_stdio[1] = INVALID_HANDLE_VALUE;
-  handle->child_stdio[2] = INVALID_HANDLE_VALUE;
+  handle->child_stdio_buffer = NULL;
 
   uv_req_init(loop, (uv_req_t*)&handle->exit_req);
   handle->exit_req.type = UV_PROCESS_EXIT;
@@ -594,6 +637,153 @@ wchar_t* make_program_env(char** env_block) {
 }
 
 
+static int uv_create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* server_pipe,
+    HANDLE* child_pipe_ptr, unsigned int flags) {
+  char pipe_name[64];
+  SECURITY_ATTRIBUTES sa;
+  DWORD server_access = 0;
+  DWORD client_access = 0;
+  HANDLE child_pipe = INVALID_HANDLE_VALUE;
+
+  if (flags & UV_READABLE_PIPE) {
+    server_access |= PIPE_ACCESS_OUTBOUND;
+    client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
+  }
+  if (flags & UV_WRITABLE_PIPE) {
+    server_access |= PIPE_ACCESS_INBOUND;
+    client_access |= GENERIC_WRITE;
+  }
+
+  /* Create server pipe handle. */
+  if (uv_stdio_pipe_server(loop,
+                           server_pipe,
+                           server_access,
+                           pipe_name,
+                           sizeof(pipe_name)) < 0) {
+    goto error;
+  }
+
+  /* Create child pipe handle. */
+  sa.nLength = sizeof sa;
+  sa.lpSecurityDescriptor = NULL;
+  sa.bInheritHandle = TRUE;
+
+  child_pipe = CreateFileA(pipe_name,
+                           client_access,
+                           0,
+                           &sa,
+                           OPEN_EXISTING,
+                           server_pipe->ipc ? FILE_FLAG_OVERLAPPED : 0,
+                           NULL);
+  if (child_pipe == INVALID_HANDLE_VALUE) {
+    uv__set_sys_error(loop, GetLastError());
+    goto error;
+  }
+
+#ifndef NDEBUG
+  /* Validate that the pipe was opened in the right mode. */
+  {
+    DWORD mode;
+    BOOL r = GetNamedPipeHandleState(child_pipe,
+                                     &mode,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     0);
+    assert(r == TRUE);
+    assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT));
+  }
+#endif
+
+  /* Do a blocking ConnectNamedPipe.  This should not block because we have */
+  /* both ends of the pipe created. */
+  if (!ConnectNamedPipe(server_pipe->handle, NULL)) {
+    if (GetLastError() != ERROR_PIPE_CONNECTED) {
+      uv__set_sys_error(loop, GetLastError());
+      goto error;
+    }
+  }
+
+  *child_pipe_ptr = child_pipe;
+  return 0;
+
+ error:
+  if (server_pipe->handle != INVALID_HANDLE_VALUE) {
+    uv_pipe_cleanup(loop, server_pipe);
+  }
+
+  if (child_pipe != INVALID_HANDLE_VALUE) {
+    CloseHandle(child_pipe);
+  }
+
+  return -1;
+}
+
+
+static int duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) {
+  HANDLE current_process;
+
+  current_process = GetCurrentProcess();
+
+  if (!DuplicateHandle(current_process,
+                       handle,
+                       current_process,
+                       dup,
+                       0,
+                       TRUE,
+                       DUPLICATE_SAME_ACCESS)) {
+    *dup = INVALID_HANDLE_VALUE;
+    uv__set_sys_error(loop, GetLastError());
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static int duplicate_fd(uv_loop_t* loop, int fd, HANDLE* dup) {
+  HANDLE handle;
+
+  if (fd == -1) {
+    *dup = INVALID_HANDLE_VALUE;
+    uv__set_artificial_error(loop, UV_EBADF);
+    return -1;
+  }
+
+  handle = (HANDLE)_get_osfhandle(fd);
+  return duplicate_handle(loop, handle, dup);
+}
+
+
+static void set_child_stdio_noinherit(void* buffer) {
+  int i, count;
+
+  count = CHILD_STDIO_COUNT(buffer);
+  for (i = 0; i < count; i++) {
+    HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
+    if (handle != INVALID_HANDLE_VALUE) {
+      SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
+    }
+  }
+}
+
+
+static void close_and_free_child_stdio(void* buffer) {
+  int i, count;
+
+  count = CHILD_STDIO_COUNT(buffer);
+  for (i = 0; i < count; i++) {
+    HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
+    if (handle != INVALID_HANDLE_VALUE) {
+      CloseHandle(handle);
+    }
+  }
+
+  free(buffer);
+}
+
+
 /*
  * Called on Windows thread-pool thread to indicate that
  * a child process has exited.
@@ -636,7 +826,7 @@ static DWORD WINAPI spawn_failure(void* data) {
   char unknown[] = "unknown error\n";
   uv_process_t* process = (uv_process_t*) data;
   uv_loop_t* loop = process->loop;
-  HANDLE child_stderr = process->child_stdio[2];
+  HANDLE child_stderr = CHILD_STDIO_HANDLE(process->child_stdio_buffer, 2);
   char* buf = NULL;
   DWORD count, written;
 
@@ -670,20 +860,6 @@ static DWORD WINAPI spawn_failure(void* data) {
 }
 
 
-static void close_child_stdio(uv_process_t* process) {
-  int i;
-  HANDLE handle;
-
-  for (i = 0; i < ARRAY_SIZE(process->child_stdio); i++) {
-    handle = process->child_stdio[i];
-    if (handle != INVALID_HANDLE_VALUE) {
-      CloseHandle(handle);
-      process->child_stdio[i] = INVALID_HANDLE_VALUE;
-    }
-  }
-}
-
-
 /* Called on main thread after a child process has exited. */
 void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
   DWORD exit_code;
@@ -743,158 +919,199 @@ void uv_process_close(uv_loop_t* loop, uv_process_t* handle) {
 void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) {
   if (handle->flags & UV_HANDLE_CLOSING) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
-    handle->flags |= UV_HANDLE_CLOSED;
     uv__handle_stop(handle);
 
     /* Clean-up the process handle. */
     CloseHandle(handle->process_handle);
 
     /* Clean up the child stdio ends that may have been left open. */
-    close_child_stdio(handle);
-
-    if (handle->close_cb) {
-      handle->close_cb((uv_handle_t*)handle);
+    if (handle->child_stdio_buffer != NULL) {
+      close_and_free_child_stdio(handle->child_stdio_buffer);
     }
+
+    uv__handle_close(handle);
   }
 }
 
 
-static int uv_create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* server_pipe,
-    HANDLE* child_pipe,  DWORD server_access, DWORD child_access,
-    int overlapped) {
-  int err;
-  SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
-  char pipe_name[64];
-  DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT;
+static int init_child_stdio(uv_loop_t* loop, uv_process_options_t* options,
+    void** buffer_ptr) {
+  void* buffer;
+  int count, i;
 
-  if (server_pipe->type != UV_NAMED_PIPE) {
-    uv__set_artificial_error(loop, UV_EINVAL);
-    err = -1;
-    goto done;
-  }
+  count = options->stdio_count;
 
-  /* Create server pipe handle. */
-  err = uv_stdio_pipe_server(loop,
-                             server_pipe,
-                             server_access,
-                             pipe_name,
-                             sizeof(pipe_name));
-  if (err) {
-    goto done;
+  if (count < 0 || count > 255) {
+    /* Only support FDs 0-255 */
+    uv__set_artificial_error(loop, UV_ENOTSUP);
+    return -1;
+  } else if (count < 3) {
+    /* There should always be at least 3 stdio handles. */
+    count = 3;
   }
 
-  /* Create child pipe handle. */
-  *child_pipe = CreateFileA(pipe_name,
-                            child_access,
-                            0,
-                            &sa,
-                            OPEN_EXISTING,
-                            overlapped ? FILE_FLAG_OVERLAPPED : 0,
-                            NULL);
-
-  if (*child_pipe == INVALID_HANDLE_VALUE) {
-    uv__set_sys_error(loop, GetLastError());
-    err = -1;
-    goto done;
+  /* Allocate the child stdio buffer */
+  buffer = malloc(CHILD_STDIO_SIZE(count));
+  if (buffer == NULL) {
+    uv__set_artificial_error(loop, UV_ENOMEM);
+    return -1;
   }
 
-  if (!SetNamedPipeHandleState(*child_pipe, &mode, NULL, NULL)) {
-    uv__set_sys_error(loop, GetLastError());
-    err = -1;
-    goto done;
+  /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can */
+  /* clean up on failure. */
+  CHILD_STDIO_COUNT(buffer) = count;
+  for (i = 0; i < count; i++) {
+    CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
+    CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
   }
 
-  /* Do a blocking ConnectNamedPipe.  This should not block because
-   * we have both ends of the pipe created.
-   */
-  if (!ConnectNamedPipe(server_pipe->handle, NULL)) {
-    if (GetLastError() != ERROR_PIPE_CONNECTED) {
-      uv__set_sys_error(loop, GetLastError());
-      err = -1;
-      goto done;
-    }
-  }
+  for (i = 0; i < options->stdio_count; i++) {
+    uv_stdio_container_t fdopt = options->stdio[i];
 
-  err = 0;
+    switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
+            UV_INHERIT_STREAM)) {
+      case UV_IGNORE:
+        /* The child is not supposed to inherit this handle. It has already */
+        /* been initialized to INVALID_HANDLE_VALUE, so just keep it like */
+        /* that. */
+        continue;
 
-done:
-  if (err) {
-    if (server_pipe->handle != INVALID_HANDLE_VALUE) {
-      uv_pipe_cleanup(loop, server_pipe);
-    }
+      case UV_CREATE_PIPE: {
+        /* Create a pair of two connected pipe ends; one end is turned into */
+        /* an uv_pipe_t for use by the parent. The other one is given to */
+        /* the child. */
+        uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream;
+        HANDLE child_pipe;
+
+        /* Create a new, connected pipe pair. stdio[i].stream should point */
+        /* to an uninitialized, but not connected pipe handle. */
+        assert(fdopt.data.stream->type == UV_NAMED_PIPE);
+        assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION));
+        assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER));
+
+        if (uv_create_stdio_pipe_pair(loop,
+                                      parent_pipe,
+                                      &child_pipe,
+                                      fdopt.flags) < 0) {
+          goto error;
+        }
 
-    if (*child_pipe != INVALID_HANDLE_VALUE) {
-      CloseHandle(*child_pipe);
-      *child_pipe = INVALID_HANDLE_VALUE;
-    }
-  }
+        CHILD_STDIO_HANDLE(buffer, i) = child_pipe;
+        CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
+        break;
+      }
 
-  return err;
-}
+      case UV_INHERIT_FD: {
+        /* Inherit a raw FD. */
+        HANDLE child_handle;
 
+        /* Make an inheritable duplicate of the handle. */
+        if (duplicate_fd(loop, fdopt.data.fd, &child_handle) < 0) {
+          goto error;
+        }
 
-static int duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) {
-  HANDLE current_process;
+        /* Figure out what the type is. */
+        switch (GetFileType(child_handle)) {
+          case FILE_TYPE_DISK:
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN;
+            break;
+
+          case FILE_TYPE_PIPE:
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
+
+          case FILE_TYPE_CHAR:
+          case FILE_TYPE_REMOTE:
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
+            break;
+
+          case FILE_TYPE_UNKNOWN:
+            if (GetLastError != 0) {
+              uv__set_sys_error(loop, GetLastError());
+              CloseHandle(child_handle);
+              goto error;
+            }
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
+            break;
+
+          default:
+            assert(0);
+        }
 
-  current_process = GetCurrentProcess();
+        CHILD_STDIO_HANDLE(buffer, i) = child_handle;
+        break;
+      }
 
-  if (!DuplicateHandle(current_process,
-                       handle,
-                       current_process,
-                       dup,
-                       0,
-                       TRUE,
-                       DUPLICATE_SAME_ACCESS)) {
-    *dup = INVALID_HANDLE_VALUE;
-    uv__set_sys_error(loop, GetLastError());
-    return -1;
-  }
+      case UV_INHERIT_STREAM: {
+        /* Use an existing stream as the stdio handle for the child. */
+        HANDLE stream_handle, child_handle;
+        unsigned char crt_flags;
+        uv_stream_t* stream = fdopt.data.stream;
+
+        /* Leech the handle out of the stream. */
+        if (stream->type = UV_TTY) {
+          stream_handle = ((uv_tty_t*) stream)->handle;
+          crt_flags = FOPEN | FDEV;
+        } else if (stream->type == UV_NAMED_PIPE &&
+                   stream->flags & UV_HANDLE_CONNECTED) {
+          stream_handle = ((uv_pipe_t*) stream)->handle;
+          crt_flags = FOPEN | FPIPE;
+        } else {
+          stream_handle = INVALID_HANDLE_VALUE;
+          crt_flags = 0;
+        }
 
-  return 0;
-}
+        if (stream_handle == NULL ||
+            stream_handle == INVALID_HANDLE_VALUE) {
+          /* The handle is already closed, or not yet created, or the */
+          /* stream type is not supported. */
+          uv__set_artificial_error(loop, UV_ENOTSUP);
+          goto error;
+        }
 
+        /* Make an inheritable copy of the handle. */
+        if (duplicate_handle(loop,
+                             stream_handle,
+                             &child_handle) < 0) {
+          goto error;
+        }
 
-static int duplicate_fd(uv_loop_t* loop, int fd, HANDLE* dup) {
-  HANDLE handle;
+        CHILD_STDIO_HANDLE(buffer, i) = child_handle;
+        CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
+      }
 
-  if (fd == -1) {
-    *dup = INVALID_HANDLE_VALUE;
-    uv__set_artificial_error(loop, UV_EBADF);
-    return -1;
+      default:
+        assert(0);
+    }
   }
 
-  handle = (HANDLE)_get_osfhandle(fd);
-  return duplicate_handle(loop, handle, dup);
+  *buffer_ptr  = buffer;
+  return 0;
+
+ error:
+  close_and_free_child_stdio(buffer);
+  return -1;
 }
 
 
 int uv_spawn(uv_loop_t* loop, uv_process_t* process,
     uv_process_options_t options) {
-  int err = 0, keep_child_stdio_open = 0;
+  int size, err = 0, keep_child_stdio_open = 0;
   wchar_t* path = NULL;
-  int size, i, overlapped;
-  DWORD server_access, child_access;
   BOOL result;
   wchar_t* application_path = NULL, *application = NULL, *arguments = NULL,
-    *env = NULL, *cwd = NULL;
-  HANDLE* child_stdio = process->child_stdio;
+           *env = NULL, *cwd = NULL;
   STARTUPINFOW startup;
   PROCESS_INFORMATION info;
-  uv_pipe_t* pipe;
+  DWORD process_flags;
 
   if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
     uv__set_artificial_error(loop, UV_ENOTSUP);
     return -1;
   }
 
-  /* Only support FDs 0-2 */
-  if (options.stdio_count > 3) {
-    uv__set_artificial_error(loop, UV_ENOTSUP);
-    return -1;
-  }
-
   assert(options.file != NULL);
   assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
+                             UV_PROCESS_DETACHED |
                              UV_PROCESS_SETGID |
                              UV_PROCESS_SETUID)));
 
@@ -913,8 +1130,11 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
     if (size) {
       cwd = (wchar_t*)malloc(size);
       if (!cwd) {
-        uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
+        uv__set_artificial_error(loop, UV_ENOMEM);
+        err = -1;
+        goto done;
       }
+
       GetCurrentDirectoryW(size, cwd);
     } else {
       uv__set_sys_error(loop, GetLastError());
@@ -942,60 +1162,10 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
     application_path = application;
   }
 
-  for (i = 0; i < options.stdio_count || i < 3; i++) {
-    if (i >= options.stdio_count ||
-        options.stdio[i].flags == UV_IGNORE) {
-      child_stdio[i] = INVALID_HANDLE_VALUE;
-      continue;
-    }
-
-    if (options.stdio[i].flags & UV_RAW_FD) {
-      err = duplicate_fd(loop, options.stdio[i].data.fd, &child_stdio[i]);
-    } else if (options.stdio[i].data.stream->type == UV_NAMED_PIPE) {
-      pipe = (uv_pipe_t*)options.stdio[i].data.stream;
-
-      if (options.stdio[i].flags & UV_CREATE_PIPE) {
-        server_access = 0;
-        child_access = 0;
-        if (pipe->ipc) {
-          server_access = PIPE_ACCESS_DUPLEX;
-          child_access = GENERIC_READ | FILE_WRITE_ATTRIBUTES | GENERIC_WRITE;
-          overlapped = 1;
-        } else {
-          overlapped = 0;
-
-          if (options.stdio[i].flags & UV_READABLE_PIPE) {
-            server_access |= PIPE_ACCESS_OUTBOUND;
-            child_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
-          }
 
-          if (options.stdio[i].flags & UV_WRITABLE_PIPE) {
-            server_access |= PIPE_ACCESS_INBOUND;
-            child_access |= GENERIC_WRITE;
-          }
-        }
-
-        err = uv_create_stdio_pipe_pair(
-            loop,
-            pipe,
-            &child_stdio[i],
-            server_access,
-            child_access,
-            overlapped);
-      } else {
-        err = duplicate_handle(loop, pipe->handle, &child_stdio[i]);
-      }
-    } else if(options.stdio[i].data.stream->type == UV_TTY) {
-      err = duplicate_handle(loop, 
-        ((uv_tty_t*)options.stdio[i].data.stream)->handle, &child_stdio[i]);
-    } else {
-      err = -1;
-      uv__set_artificial_error(loop, UV_ENOTSUP);
-    }
-
-    if (err) {
-      goto done;
-    }
+  if (init_child_stdio(loop, &options, &process->child_stdio_buffer) < 0) {
+     err = -1;
+     goto done;
   }
 
   startup.cb = sizeof(startup);
@@ -1003,18 +1173,23 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
   startup.lpDesktop = NULL;
   startup.lpTitle = NULL;
   startup.dwFlags = STARTF_USESTDHANDLES;
-  startup.cbReserved2 = 0;
-  startup.lpReserved2 = NULL;
-  startup.hStdInput = child_stdio[0];
-  startup.hStdOutput = child_stdio[1];
-  startup.hStdError = child_stdio[2];
+  startup.cbReserved2 = CHILD_STDIO_CBRESERVED2(process->child_stdio_buffer);
+  startup.lpReserved2 = CHILD_STDIO_LPRESERVED2(process->child_stdio_buffer);
+  startup.hStdInput = CHILD_STDIO_HANDLE(process->child_stdio_buffer, 0);
+  startup.hStdOutput = CHILD_STDIO_HANDLE(process->child_stdio_buffer, 1);
+  startup.hStdError = CHILD_STDIO_HANDLE(process->child_stdio_buffer, 2);
+
+  process_flags = CREATE_UNICODE_ENVIRONMENT;
+  if (options.flags & UV_PROCESS_DETACHED) {
+    process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
+  }
 
   if (CreateProcessW(application_path,
                      arguments,
                      NULL,
                      NULL,
                      1,
-                     CREATE_UNICODE_ENVIRONMENT,
+                     process_flags,
                      env,
                      cwd,
                      &startup,
@@ -1062,19 +1237,19 @@ done:
   free(env);
   free(path);
 
-  /* Under normal circumstances we should close the stdio handles now - */
-  /* the child now has its own duplicates, or something went horribly wrong. */
+  /* Under normal circumstances we should close the stdio handles now - the */
+  /* the child now has its own duplicates, or something went horribly wrong */
   /* The only exception is when CreateProcess has failed, then we actually */
   /* need to keep the stdio handles to report the error asynchronously. */
-  if (!keep_child_stdio_open) {
-    close_child_stdio(process);
+  if (process->child_stdio_buffer == NULL) {
+    /* Something went wrong before child stdio was initialized. */
+  } else if (!keep_child_stdio_open) {
+    close_and_free_child_stdio(process->child_stdio_buffer);
+    process->child_stdio_buffer = NULL;
   } else {
     /* We're keeping the handles open, the thread pool is going to have */
     /* it's way with them. But at least make them non-inheritable. */
-    int i;
-    for (i = 0; i < ARRAY_SIZE(process->child_stdio); i++) {
-      SetHandleInformation(child_stdio[i], HANDLE_FLAG_INHERIT, 0);
-    }
+    set_child_stdio_noinherit(process->child_stdio_buffer);
   }
 
   if (err == 0) {
index fd956a0..c049d64 100644 (file)
@@ -197,7 +197,6 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
   if (handle->flags & UV_HANDLE_CLOSING &&
       handle->reqs_pending == 0) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
-    handle->flags |= UV_HANDLE_CLOSED;
     uv__handle_stop(handle);
 
     if (!(handle->flags & UV_HANDLE_TCP_SOCKET_CLOSED)) {
@@ -236,10 +235,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
       }
     }
 
-    if (handle->close_cb) {
-      handle->close_cb((uv_handle_t*)handle);
-    }
-
+    uv__handle_close(handle);
     loop->active_tcp_streams--;
   }
 }
index fbde888..e71e4c6 100644 (file)
@@ -126,12 +126,8 @@ int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
 void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle) {
   if (handle->flags & UV_HANDLE_CLOSING) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
-    handle->flags |= UV_HANDLE_CLOSED;
     uv__handle_stop(handle);
-
-    if (handle->close_cb) {
-      handle->close_cb((uv_handle_t*)handle);
-    }
+    uv__handle_close(handle);
   }
 }
 
index bd46215..0715992 100644 (file)
@@ -1773,12 +1773,8 @@ void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
     assert(handle->read_raw_wait == NULL);
 
     assert(!(handle->flags & UV_HANDLE_CLOSED));
-    handle->flags |= UV_HANDLE_CLOSED;
     uv__handle_stop(handle);
-
-    if (handle->close_cb) {
-      handle->close_cb((uv_handle_t*)handle);
-    }
+    uv__handle_close(handle);
   }
 }
 
index ddd08a2..3ea23f9 100644 (file)
@@ -155,12 +155,8 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) {
   if (handle->flags & UV_HANDLE_CLOSING &&
       handle->reqs_pending == 0) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
-    handle->flags |= UV_HANDLE_CLOSED;
     uv__handle_stop(handle);
-
-    if (handle->close_cb) {
-      handle->close_cb((uv_handle_t*)handle);
-    }
+    uv__handle_close(handle);
   }
 }
 
index 26267f4..e4494f1 100644 (file)
@@ -21,6 +21,7 @@
 
 BENCHMARK_DECLARE (sizes)
 BENCHMARK_DECLARE (loop_count)
+BENCHMARK_DECLARE (loop_count_timed)
 BENCHMARK_DECLARE (ping_pongs)
 BENCHMARK_DECLARE (tcp_write_batch)
 BENCHMARK_DECLARE (tcp4_pound_100)
@@ -55,6 +56,7 @@ HELPER_DECLARE    (dns_server)
 TASK_LIST_START
   BENCHMARK_ENTRY  (sizes)
   BENCHMARK_ENTRY  (loop_count)
+  BENCHMARK_ENTRY  (loop_count_timed)
 
   BENCHMARK_ENTRY  (ping_pongs)
   BENCHMARK_HELPER (ping_pongs, tcp4_echo_server)
index 8bcb75f..a58181a 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 
+#define NUM_TICKS (2 * 1000 * 1000)
+
 static unsigned long ticks;
 static uv_idle_t idle_handle;
 static uv_timer_t timer_handle;
 
 
 static void idle_cb(uv_idle_t* handle, int status) {
+  if (++ticks == NUM_TICKS)
+    uv_idle_stop(handle);
+}
+
+
+static void idle2_cb(uv_idle_t* handle, int status) {
   ticks++;
 }
 
@@ -43,13 +51,35 @@ static void timer_cb(uv_timer_t* handle, int status) {
 
 BENCHMARK_IMPL(loop_count) {
   uv_loop_t* loop = uv_default_loop();
-
-  uv_timer_init(loop, &timer_handle);
-  uv_timer_start(&timer_handle, timer_cb, 5000, 0);
+  uint64_t ns;
 
   uv_idle_init(loop, &idle_handle);
   uv_idle_start(&idle_handle, idle_cb);
 
+  ns = uv_hrtime();
+  uv_run(loop);
+  ns = uv_hrtime() - ns;
+
+  ASSERT(ticks == NUM_TICKS);
+
+  LOGF("loop_count: %d ticks in %.2fs (%.0f/s)\n",
+       NUM_TICKS,
+       ns / 1e9,
+       NUM_TICKS / (ns / 1e9));
+
+  return 0;
+}
+
+
+BENCHMARK_IMPL(loop_count_timed) {
+  uv_loop_t* loop = uv_default_loop();
+
+  uv_idle_init(loop, &idle_handle);
+  uv_idle_start(&idle_handle, idle2_cb);
+
+  uv_timer_init(loop, &timer_handle);
+  uv_timer_start(&timer_handle, timer_cb, 5000, 0);
+
   uv_run(loop);
 
   LOGF("loop_count: %lu ticks (%.0f ticks/s)\n", ticks, ticks / 5.0);
index db7ea70..37f91da 100644 (file)
 #include <stdio.h>
 #include <string.h>
 
+#ifdef _WIN32
+# include <io.h>
+#endif
+
 #include "uv.h"
 #include "runner.h"
 #include "task.h"
@@ -104,5 +108,17 @@ static int maybe_run_test(int argc, char **argv) {
     while (1) uv_sleep(10000);
   }
 
+  if (strcmp(argv[1], "spawn_helper5") == 0) {
+    const char* out = "fourth stdio!\n\0";
+#ifdef _WIN32
+    DWORD bytes;
+    WriteFile((HANDLE) _get_osfhandle(3), out, strlen(out), &bytes, NULL);
+#else
+    write(3, out, strlen(out));
+    fsync(3);
+#endif
+    return 1;
+  }
+
   return run_test(argv[1], TEST_TIMEOUT, 0);
 }
index ffb1fe2..9a5ba69 100644 (file)
@@ -81,6 +81,7 @@ TEST_DECLARE   (timer_start_twice)
 TEST_DECLARE   (idle_starvation)
 TEST_DECLARE   (loop_handles)
 TEST_DECLARE   (get_loadavg)
+TEST_DECLARE   (walk_handles)
 TEST_DECLARE   (ref)
 TEST_DECLARE   (idle_ref)
 TEST_DECLARE   (async_ref)
@@ -118,7 +119,9 @@ TEST_DECLARE   (pass_always)
 TEST_DECLARE   (spawn_exit_code)
 TEST_DECLARE   (spawn_stdout)
 TEST_DECLARE   (spawn_stdin)
+TEST_DECLARE   (spawn_stdio_greater_than_3)
 TEST_DECLARE   (spawn_and_kill)
+TEST_DECLARE   (spawn_detached)
 TEST_DECLARE   (spawn_and_kill_with_std)
 TEST_DECLARE   (spawn_and_ping)
 TEST_DECLARE   (spawn_setuid_fails)
@@ -300,6 +303,7 @@ TASK_LIST_START
   TEST_ENTRY  (process_ref)
 
   TEST_ENTRY  (loop_handles)
+  TEST_ENTRY  (walk_handles)
 
   TEST_ENTRY  (async)
 
@@ -330,7 +334,9 @@ TASK_LIST_START
   TEST_ENTRY  (spawn_exit_code)
   TEST_ENTRY  (spawn_stdout)
   TEST_ENTRY  (spawn_stdin)
+  TEST_ENTRY  (spawn_stdio_greater_than_3)
   TEST_ENTRY  (spawn_and_kill)
+  TEST_ENTRY  (spawn_detached)
   TEST_ENTRY  (spawn_and_kill_with_std)
   TEST_ENTRY  (spawn_and_ping)
   TEST_ENTRY  (spawn_setuid_fails)
index 45a41f1..9972dfa 100644 (file)
@@ -148,7 +148,7 @@ static void idle_1_cb(uv_idle_t* handle, int status) {
   ASSERT(idles_1_active > 0);
 
   /* Init idle_2 and make it active */
-  if (!idle_2_is_active) {
+  if (!idle_2_is_active && !uv_is_closing((uv_handle_t*)&idle_2_handle)) {
     r = uv_idle_init(uv_default_loop(), &idle_2_handle);
     ASSERT(r == 0);
     r = uv_idle_start(&idle_2_handle, idle_2_cb);
@@ -208,11 +208,6 @@ static void check_cb(uv_check_t* handle, int status) {
   ASSERT(handle == &check_handle);
   ASSERT(status == 0);
 
-  /* XXX
-  ASSERT(idles_1_active == 0);
-  ASSERT(idle_2_is_active == 0);
-  */
-
   if (loop_iteration < ITERATIONS) {
     /* Make some idle watchers active */
     for (i = 0; i < 1 + (loop_iteration % IDLE_COUNT); i++) {
@@ -250,9 +245,6 @@ static void prepare_2_cb(uv_prepare_t* handle, int status) {
   ASSERT(handle == &prepare_2_handle);
   ASSERT(status == 0);
 
-  /* XXX ASSERT(idles_1_active == 0); */
-  /* XXX ASSERT(idle_2_is_active == 0); */
-
   /* prepare_2 gets started by prepare_1 when (loop_iteration % 2 == 0), */
   /* and it stops itself immediately. A started watcher is not queued */
   /* until the next round, so when this callback is made */
@@ -274,11 +266,6 @@ static void prepare_1_cb(uv_prepare_t* handle, int status) {
   ASSERT(handle == &prepare_1_handle);
   ASSERT(status == 0);
 
-  /* XXX
-  ASSERT(idles_1_active == 0);
-  ASSERT(idle_2_is_active == 0);
-  */
-
   if (loop_iteration % 2 == 0) {
     r = uv_prepare_start(&prepare_2_handle, prepare_2_cb);
     ASSERT(r == 0);
@@ -340,12 +327,8 @@ TEST_IMPL(loop_handles) {
   ASSERT(check_close_cb_called == 1);
 
   /* idle_1_cb should be called a lot */
-  /* XXX ASSERT(idle_1_cb_called >= ITERATIONS * IDLE_COUNT * 2); */
   ASSERT(idle_1_close_cb_called == IDLE_COUNT);
-  /* XXX ASSERT(idles_1_active == 0); */
 
-  /* XXX ASSERT(idle_2_cb_started >= ITERATIONS); */
-  /* XXX ASSERT(idle_2_cb_called == idle_2_cb_started); */
   ASSERT(idle_2_close_cb_called == idle_2_cb_started);
   ASSERT(idle_2_is_active == 0);
 
index 25863de..6043043 100644 (file)
@@ -99,6 +99,10 @@ static void kill_cb(uv_process_t* process, int exit_status, int term_signal) {
   ASSERT(err.code == UV_ESRCH);
 }
 
+static void detach_failure_cb(uv_process_t* process, int exit_status, int term_signal) {
+  printf("detach_cb\n");
+  exit_cb_called++;
+}
 
 static uv_buf_t on_alloc(uv_handle_t* handle, size_t suggested_size) {
   uv_buf_t buf;
@@ -217,7 +221,7 @@ TEST_IMPL(spawn_stdout_to_file) {
 
   options.stdio = stdio;
   options.stdio[0].flags = UV_IGNORE;
-  options.stdio[1].flags = UV_RAW_FD;
+  options.stdio[1].flags = UV_INHERIT_FD;
   options.stdio[1].data.fd = file;
   options.stdio_count = 2;
 
@@ -291,6 +295,40 @@ TEST_IMPL(spawn_stdin) {
 }
 
 
+TEST_IMPL(spawn_stdio_greater_than_3) {
+  int r;
+  uv_pipe_t pipe;
+  uv_stdio_container_t stdio[4];
+
+  init_process_options("spawn_helper5", exit_cb);
+
+  uv_pipe_init(uv_default_loop(), &pipe, 0);
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_IGNORE;
+  options.stdio[1].flags = UV_IGNORE;
+  options.stdio[2].flags = UV_IGNORE;
+  options.stdio[3].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[3].data.stream = (uv_stream_t*)&pipe;
+  options.stdio_count = 4;
+
+  r = uv_spawn(uv_default_loop(), &process, options);
+  ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*) &pipe, on_alloc, on_read);
+  ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop());
+  ASSERT(r == 0);
+
+  ASSERT(exit_cb_called == 1);
+  ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */
+  printf("output from stdio[3] is: %s", output);
+  ASSERT(strcmp("fourth stdio!\n", output) == 0);
+
+  return 0;
+}
+
+
 TEST_IMPL(spawn_and_kill) {
   int r;
 
@@ -314,6 +352,32 @@ TEST_IMPL(spawn_and_kill) {
   return 0;
 }
 
+TEST_IMPL(spawn_detached) {
+  int r;
+  uv_err_t err;
+
+  init_process_options("spawn_helper4", detach_failure_cb);
+
+  options.flags |= UV_PROCESS_DETACHED;
+
+  r = uv_spawn(uv_default_loop(), &process, options);
+  ASSERT(r == 0);
+
+  uv_unref((uv_handle_t*)&process);
+
+  r = uv_run(uv_default_loop());
+  ASSERT(r == 0);
+
+  ASSERT(exit_cb_called == 0);
+
+  err = uv_kill(process.pid, 0);
+  ASSERT(err.code == 0);
+
+  err = uv_kill(process.pid, 15);
+  ASSERT(err.code == 0);
+
+  return 0;
+}
 
 TEST_IMPL(spawn_and_kill_with_std) {
   int r;
diff --git a/deps/uv/test/test-walk-handles.c b/deps/uv/test/test-walk-handles.c
new file mode 100644 (file)
index 0000000..a72c095
--- /dev/null
@@ -0,0 +1,77 @@
+/* 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 <stdlib.h>
+
+static char magic_cookie[] = "magic cookie";
+static int seen_timer_handle;
+static uv_timer_t timer;
+
+
+static void walk_cb(uv_handle_t* handle, void* arg) {
+  ASSERT(arg == (void*)magic_cookie);
+
+  if (handle == (uv_handle_t*)&timer) {
+    seen_timer_handle++;
+  } else {
+    ASSERT(0 && "unexpected handle");
+  }
+}
+
+
+static void timer_cb(uv_timer_t* handle, int status) {
+  ASSERT(handle == &timer);
+  ASSERT(status == 0);
+
+  uv_walk(handle->loop, walk_cb, magic_cookie);
+  uv_close((uv_handle_t*)handle, NULL);
+}
+
+
+TEST_IMPL(walk_handles) {
+  uv_loop_t* loop;
+  int r;
+
+  loop = uv_default_loop();
+
+  r = uv_timer_init(loop, &timer);
+  ASSERT(r == 0);
+
+  r = uv_timer_start(&timer, timer_cb, 1, 0);
+  ASSERT(r == 0);
+
+  /* Start event loop, expect to see the timer handle in walk_cb. */
+  ASSERT(seen_timer_handle == 0);
+  r = uv_run(loop);
+  ASSERT(r == 0);
+  ASSERT(seen_timer_handle == 1);
+
+  /* Loop is finished, walk_cb should not see our timer handle. */
+  seen_timer_handle = 0;
+  uv_walk(loop, walk_cb, magic_cookie);
+  ASSERT(seen_timer_handle == 0);
+
+  return 0;
+}
index f53e18e..47f8767 100644 (file)
         'test/test-ipc-send-recv.c',
         'test/test-list.h',
         'test/test-loop-handles.c',
+        'test/test-walk-handles.c',
         'test/test-multiple-listen.c',
         'test/test-pass-always.c',
         'test/test-ping-pong.c',