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.
This commit is contained in:
Ben Noordhuis 2012-05-30 01:51:28 +02:00
parent 122cd94695
commit 171ad8567d
20 changed files with 130 additions and 53 deletions

View File

@ -301,6 +301,7 @@ typedef void (*uv_exit_cb)(uv_process_t*, int exit_status, int term_signal);
typedef void (*uv_fs_cb)(uv_fs_t* req);
typedef void (*uv_work_cb)(uv_work_t* req);
typedef void (*uv_after_work_cb)(uv_work_t* req);
typedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg);
/*
* This will be called repeatedly after the uv_fs_event_t is initialized.
@ -382,6 +383,7 @@ struct uv_shutdown_s {
/* read-only */ \
uv_handle_type type; \
/* private */ \
ngx_queue_t handle_queue; \
UV_HANDLE_PRIVATE_FIELDS \
/* The abstract base class of all handles. */
@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -317,6 +317,18 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
}
void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) {
ngx_queue_t* q;
uv_handle_t* h;
ngx_queue_foreach(q, &loop->handle_queue) {
h = ngx_queue_data(q, uv_handle_t, handle_queue);
if (h->flags & UV__HANDLE_INTERNAL) continue;
walk_cb(h, arg);
}
}
void uv_ref(uv_handle_t* handle) {
uv__handle_ref(handle);
}

View File

@ -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_;

View File

@ -58,12 +58,8 @@ void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) {
if (handle->flags & UV_HANDLE_CLOSING &&
!handle->async_sent) {
assert(!(handle->flags & UV_HANDLE_CLOSED));
handle->flags |= UV_HANDLE_CLOSED;
uv__handle_stop(handle);
if (handle->close_cb) {
handle->close_cb((uv_handle_t*)handle);
}
uv__handle_close(handle);
}
}

View File

@ -479,7 +479,6 @@ void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
if (handle->flags & UV_HANDLE_CLOSING &&
!handle->req_pending) {
assert(!(handle->flags & UV_HANDLE_CLOSED));
handle->flags |= UV_HANDLE_CLOSED;
uv__handle_stop(handle);
if (handle->buffer) {
@ -507,8 +506,6 @@ void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
handle->dirw = NULL;
}
if (handle->close_cb) {
handle->close_cb((uv_handle_t*)handle);
}
uv__handle_close(handle);
}
}

View File

@ -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++;
}

View File

@ -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

View File

@ -30,10 +30,7 @@ void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) {
assert(!(handle->flags & UV_HANDLE_CLOSED));
handle->flags |= UV_HANDLE_CLOSED;
uv__handle_stop(handle);
if (handle->close_cb) {
handle->close_cb(handle);
}
uv__handle_close(handle);
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -197,7 +197,6 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
if (handle->flags & UV_HANDLE_CLOSING &&
handle->reqs_pending == 0) {
assert(!(handle->flags & UV_HANDLE_CLOSED));
handle->flags |= UV_HANDLE_CLOSED;
uv__handle_stop(handle);
if (!(handle->flags & UV_HANDLE_TCP_SOCKET_CLOSED)) {
@ -236,10 +235,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
}
}
if (handle->close_cb) {
handle->close_cb((uv_handle_t*)handle);
}
uv__handle_close(handle);
loop->active_tcp_streams--;
}
}

View File

@ -126,12 +126,8 @@ int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle) {
if (handle->flags & UV_HANDLE_CLOSING) {
assert(!(handle->flags & UV_HANDLE_CLOSED));
handle->flags |= UV_HANDLE_CLOSED;
uv__handle_stop(handle);
if (handle->close_cb) {
handle->close_cb((uv_handle_t*)handle);
}
uv__handle_close(handle);
}
}

View File

@ -1773,12 +1773,8 @@ void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
assert(handle->read_raw_wait == NULL);
assert(!(handle->flags & UV_HANDLE_CLOSED));
handle->flags |= UV_HANDLE_CLOSED;
uv__handle_stop(handle);
if (handle->close_cb) {
handle->close_cb((uv_handle_t*)handle);
}
uv__handle_close(handle);
}
}

View File

@ -155,12 +155,8 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) {
if (handle->flags & UV_HANDLE_CLOSING &&
handle->reqs_pending == 0) {
assert(!(handle->flags & UV_HANDLE_CLOSED));
handle->flags |= UV_HANDLE_CLOSED;
uv__handle_stop(handle);
if (handle->close_cb) {
handle->close_cb((uv_handle_t*)handle);
}
uv__handle_close(handle);
}
}

View File

@ -81,6 +81,7 @@ TEST_DECLARE (timer_start_twice)
TEST_DECLARE (idle_starvation)
TEST_DECLARE (loop_handles)
TEST_DECLARE (get_loadavg)
TEST_DECLARE (walk_handles)
TEST_DECLARE (ref)
TEST_DECLARE (idle_ref)
TEST_DECLARE (async_ref)
@ -300,6 +301,7 @@ TASK_LIST_START
TEST_ENTRY (process_ref)
TEST_ENTRY (loop_handles)
TEST_ENTRY (walk_handles)
TEST_ENTRY (async)

77
test/test-walk-handles.c Normal file
View File

@ -0,0 +1,77 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "task.h"
#include <stdio.h>
#include <stdlib.h>
static char magic_cookie[] = "magic cookie";
static int seen_timer_handle;
static uv_timer_t timer;
static void walk_cb(uv_handle_t* handle, void* arg) {
ASSERT(arg == (void*)magic_cookie);
if (handle == (uv_handle_t*)&timer) {
seen_timer_handle++;
} else {
ASSERT(0 && "unexpected handle");
}
}
static void timer_cb(uv_timer_t* handle, int status) {
ASSERT(handle == &timer);
ASSERT(status == 0);
uv_walk(handle->loop, walk_cb, magic_cookie);
uv_close((uv_handle_t*)handle, NULL);
}
TEST_IMPL(walk_handles) {
uv_loop_t* loop;
int r;
loop = uv_default_loop();
r = uv_timer_init(loop, &timer);
ASSERT(r == 0);
r = uv_timer_start(&timer, timer_cb, 1, 0);
ASSERT(r == 0);
/* Start event loop, expect to see the timer handle in walk_cb. */
ASSERT(seen_timer_handle == 0);
r = uv_run(loop);
ASSERT(r == 0);
ASSERT(seen_timer_handle == 1);
/* Loop is finished, walk_cb should not see our timer handle. */
seen_timer_handle = 0;
uv_walk(loop, walk_cb, magic_cookie);
ASSERT(seen_timer_handle == 0);
return 0;
}

1
uv.gyp
View File

@ -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',