Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

Commit

Permalink
unix, windows: add uv_walk()
Browse files Browse the repository at this point in the history
Lets the libuv user iterate over the open handles. Mostly intended as a
debugging tool or a post-hoc cleanup mechanism.
  • Loading branch information
bnoordhuis committed May 30, 2012
1 parent 122cd94 commit 171ad85
Show file tree
Hide file tree
Showing 20 changed files with 130 additions and 53 deletions.
9 changes: 9 additions & 0 deletions include/uv.h
Expand Up @@ -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.
Expand Down Expand Up @@ -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. */
Expand All @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions src/unix/core.c
Expand Up @@ -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);
}


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


Expand Down
1 change: 1 addition & 0 deletions src/unix/loop.c
Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions src/uv-common.c
Expand Up @@ -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);
}
Expand Down
10 changes: 6 additions & 4 deletions src/uv-common.h
Expand Up @@ -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_;
Expand Down
6 changes: 1 addition & 5 deletions src/win/async.c
Expand Up @@ -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);
}
}

Expand Down
5 changes: 1 addition & 4 deletions src/win/fs-event.c
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
}
1 change: 1 addition & 0 deletions src/win/handle.c
Expand Up @@ -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++;
}
Expand Down
8 changes: 8 additions & 0 deletions src/win/internal.h
Expand Up @@ -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
Expand Down
5 changes: 1 addition & 4 deletions src/win/loop-watcher.c
Expand Up @@ -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);
}
}

Expand Down
5 changes: 1 addition & 4 deletions src/win/pipe.c
Expand Up @@ -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) {
Expand All @@ -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);
}
}

Expand Down
6 changes: 1 addition & 5 deletions src/win/poll.c
Expand Up @@ -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);
}
6 changes: 1 addition & 5 deletions src/win/process.c
Expand Up @@ -743,18 +743,14 @@ 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);
}
uv__handle_close(handle);
}
}

Expand Down
6 changes: 1 addition & 5 deletions src/win/tcp.c
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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--;
}
}
Expand Down
6 changes: 1 addition & 5 deletions src/win/timer.c
Expand Up @@ -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);
}
}

Expand Down
6 changes: 1 addition & 5 deletions src/win/tty.c
Expand Up @@ -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);
}
}

Expand Down
6 changes: 1 addition & 5 deletions src/win/udp.c
Expand Up @@ -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);
}
}

Expand Down
2 changes: 2 additions & 0 deletions test/test-list.h
Expand Up @@ -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)
Expand Down Expand Up @@ -300,6 +301,7 @@ TASK_LIST_START
TEST_ENTRY (process_ref)

TEST_ENTRY (loop_handles)
TEST_ENTRY (walk_handles)

TEST_ENTRY (async)

Expand Down
77 changes: 77 additions & 0 deletions 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 <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 change: 1 addition & 0 deletions uv.gyp
Expand Up @@ -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',
Expand Down

0 comments on commit 171ad85

Please sign in to comment.