diff --git a/include/uv-private/uv-unix.h b/include/uv-private/uv-unix.h index f76f6ac4..74b4e81a 100644 --- a/include/uv-private/uv-unix.h +++ b/include/uv-private/uv-unix.h @@ -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; \ diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index 8c048bef..ef58401d 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -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 \ diff --git a/src/unix/loop.c b/src/unix/loop.c index 5672abe9..99353107 100644 --- a/src/unix/loop.c +++ b/src/unix/loop.c @@ -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; diff --git a/src/unix/timer.c b/src/unix/timer.c index 9708dbd8..2c2458d0 100644 --- a/src/unix/timer.c +++ b/src/unix/timer.c @@ -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); diff --git a/src/win/core.c b/src/win/core.c index 67558b49..e56536a7 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -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_; } diff --git a/src/win/timer.c b/src/win/timer.c index 370f2b6d..3ad656f9 100644 --- a/src/win/timer.c +++ b/src/win/timer.c @@ -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); diff --git a/test/test-list.h b/test/test-list.h index 77f1f35d..efc97380 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -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) diff --git a/test/test-timer.c b/test/test-timer.c index b1accb1e..6682ecaa 100644 --- a/test/test-timer.c +++ b/test/test-timer.c @@ -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; +}