diff --git a/src/win/core.c b/src/win/core.c index 7a457875..c3959756 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -45,8 +45,9 @@ static uv_once_t uv_default_loop_init_guard_ = UV_ONCE_INIT; #if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)) -/* Our crt debug report handler allows us to temporarily disable asserts */ -/* just for the current thread. */ +/* Our crt debug report handler allows us to temporarily disable asserts + * just for the current thread. + */ UV_THREAD_LOCAL int uv__crt_assert_enabled = TRUE; @@ -55,8 +56,9 @@ static int uv__crt_dbg_report_handler(int report_type, char *message, int *ret_v return FALSE; if (ret_val) { - /* Set ret_val to 0 to continue with normal execution. */ - /* Set ret_val to 1 to trigger a breakpoint. */ + /* Set ret_val to 0 to continue with normal execution. + * Set ret_val to 1 to trigger a breakpoint. + */ if(IsDebuggerPresent()) *ret_val = 1; @@ -86,21 +88,24 @@ static void uv_init(void) { SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); - /* Tell the CRT to not exit the application when an invalid parameter is */ - /* passed. The main issue is that invalid FDs will trigger this behavior. */ + /* Tell the CRT to not exit the application when an invalid parameter is + * passed. The main issue is that invalid FDs will trigger this behavior. + */ #if !defined(__MINGW32__) || __MSVCRT_VERSION__ >= 0x800 _set_invalid_parameter_handler(uv__crt_invalid_parameter_handler); #endif - /* We also need to setup our debug report handler because some CRT */ - /* functions (eg _get_osfhandle) raise an assert when called with invalid */ - /* FDs even though they return the proper error code in the release build. */ + /* We also need to setup our debug report handler because some CRT + * functions (eg _get_osfhandle) raise an assert when called with invalid + * FDs even though they return the proper error code in the release build. + */ #if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)) _CrtSetReportHook(uv__crt_dbg_report_handler); #endif - /* Fetch winapi function pointers. This must be done first because other */ - /* intialization code might need these function pointers to be loaded. */ + /* Fetch winapi function pointers. This must be done first because other + * intialization code might need these function pointers to be loaded. + */ uv_winapi_init(); /* Initialize winsock */ @@ -129,8 +134,9 @@ int uv_loop_init(uv_loop_t* loop) { if (loop->iocp == NULL) return uv_translate_sys_error(GetLastError()); - /* To prevent uninitialized memory access, loop->time must be intialized */ - /* to zero before calling uv_update_time for the first time. */ + /* To prevent uninitialized memory access, loop->time must be intialized + * to zero before calling uv_update_time for the first time. + */ loop->time = 0; loop->last_tick_count = 0; uv_update_time(loop); @@ -272,22 +278,31 @@ int uv_backend_fd(const uv_loop_t* loop) { int uv_backend_timeout(const uv_loop_t* loop) { - return 0; + if (loop->stop_flag != 0) + return 0; + + if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop)) + return 0; + + if (loop->pending_reqs_tail) + return 0; + + if (loop->endgame_handles) + return 0; + + if (loop->idle_handles) + return 0; + + return uv__next_timeout(loop); } -static void uv_poll(uv_loop_t* loop, int block) { - DWORD bytes, timeout; +static void uv_poll(uv_loop_t* loop, DWORD timeout) { + DWORD bytes; ULONG_PTR key; OVERLAPPED* overlapped; uv_req_t* req; - if (block) { - timeout = uv_get_poll_timeout(loop); - } else { - timeout = 0; - } - GetQueuedCompletionStatus(loop->iocp, &bytes, &key, @@ -302,28 +317,22 @@ static void uv_poll(uv_loop_t* loop, int block) { /* Serious error */ uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus"); } else { - /* We're sure that at least `timeout` milliseconds have expired, but */ - /* this may not be reflected yet in the GetTickCount() return value. */ - /* Therefore we ensure it's taken into account here. */ + /* We're sure that at least `timeout` milliseconds have expired, but + * this may not be reflected yet in the GetTickCount() return value. + * Therefore we ensure it's taken into account here. + */ uv__time_forward(loop, timeout); } } -static void uv_poll_ex(uv_loop_t* loop, int block) { +static void uv_poll_ex(uv_loop_t* loop, DWORD timeout) { BOOL success; - DWORD timeout; uv_req_t* req; OVERLAPPED_ENTRY overlappeds[128]; ULONG count; ULONG i; - if (block) { - timeout = uv_get_poll_timeout(loop); - } else { - timeout = 0; - } - success = pGetQueuedCompletionStatusEx(loop->iocp, overlappeds, ARRAY_SIZE(overlappeds), @@ -341,9 +350,10 @@ static void uv_poll_ex(uv_loop_t* loop, int block) { /* Serious error */ uv_fatal_error(GetLastError(), "GetQueuedCompletionStatusEx"); } else if (timeout > 0) { - /* We're sure that at least `timeout` milliseconds have expired, but */ - /* this may not be reflected yet in the GetTickCount() return value. */ - /* Therefore we ensure it's taken into account here. */ + /* We're sure that at least `timeout` milliseconds have expired, but + * this may not be reflected yet in the GetTickCount() return value. + * Therefore we ensure it's taken into account here. + */ uv__time_forward(loop, timeout); } } @@ -362,8 +372,9 @@ int uv_loop_alive(const uv_loop_t* loop) { int uv_run(uv_loop_t *loop, uv_run_mode mode) { + DWORD timeout; int r; - void (*poll)(uv_loop_t* loop, int block); + void (*poll)(uv_loop_t* loop, DWORD timeout); if (pGetQueuedCompletionStatusEx) poll = &uv_poll_ex; @@ -382,13 +393,11 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { uv_idle_invoke(loop); uv_prepare_invoke(loop); - (*poll)(loop, loop->idle_handles == NULL && - loop->pending_reqs_tail == NULL && - loop->endgame_handles == NULL && - !loop->stop_flag && - (loop->active_handles > 0 || - !QUEUE_EMPTY(&loop->active_reqs)) && - !(mode & UV_RUN_NOWAIT)); + timeout = 0; + if ((mode & UV_RUN_NOWAIT) == 0) + timeout = uv_backend_timeout(loop); + + (*poll)(loop, timeout); uv_check_invoke(loop); uv_process_endgames(loop); diff --git a/src/win/internal.h b/src/win/internal.h index 99bc01e9..9eadb712 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -239,7 +239,7 @@ void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle); */ void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle); -DWORD uv_get_poll_timeout(uv_loop_t* loop); +DWORD uv__next_timeout(const uv_loop_t* loop); void uv__time_forward(uv_loop_t* loop, uint64_t msecs); void uv_process_timers(uv_loop_t* loop); diff --git a/src/win/timer.c b/src/win/timer.c index 16a2fc5f..c229d4c8 100644 --- a/src/win/timer.c +++ b/src/win/timer.c @@ -36,10 +36,11 @@ void uv_update_time(uv_loop_t* loop) { time.QuadPart = loop->time; - /* GetTickCount() can conceivably wrap around, so when the current tick */ - /* count is lower than the last tick count, we'll assume it has wrapped. */ - /* uv_poll must make sure that the timer can never overflow more than */ - /* once between two subsequent uv_update_time calls. */ + /* GetTickCount() can conceivably wrap around, so when the current tick + * count is lower than the last tick count, we'll assume it has wrapped. + * uv_poll must make sure that the timer can never overflow more than + * once between two subsequent uv_update_time calls. + */ time.LowPart = ticks; if (ticks < loop->last_tick_count) time.HighPart++; @@ -47,13 +48,14 @@ void uv_update_time(uv_loop_t* loop) { /* Remember the last tick count. */ loop->last_tick_count = ticks; - /* The GetTickCount() resolution isn't too good. Sometimes it'll happen */ - /* that GetQueuedCompletionStatus() or GetQueuedCompletionStatusEx() has */ - /* waited for a couple of ms but this is not reflected in the GetTickCount */ - /* result yet. Therefore whenever GetQueuedCompletionStatus times out */ - /* we'll add the number of ms that it has waited to the current loop time. */ - /* When that happened the loop time might be a little ms farther than what */ - /* we've just computed, and we shouldn't update the loop time. */ + /* The GetTickCount() resolution isn't too good. Sometimes it'll happen + * that GetQueuedCompletionStatus() or GetQueuedCompletionStatusEx() has + * waited for a couple of ms but this is not reflected in the GetTickCount + * result yet. Therefore whenever GetQueuedCompletionStatus times out + * we'll add the number of ms that it has waited to the current loop time. + * When that happened the loop time might be a little ms farther than what + * we've just computed, and we shouldn't update the loop time. + */ if (loop->time < time.QuadPart) loop->time = time.QuadPart; } @@ -193,24 +195,26 @@ uint64_t uv_timer_get_repeat(const uv_timer_t* handle) { } -DWORD uv_get_poll_timeout(uv_loop_t* loop) { +DWORD uv__next_timeout(const uv_loop_t* loop) { uv_timer_t* timer; int64_t delta; - /* Check if there are any running timers */ - timer = RB_MIN(uv_timer_tree_s, &loop->timers); + /* Check if there are any running timers + * Need to cast away const first, since RB_MIN doesn't know what we are + * going to do with this return value, it can't be marked const + */ + timer = RB_MIN(uv_timer_tree_s, &((uv_loop_t*)loop)->timers); if (timer) { - uv_update_time(loop); - delta = timer->due - loop->time; if (delta >= UINT_MAX >> 1) { - /* A timeout value of UINT_MAX means infinite, so that's no good. But */ - /* more importantly, there's always the risk that GetTickCount wraps. */ - /* uv_update_time can detect this, but we must make sure that the */ - /* tick counter never overflows twice between two subsequent */ - /* uv_update_time calls. We do this by never sleeping more than half */ - /* the time it takes to wrap the counter - which is huge overkill, */ - /* but hey, it's not so bad to wake up every 25 days. */ + /* A timeout value of UINT_MAX means infinite, so that's no good. But + * more importantly, there's always the risk that GetTickCount wraps. + * uv_update_time can detect this, but we must make sure that the + * tick counter never overflows twice between two subsequent + * uv_update_time calls. We do this by never sleeping more than half + * the time it takes to wrap the counter - which is huge overkill, + * but hey, it's not so bad to wake up every 25 days. + */ return UINT_MAX >> 1; } else if (delta < 0) { /* Negative timeout values are not allowed */ diff --git a/test/test-list.h b/test/test-list.h index d9443b8a..3411c09a 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -28,6 +28,7 @@ TEST_DECLARE (loop_alive) TEST_DECLARE (loop_close) TEST_DECLARE (loop_stop) TEST_DECLARE (loop_update_time) +TEST_DECLARE (loop_backend_timeout) TEST_DECLARE (barrier_1) TEST_DECLARE (barrier_2) TEST_DECLARE (barrier_3) @@ -291,6 +292,7 @@ TASK_LIST_START TEST_ENTRY (loop_close) TEST_ENTRY (loop_stop) TEST_ENTRY (loop_update_time) + TEST_ENTRY (loop_backend_timeout) TEST_ENTRY (barrier_1) TEST_ENTRY (barrier_2) TEST_ENTRY (barrier_3) diff --git a/test/test-loop-time.c b/test/test-loop-time.c index 49dc79b2..a2db42cc 100644 --- a/test/test-loop-time.c +++ b/test/test-loop-time.c @@ -30,5 +30,34 @@ TEST_IMPL(loop_update_time) { while (uv_now(uv_default_loop()) - start < 1000) ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_NOWAIT)); + MAKE_VALGRIND_HAPPY(); + return 0; +} + +static void cb(uv_timer_t* timer) { + uv_close((uv_handle_t*)timer, NULL); +} + +TEST_IMPL(loop_backend_timeout) { + uv_loop_t *loop = uv_default_loop(); + uv_timer_t timer; + int r; + + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + + ASSERT(!uv_loop_alive(loop)); + ASSERT(uv_backend_timeout(loop) == 0); + + r = uv_timer_start(&timer, cb, 1000, 0); /* 1 sec */ + ASSERT(r == 0); + ASSERT(uv_backend_timeout(loop) > 100); /* 0.1 sec */ + ASSERT(uv_backend_timeout(loop) <= 1000); /* 1 sec */ + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + ASSERT(uv_backend_timeout(loop) == 0); + + MAKE_VALGRIND_HAPPY(); return 0; }