unix,windows: fix timer order in case of same timeout

Compare start_id of timer handles when they have the same timeout.
start_id is allocated with loop->timer_counter in uv_timer_start.
This commit is contained in:
Shigeki Ohtsu 2013-02-07 13:59:17 +09:00 committed by Ben Noordhuis
parent c15d4a7c62
commit fadfeaf6ec
8 changed files with 79 additions and 6 deletions

View File

@ -178,6 +178,7 @@ typedef struct {
uv__io_t signal_io_watcher; \
uv_signal_t child_watcher; \
int emfile_fd; \
uint64_t timer_counter; \
UV_PLATFORM_LOOP_FIELDS \
#define UV_REQ_TYPE_PRIVATE /* empty */
@ -265,7 +266,8 @@ typedef struct {
} tree_entry; \
uv_timer_cb timer_cb; \
uint64_t timeout; \
uint64_t repeat;
uint64_t repeat; \
uint64_t start_id;
#define UV_GETADDRINFO_PRIVATE_FIELDS \
struct uv__work work_req; \

View File

@ -302,7 +302,9 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
/* Counter to keep track of active tcp streams */ \
unsigned int active_tcp_streams; \
/* Counter to keep track of active udp streams */ \
unsigned int active_udp_streams;
unsigned int active_udp_streams; \
/* Counter to started timer */ \
uint64_t timer_counter;
#define UV_REQ_TYPE_PRIVATE \
/* TODO: remove the req suffix */ \
@ -487,6 +489,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
RB_ENTRY(uv_timer_s) tree_entry; \
int64_t due; \
int64_t repeat; \
uint64_t start_id; \
uv_timer_cb timer_cb;
#define UV_ASYNC_PRIVATE_FIELDS \

View File

@ -57,6 +57,8 @@ int uv__loop_init(uv_loop_t* loop, int default_loop) {
loop->backend_fd = -1;
loop->emfile_fd = -1;
loop->timer_counter = 0;
if (uv__platform_loop_init(loop, default_loop))
return -1;

View File

@ -27,9 +27,13 @@ static int uv__timer_cmp(const uv_timer_t* a, const uv_timer_t* b) {
return -1;
if (a->timeout > b->timeout)
return 1;
if (a < b)
/*
* compare start_id when both has the same timeout. start_id is
* allocated with loop->timer_counter in uv_timer_start().
*/
if (a->start_id < b->start_id)
return -1;
if (a > b)
if (a->start_id > b->start_id)
return 1;
return 0;
}
@ -59,6 +63,8 @@ int uv_timer_start(uv_timer_t* handle,
handle->timer_cb = cb;
handle->timeout = handle->loop->time + timeout;
handle->repeat = repeat;
/* start_id is the second index to be compared in uv__timer_cmp() */
handle->start_id = handle->loop->timer_counter++;
RB_INSERT(uv__timers, &handle->loop->timer_handles, handle);
uv__handle_start(handle);

View File

@ -115,6 +115,8 @@ static void uv_loop_init(uv_loop_t* loop) {
loop->active_tcp_streams = 0;
loop->active_udp_streams = 0;
loop->timer_counter = 0;
loop->last_err = uv_ok_;
}

View File

@ -55,9 +55,13 @@ static int uv_timer_compare(uv_timer_t* a, uv_timer_t* b) {
return -1;
if (a->due > b->due)
return 1;
if ((intptr_t)a < (intptr_t)b)
/*
* compare start_id when both has the same due. start_id is
* allocated with loop->timer_counter in uv_timer_start().
*/
if (a->start_id < b->start_id)
return -1;
if ((intptr_t)a > (intptr_t)b)
if (a->start_id > b->start_id)
return 1;
return 0;
}
@ -98,6 +102,9 @@ int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, int64_t timeout,
handle->flags |= UV_HANDLE_ACTIVE;
uv__handle_start(handle);
/* start_id is the second index to be compared in uv__timer_cmp() */
handle->start_id = handle->loop->timer_counter++;
old = RB_INSERT(uv_timer_tree_s, &loop->timers, handle);
assert(old == NULL);

View File

@ -96,6 +96,7 @@ TEST_DECLARE (error_message)
TEST_DECLARE (timer)
TEST_DECLARE (timer_again)
TEST_DECLARE (timer_start_twice)
TEST_DECLARE (timer_order)
TEST_DECLARE (idle_starvation)
TEST_DECLARE (loop_handles)
TEST_DECLARE (get_loadavg)
@ -334,6 +335,7 @@ TASK_LIST_START
TEST_ENTRY (timer)
TEST_ENTRY (timer_again)
TEST_ENTRY (timer_start_twice)
TEST_ENTRY (timer_order)
TEST_ENTRY (idle_starvation)

View File

@ -27,6 +27,7 @@ static int once_cb_called = 0;
static int once_close_cb_called = 0;
static int repeat_cb_called = 0;
static int repeat_close_cb_called = 0;
static int order_cb_called = 0;
static int64_t start_time;
@ -153,3 +154,51 @@ TEST_IMPL(timer_start_twice) {
MAKE_VALGRIND_HAPPY();
return 0;
}
static void order_cb_a(uv_timer_t *handle, int status) {
ASSERT(order_cb_called++ == *(int*)handle->data);
}
static void order_cb_b(uv_timer_t *handle, int status) {
ASSERT(order_cb_called++ == *(int*)handle->data);
}
TEST_IMPL(timer_order) {
int first;
int second;
uv_timer_t handle_a;
uv_timer_t handle_b;
first = 0;
second = 1;
ASSERT(0 == uv_timer_init(uv_default_loop(), &handle_a));
ASSERT(0 == uv_timer_init(uv_default_loop(), &handle_b));
/* Test for starting handle_a then handle_b */
handle_a.data = &first;
ASSERT(0 == uv_timer_start(&handle_a, order_cb_a, 0, 0));
handle_b.data = &second;
ASSERT(0 == uv_timer_start(&handle_b, order_cb_b, 0, 0));
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
ASSERT(order_cb_called == 2);
ASSERT(0 == uv_timer_stop(&handle_a));
ASSERT(0 == uv_timer_stop(&handle_b));
/* Test for starting handle_b then handle_a */
order_cb_called = 0;
handle_b.data = &first;
ASSERT(0 == uv_timer_start(&handle_b, order_cb_b, 0, 0));
handle_a.data = &second;
ASSERT(0 == uv_timer_start(&handle_a, order_cb_a, 0, 0));
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
ASSERT(order_cb_called == 2);
return 0;
}