From: Bert Belder Date: Fri, 1 Jun 2012 16:07:22 +0000 (+0200) Subject: uv: upgrade to 87dbffbd X-Git-Tag: v0.7.10~57 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=30a0e58d63a8fb48ee47472a52450539e0656df9;p=platform%2Fupstream%2Fnodejs.git uv: upgrade to 87dbffbd --- diff --git a/deps/uv/include/uv-private/ev.h b/deps/uv/include/uv-private/ev.h index 11e81cd..7709bc3 100644 --- a/deps/uv/include/uv-private/ev.h +++ b/deps/uv/include/uv-private/ev.h @@ -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 */ /* diff --git a/deps/uv/include/uv-private/uv-unix.h b/deps/uv/include/uv-private/uv-unix.h index 27ed333..ae9f5fb 100644 --- a/deps/uv/include/uv-private/uv-unix.h +++ b/deps/uv/include/uv-private/uv-unix.h @@ -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; \ diff --git a/deps/uv/include/uv-private/uv-win.h b/deps/uv/include/uv-private/uv-win.h index ad3e965..3b091b9 100644 --- a/deps/uv/include/uv-private/uv-win.h +++ b/deps/uv/include/uv-private/uv-win.h @@ -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; \ diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index f9ffbaf..2ddcfa7 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -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; diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index b3f2321..1802d2f 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -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; } diff --git a/deps/uv/src/unix/ev/ev.c b/deps/uv/src/unix/ev/ev.c index a432bfb..a722981 100644 --- a/deps/uv/src/unix/ev/ev.c +++ b/deps/uv/src/unix/ev/ev.c @@ -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; diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index eb5be60..8a28b2a 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -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); diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c index 7005765..08985d6 100644 --- a/deps/uv/src/unix/loop.c +++ b/deps/uv/src/unix/loop.c @@ -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); diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index 666ebec..317ac67 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -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. diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index 2efe896..7584d92 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -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++) { diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 7ea4487..9091b90 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -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; } diff --git a/deps/uv/src/unix/timer.c b/deps/uv/src/unix/timer.c index e50a35a..0d81997 100644 --- a/deps/uv/src/unix/timer.c +++ b/deps/uv/src/unix/timer.c @@ -22,92 +22,114 @@ #include "internal.h" #include - -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); + } } diff --git a/deps/uv/src/unix/uv-eio.c b/deps/uv/src/unix/uv-eio.c index b36eea3..10dc0a6 100644 --- a/deps/uv/src/unix/uv-eio.c +++ b/deps/uv/src/unix/uv-eio.c @@ -22,6 +22,7 @@ #include "uv.h" #include "eio.h" +#include "internal.h" #include #include @@ -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); } diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c index e81f7a3..761133a 100644 --- a/deps/uv/src/uv-common.c +++ b/deps/uv/src/uv-common.c @@ -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); } diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h index 6e484ef..4a721ef 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h @@ -49,12 +49,14 @@ #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)) diff --git a/deps/uv/src/win/async.c b/deps/uv/src/win/async.c index 9df75e0..87f5529 100644 --- a/deps/uv/src/win/async.c +++ b/deps/uv/src/win/async.c @@ -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); } } diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index 4f546ee..78c9fac 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -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; diff --git a/deps/uv/src/win/fs-event.c b/deps/uv/src/win/fs-event.c index 61f9de2..30a571a 100644 --- a/deps/uv/src/win/fs-event.c +++ b/deps/uv/src/win/fs-event.c @@ -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); } } diff --git a/deps/uv/src/win/handle.c b/deps/uv/src/win/handle.c index b501143..a3abb0a 100644 --- a/deps/uv/src/win/handle.c +++ b/deps/uv/src/win/handle.c @@ -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++; } diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index f594ddd..7876226 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -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 diff --git a/deps/uv/src/win/loop-watcher.c b/deps/uv/src/win/loop-watcher.c index 46d184c..c573cd7 100644 --- a/deps/uv/src/win/loop-watcher.c +++ b/deps/uv/src/win/loop-watcher.c @@ -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); } } diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index 2b48e38..8a64f71 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -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); } } diff --git a/deps/uv/src/win/poll.c b/deps/uv/src/win/poll.c index c6d68ff..55e0b27 100644 --- a/deps/uv/src/win/poll.c +++ b/deps/uv/src/win/poll.c @@ -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); } diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c index e3d8a1f..23a3012 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -22,15 +22,28 @@ #include "uv.h" #include "internal.h" +#include #include #include -#include #include #include #include + #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) { diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index fd956a0..c049d64 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -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--; } } diff --git a/deps/uv/src/win/timer.c b/deps/uv/src/win/timer.c index fbde888..e71e4c6 100644 --- a/deps/uv/src/win/timer.c +++ b/deps/uv/src/win/timer.c @@ -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); } } diff --git a/deps/uv/src/win/tty.c b/deps/uv/src/win/tty.c index bd46215..0715992 100644 --- a/deps/uv/src/win/tty.c +++ b/deps/uv/src/win/tty.c @@ -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); } } diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c index ddd08a2..3ea23f9 100644 --- a/deps/uv/src/win/udp.c +++ b/deps/uv/src/win/udp.c @@ -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); } } diff --git a/deps/uv/test/benchmark-list.h b/deps/uv/test/benchmark-list.h index 26267f4..e4494f1 100644 --- a/deps/uv/test/benchmark-list.h +++ b/deps/uv/test/benchmark-list.h @@ -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) diff --git a/deps/uv/test/benchmark-loop-count.c b/deps/uv/test/benchmark-loop-count.c index 8bcb75f..a58181a 100644 --- a/deps/uv/test/benchmark-loop-count.c +++ b/deps/uv/test/benchmark-loop-count.c @@ -25,12 +25,20 @@ #include #include +#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); diff --git a/deps/uv/test/run-tests.c b/deps/uv/test/run-tests.c index db7ea70..37f91da 100644 --- a/deps/uv/test/run-tests.c +++ b/deps/uv/test/run-tests.c @@ -22,6 +22,10 @@ #include #include +#ifdef _WIN32 +# include +#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); } diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index ffb1fe2..9a5ba69 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -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) diff --git a/deps/uv/test/test-loop-handles.c b/deps/uv/test/test-loop-handles.c index 45a41f1..9972dfa 100644 --- a/deps/uv/test/test-loop-handles.c +++ b/deps/uv/test/test-loop-handles.c @@ -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); diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index 25863de..6043043 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -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 index 0000000..a72c095 --- /dev/null +++ b/deps/uv/test/test-walk-handles.c @@ -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 +#include + +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; +} diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index f53e18e..47f8767 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -319,6 +319,7 @@ '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',