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

Commit

Permalink
Browse files Browse the repository at this point in the history
unix, windows: rework reference counting scheme
This commit changes how the event loop determines if it needs to stay alive.

Previously, an internal counter was increased whenever a handle got created
and decreased again when the handle was closed.

While conceptually simple, it turned out hard to work with: you often want
to keep the event loop alive only if the handle is actually doing something.
Stopped or inactive handles were a frequent source of hanging event loops.

That's why this commit changes the reference counting scheme to a model where
a handle only references the event loop when it's active. 'Active' means
different things for different handle types, e.g.:

 * timers: ticking
 * sockets: reading, writing or listening
 * processes: always active (for now, subject to change)
 * idle, check, prepare: only active when started

This commit also changes how the uv_ref() and uv_unref() functions work: they
now operate on the level of individual handles, not the whole event loop.

The Windows implementation was done by Bert Belder.
  • Loading branch information
bnoordhuis committed May 17, 2012
1 parent 07622e7 commit 9efa8b3
Show file tree
Hide file tree
Showing 60 changed files with 1,002 additions and 966 deletions.
3 changes: 0 additions & 3 deletions config-unix.mk
Expand Up @@ -29,16 +29,13 @@ CPPFLAGS += -D_FILE_OFFSET_BITS=64

OBJS += src/unix/async.o
OBJS += src/unix/cares.o
OBJS += src/unix/check.o
OBJS += src/unix/core.o
OBJS += src/unix/dl.o
OBJS += src/unix/error.o
OBJS += src/unix/fs.o
OBJS += src/unix/idle.o
OBJS += src/unix/loop.o
OBJS += src/unix/pipe.o
OBJS += src/unix/poll.o
OBJS += src/unix/prepare.o
OBJS += src/unix/process.o
OBJS += src/unix/stream.o
OBJS += src/unix/tcp.o
Expand Down
19 changes: 11 additions & 8 deletions include/uv-private/uv-unix.h
Expand Up @@ -104,7 +104,10 @@ typedef struct {
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* endgame_handles; \
uv_handle_t* pending_handles; \
ngx_queue_t prepare_handles; \
ngx_queue_t check_handles; \
ngx_queue_t idle_handles; \
UV_LOOP_PRIVATE_PLATFORM_FIELDS

#define UV_REQ_BUFSML_SIZE (4)
Expand Down Expand Up @@ -141,7 +144,7 @@ typedef struct {
#define UV_HANDLE_PRIVATE_FIELDS \
int fd; \
int flags; \
uv_handle_t* endgame_next; /* that's what uv-win calls it */ \
uv_handle_t* next_pending; \


#define UV_STREAM_PRIVATE_FIELDS \
Expand Down Expand Up @@ -183,20 +186,20 @@ typedef struct {

/* UV_PREPARE */ \
#define UV_PREPARE_PRIVATE_FIELDS \
ev_prepare prepare_watcher; \
uv_prepare_cb prepare_cb;
uv_prepare_cb prepare_cb; \
ngx_queue_t queue;


/* UV_CHECK */
#define UV_CHECK_PRIVATE_FIELDS \
ev_check check_watcher; \
uv_check_cb check_cb;
uv_check_cb check_cb; \
ngx_queue_t queue;


/* UV_IDLE */
#define UV_IDLE_PRIVATE_FIELDS \
ev_idle idle_watcher; \
uv_idle_cb idle_cb;
uv_idle_cb idle_cb; \
ngx_queue_t queue;


/* UV_ASYNC */
Expand Down
7 changes: 3 additions & 4 deletions include/uv-private/uv-win.h
Expand Up @@ -32,6 +32,7 @@
#include <sys/stat.h>

#include "tree.h"
#include "ngx-queue.h"

#define MAX_PIPENAME_LEN 256

Expand Down Expand Up @@ -203,8 +204,6 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
#define UV_LOOP_PRIVATE_FIELDS \
/* The loop's I/O completion port */ \
HANDLE iocp; \
/* Reference count that keeps the event loop alive */ \
int refs; \
/* The current time according to the event loop. in msecs. */ \
int64_t time; \
/* Tail of a single-linked circular queue of pending reqs. If the queue */ \
Expand Down Expand Up @@ -246,7 +245,6 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
UV_ARES_EVENT_REQ, \
UV_ARES_CLEANUP_REQ, \
UV_FS_EVENT_REQ, \
UV_GETADDRINFO_REQ, \
UV_POLL_REQ, \
UV_PROCESS_EXIT, \
UV_PROCESS_CLOSE, \
Expand Down Expand Up @@ -310,6 +308,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);

#define UV_STREAM_PRIVATE_FIELDS \
unsigned int reqs_pending; \
int activecnt; \
uv_read_t read_req; \
union { \
struct { uv_stream_connection_fields }; \
Expand Down Expand Up @@ -337,6 +336,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
#define UV_UDP_PRIVATE_FIELDS \
SOCKET socket; \
unsigned int reqs_pending; \
int activecnt; \
uv_req_t recv_req; \
uv_buf_t recv_buffer; \
struct sockaddr_storage recv_from; \
Expand Down Expand Up @@ -444,7 +444,6 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
unsigned int flags;

#define UV_GETADDRINFO_PRIVATE_FIELDS \
struct uv_req_s getadddrinfo_req; \
uv_getaddrinfo_cb getaddrinfo_cb; \
void* alloc; \
wchar_t* node; \
Expand Down
28 changes: 22 additions & 6 deletions include/uv.h
Expand Up @@ -224,10 +224,6 @@ typedef struct uv_work_s uv_work_t;
UV_EXTERN uv_loop_t* uv_loop_new(void);
UV_EXTERN void uv_loop_delete(uv_loop_t*);

/* This is a debugging tool. It's NOT part of the official API. */
UV_EXTERN int uv_loop_refcount(const uv_loop_t*);


/*
* Returns the default loop.
*/
Expand All @@ -248,8 +244,8 @@ UV_EXTERN int uv_run_once (uv_loop_t*);
* Manually modify the event loop's reference count. Useful if the user wants
* to have a handle or timeout that doesn't keep the loop alive.
*/
UV_EXTERN void uv_ref(uv_loop_t*);
UV_EXTERN void uv_unref(uv_loop_t*);
UV_EXTERN void uv_ref(uv_handle_t*);
UV_EXTERN void uv_unref(uv_handle_t*);

UV_EXTERN void uv_update_time(uv_loop_t*);
UV_EXTERN int64_t uv_now(uv_loop_t*);
Expand Down Expand Up @@ -336,12 +332,18 @@ UV_EXTERN uv_err_t uv_last_error(uv_loop_t*);
UV_EXTERN const char* uv_strerror(uv_err_t err);
UV_EXTERN const char* uv_err_name(uv_err_t err);

#ifndef UV_LEAN_AND_MEAN
# define UV_REQ_EXTRA_FIELDS ngx_queue_t active_queue;
#else
# define UV_REQ_EXTRA_FIELDS
#endif

#define UV_REQ_FIELDS \
/* read-only */ \
uv_req_type type; \
/* public */ \
void* data; \
UV_REQ_EXTRA_FIELDS \
/* private */ \
UV_REQ_PRIVATE_FIELDS

Expand Down Expand Up @@ -374,13 +376,20 @@ struct uv_shutdown_s {
};


#ifndef UV_LEAN_AND_MEAN
# define UV_HANDLE_EXTRA_FIELDS ngx_queue_t active_queue;
#else
# define UV_HANDLE_EXTRA_FIELDS
#endif

#define UV_HANDLE_FIELDS \
/* read-only */ \
uv_loop_t* loop; \
uv_handle_type type; \
/* public */ \
uv_close_cb close_cb; \
void* data; \
UV_HANDLE_EXTRA_FIELDS \
/* private */ \
UV_HANDLE_PRIVATE_FIELDS

Expand Down Expand Up @@ -1638,6 +1647,13 @@ struct uv_loop_s {
uv_err_t last_err;
/* User data - use this for whatever. */
void* data;
#ifndef UV_LEAN_AND_MEAN
ngx_queue_t active_reqs;
ngx_queue_t active_handles;
#else
unsigned int active_reqs;
unsigned int active_handles;
#endif
};


Expand Down
6 changes: 4 additions & 2 deletions src/unix/async.c
Expand Up @@ -40,7 +40,8 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb) {

/* Note: This does not have symmetry with the other libev wrappers. */
ev_async_start(loop->ev, &async->async_watcher);
ev_unref(loop->ev);
uv__handle_unref(async);
uv__handle_start(async);

return 0;
}
Expand All @@ -54,5 +55,6 @@ int uv_async_send(uv_async_t* async) {

void uv__async_close(uv_async_t* handle) {
ev_async_stop(handle->loop->ev, &handle->async_watcher);
ev_ref(handle->loop->ev);
uv__handle_ref(handle);
uv__handle_stop(handle);
}
23 changes: 4 additions & 19 deletions src/unix/cares.c
Expand Up @@ -37,20 +37,6 @@ static void uv__ares_timeout(uv_timer_t* handle, int status) {
}


static void uv__ares_timer_start(uv_loop_t* loop) {
if (uv_is_active((uv_handle_t*)&loop->timer)) return;
uv_timer_start(&loop->timer, uv__ares_timeout, 1000, 1000);
uv_ref(loop);
}


static void uv__ares_timer_stop(uv_loop_t* loop) {
if (!uv_is_active((uv_handle_t*)&loop->timer)) return;
uv_timer_stop(&loop->timer);
uv_unref(loop);
}


static void uv__ares_io(struct ev_loop* ev, struct ev_io* watcher,
int revents) {
uv_loop_t* loop = ev_userdata(ev);
Expand Down Expand Up @@ -104,9 +90,9 @@ static void uv__ares_sockstate_cb(void* data, ares_socket_t sock,
/* New socket */

/* If this is the first socket then start the timer. */
if (!uv_is_active((uv_handle_t*)&loop->timer)) {
if (!uv__is_active(&loop->timer)) {
assert(uv_ares_handles_empty(loop));
uv__ares_timer_start(loop);
uv_timer_start(&loop->timer, uv__ares_timeout, 1000, 1000);
}

h = uv__ares_task_create(loop, sock);
Expand Down Expand Up @@ -140,7 +126,7 @@ static void uv__ares_sockstate_cb(void* data, ares_socket_t sock,
free(h);

if (uv_ares_handles_empty(loop)) {
uv__ares_timer_stop(loop);
uv_timer_stop(&loop->timer);
}
}
}
Expand Down Expand Up @@ -176,7 +162,6 @@ int uv_ares_init_options(uv_loop_t* loop, ares_channel *channelptr,
* first socket is opened.
*/
uv_timer_init(loop, &loop->timer);
uv_unref(loop);
loop->timer.data = loop;

return rc;
Expand All @@ -187,7 +172,7 @@ int uv_ares_init_options(uv_loop_t* loop, ares_channel *channelptr,
void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) {
/* only allow destroy if did init */
if (loop->channel) {
uv__ares_timer_stop(loop);
uv_timer_stop(&loop->timer);
ares_destroy(channel);
loop->channel = NULL;
}
Expand Down
80 changes: 0 additions & 80 deletions src/unix/check.c

This file was deleted.

1 comment on commit 9efa8b3

@piscisaureus
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • timers: ticking
  • sockets: reading, writing or listening

Add: connecting, shutting down

  • processes: always active (for now, subject to change)

This is not the case on windows atm: process handles are active until the process exits, or the process handle is closed.

  • idle, check, prepare: only active when started

Also:

  • poll handles are active when the event mask is nonzero
  • async and fs_event are always active

Please sign in to comment.