From 171ad8567da60bbc61d159f67e45da934db7228e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 30 May 2012 01:51:28 +0200 Subject: [PATCH] unix, windows: add uv_walk() Lets the libuv user iterate over the open handles. Mostly intended as a debugging tool or a post-hoc cleanup mechanism. --- include/uv.h | 9 +++++ src/unix/core.c | 5 +-- src/unix/loop.c | 1 + src/uv-common.c | 12 +++++++ src/uv-common.h | 10 +++--- src/win/async.c | 6 +--- src/win/fs-event.c | 5 +-- src/win/handle.c | 1 + src/win/internal.h | 8 +++++ src/win/loop-watcher.c | 5 +-- src/win/pipe.c | 5 +-- src/win/poll.c | 6 +--- src/win/process.c | 6 +--- src/win/tcp.c | 6 +--- src/win/timer.c | 6 +--- src/win/tty.c | 6 +--- src/win/udp.c | 6 +--- test/test-list.h | 2 ++ test/test-walk-handles.c | 77 ++++++++++++++++++++++++++++++++++++++++ uv.gyp | 1 + 20 files changed, 130 insertions(+), 53 deletions(-) create mode 100644 test/test-walk-handles.c diff --git a/include/uv.h b/include/uv.h index f9ffbaf4..fd1f7d38 100644 --- a/include/uv.h +++ b/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. */ @@ -407,6 +409,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. @@ -1668,6 +1676,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/src/unix/core.c b/src/unix/core.c index 6c5e8e61..e4e909db 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -159,12 +159,12 @@ static void uv__finish_close(uv_handle_t* handle) { break; } + uv__handle_unref(handle); + ngx_queue_remove(&handle->handle_queue); if (handle->close_cb) { handle->close_cb(handle); } - - uv__handle_unref(handle); } @@ -278,6 +278,7 @@ void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, handle->type = type; handle->flags = UV__HANDLE_REF; /* ref the loop when active */ handle->next_closing = NULL; + ngx_queue_insert_tail(&loop->handle_queue, &handle->handle_queue); } diff --git a/src/unix/loop.c b/src/unix/loop.c index 43a92b69..43f7e810 100644 --- a/src/unix/loop.c +++ b/src/unix/loop.c @@ -41,6 +41,7 @@ int uv__loop_init(uv_loop_t* loop, int default_loop) { ngx_queue_init(&loop->idle_handles); ngx_queue_init(&loop->check_handles); ngx_queue_init(&loop->prepare_handles); + ngx_queue_init(&loop->handle_queue); loop->closing_handles = NULL; loop->channel = NULL; loop->ev = (default_loop ? ev_default_loop : ev_loop_new)(flags); diff --git a/src/uv-common.c b/src/uv-common.c index e81f7a31..761133a6 100644 --- a/src/uv-common.c +++ b/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/src/uv-common.h b/src/uv-common.h index df2a647b..4a721ef9 100644 --- a/src/uv-common.h +++ b/src/uv-common.h @@ -49,12 +49,14 @@ #ifndef _WIN32 enum { - UV__HANDLE_ACTIVE = 0x4000, - UV__HANDLE_REF = 0x8000 + UV__HANDLE_INTERNAL = 0x8000, + UV__HANDLE_ACTIVE = 0x4000, + UV__HANDLE_REF = 0x2000 }; #else -# define UV__HANDLE_ACTIVE 0x40 -# define UV__HANDLE_REF 0x20 +# define UV__HANDLE_INTERNAL 0x80 +# define UV__HANDLE_ACTIVE 0x40 +# define UV__HANDLE_REF 0x20 #endif extern const uv_err_t uv_ok_; diff --git a/src/win/async.c b/src/win/async.c index 9df75e08..87f55296 100644 --- a/src/win/async.c +++ b/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/src/win/fs-event.c b/src/win/fs-event.c index 61f9de23..30a571a7 100644 --- a/src/win/fs-event.c +++ b/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/src/win/handle.c b/src/win/handle.c index 5f8b2408..a3abb0ab 100644 --- a/src/win/handle.c +++ b/src/win/handle.c @@ -65,6 +65,7 @@ int uv_is_active(const uv_handle_t* handle) { void uv_handle_init(uv_loop_t* loop, uv_handle_t* handle) { handle->loop = loop; handle->flags = UV__HANDLE_REF; + ngx_queue_insert_tail(&loop->handle_queue, &handle->handle_queue); loop->counters.handle_init++; } diff --git a/src/win/internal.h b/src/win/internal.h index e505292c..78762261 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -130,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/src/win/loop-watcher.c b/src/win/loop-watcher.c index 46d184ca..c573cd7d 100644 --- a/src/win/loop-watcher.c +++ b/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/src/win/pipe.c b/src/win/pipe.c index 2b48e38f..384e9b44 100644 --- a/src/win/pipe.c +++ b/src/win/pipe.c @@ -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/src/win/poll.c b/src/win/poll.c index c6d68ffd..f5e271f3 100644 --- a/src/win/poll.c +++ b/src/win/poll.c @@ -611,10 +611,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/src/win/process.c b/src/win/process.c index e3d8a1fd..d0a81944 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -743,7 +743,6 @@ 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. */ @@ -751,10 +750,7 @@ void uv_process_endgame(uv_loop_t* loop, uv_process_t* 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); - } + uv__handle_close(handle); } } diff --git a/src/win/tcp.c b/src/win/tcp.c index fd956a09..c049d64d 100644 --- a/src/win/tcp.c +++ b/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/src/win/timer.c b/src/win/timer.c index fbde888d..e71e4c65 100644 --- a/src/win/timer.c +++ b/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/src/win/tty.c b/src/win/tty.c index bd46215e..07159922 100644 --- a/src/win/tty.c +++ b/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/src/win/udp.c b/src/win/udp.c index ddd08a26..3ea23f9d 100644 --- a/src/win/udp.c +++ b/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/test/test-list.h b/test/test-list.h index ffb1fe23..9358d56d 100644 --- a/test/test-list.h +++ b/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) @@ -300,6 +301,7 @@ TASK_LIST_START TEST_ENTRY (process_ref) TEST_ENTRY (loop_handles) + TEST_ENTRY (walk_handles) TEST_ENTRY (async) diff --git a/test/test-walk-handles.c b/test/test-walk-handles.c new file mode 100644 index 00000000..a72c095b --- /dev/null +++ b/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/uv.gyp b/uv.gyp index f53e18ec..47f87670 100644 --- a/uv.gyp +++ b/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',