};
#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 */
/*
# 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)
/* 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 \
uv__io_t io_watcher;
-/* UV_PREPARE */ \
+/* UV_PREPARE */
#define UV_PREPARE_PRIVATE_FIELDS \
uv_prepare_cb prepare_cb; \
ngx_queue_t queue;
/* 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; \
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; \
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.
/* read-only */ \
uv_handle_type type; \
/* private */ \
+ ngx_queue_t handle_queue; \
UV_HANDLE_PRIVATE_FIELDS \
/* The abstract base class of all handles. */
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.
*
/* 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 {
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;
/*
* 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)
};
/*
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;
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;
}
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;
+ }
}
}
-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);
}
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;
}
}
void
-ev_run (EV_P_ int flags)
+ev_run (EV_P_ ev_tstamp waittime)
{
#if EV_FEATURE_API
++loop_depth;
}
#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;
/* 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;
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) {
/* 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);
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);
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);
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);
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.
*/
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;
}
}
assert(options.file != NULL);
assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
+ UV_PROCESS_DETACHED |
UV_PROCESS_SETGID |
UV_PROCESS_SETUID)));
if (pid == 0) {
/* Child */
+ if (options.flags & UV_PROCESS_DETACHED) {
+ setsid();
+ }
/* Dup fds */
for (i = 0; i < options.stdio_count; i++) {
}
-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;
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;
}
#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);
+ }
}
#include "uv.h"
#include "eio.h"
+#include "internal.h"
#include <assert.h>
#include <stdio.h>
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);
}
}
+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);
}
#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_;
#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))
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);
}
}
uv_update_time(loop);
+ ngx_queue_init(&loop->handle_queue);
ngx_queue_init(&loop->active_reqs);
loop->active_handles = 0;
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) {
handle->dirw = NULL;
}
- if (handle->close_cb) {
- handle->close_cb((uv_handle_t*)handle);
- }
+ uv__handle_close(handle);
}
}
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++;
}
#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. */
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
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);
}
}
int err;
char* ptr = (char*)handle;
- while (TRUE) {
+ for (;;) {
uv_unique_pipe_name(ptr, name, nameSize);
pipeHandle = CreateNamedPipeA(name,
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) {
handle->accept_reqs = NULL;
}
- if (handle->close_cb) {
- handle->close_cb((uv_handle_t*)handle);
- }
+ uv__handle_close(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;
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,
}
}
+ if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) {
+ uv_fatal_error(GetLastError(), "WaitForSingleObject");
+ }
+
CloseHandle(event);
return 0;
}
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);
}
}
}
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);
}
#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;
#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); \
}
+/* 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;
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;
}
+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.
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;
}
-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;
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)));
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());
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);
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,
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) {
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)) {
}
}
- if (handle->close_cb) {
- handle->close_cb((uv_handle_t*)handle);
- }
-
+ uv__handle_close(handle);
loop->active_tcp_streams--;
}
}
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);
}
}
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);
}
}
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);
}
}
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)
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)
#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++;
}
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);
#include <stdio.h>
#include <string.h>
+#ifdef _WIN32
+# include <io.h>
+#endif
+
#include "uv.h"
#include "runner.h"
#include "task.h"
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);
}
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)
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)
TEST_ENTRY (process_ref)
TEST_ENTRY (loop_handles)
+ TEST_ENTRY (walk_handles)
TEST_ENTRY (async)
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)
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);
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++) {
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 */
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);
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);
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;
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;
}
+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;
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;
--- /dev/null
+/* 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;
+}
'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',