windows: add uv_backend_timeout
This commit is contained in:
parent
89fc7d80c4
commit
2e9d86e105
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user