diff --git a/src/unix/core.c b/src/unix/core.c index 2d193ecc..3eb45da4 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -317,8 +317,21 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) { uv__io_poll(loop, timeout); uv__run_check(loop); uv__run_closing_handles(loop); - r = uv__loop_alive(loop); + if (mode == UV_RUN_ONCE) { + /* UV_RUN_ONCE implies forward progess: at least one callback must have + * been invoked when it returns. uv__io_poll() can return without doing + * I/O (meaning: no callbacks) when its timeout expires - which means we + * have pending timers that satisfy the forward progress constraint. + * + * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from + * the check. + */ + uv__update_time(loop); + uv__run_timers(loop); + } + + r = uv__loop_alive(loop); UV_TICK_STOP(loop, mode); if (mode & (UV_RUN_ONCE | UV_RUN_NOWAIT)) diff --git a/src/win/core.c b/src/win/core.c index 0cf1bbbb..9b777464 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -298,6 +298,20 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { !(mode & UV_RUN_NOWAIT)); uv_check_invoke(loop); + + if (mode == UV_RUN_ONCE) { + /* UV_RUN_ONCE implies forward progess: at least one callback must have + * been invoked when it returns. uv__io_poll() can return without doing + * I/O (meaning: no callbacks) when its timeout expires - which means we + * have pending timers that satisfy the forward progress constraint. + * + * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from + * the check. + */ + uv_update_time(loop); + uv_process_timers(loop); + } + r = uv__loop_alive(loop); if (mode & (UV_RUN_ONCE | UV_RUN_NOWAIT)) break; diff --git a/test/test-list.h b/test/test-list.h index 0822d7f1..156266d6 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -101,6 +101,7 @@ TEST_DECLARE (timer_start_twice) TEST_DECLARE (timer_order) TEST_DECLARE (timer_huge_timeout) TEST_DECLARE (timer_huge_repeat) +TEST_DECLARE (timer_run_once) TEST_DECLARE (idle_starvation) TEST_DECLARE (loop_handles) TEST_DECLARE (get_loadavg) @@ -350,6 +351,7 @@ TASK_LIST_START TEST_ENTRY (timer_order) TEST_ENTRY (timer_huge_timeout) TEST_ENTRY (timer_huge_repeat) + TEST_ENTRY (timer_run_once) TEST_ENTRY (idle_starvation) diff --git a/test/test-timer.c b/test/test-timer.c index 6aa28897..39549572 100644 --- a/test/test-timer.c +++ b/test/test-timer.c @@ -264,3 +264,31 @@ TEST_IMPL(timer_huge_repeat) { MAKE_VALGRIND_HAPPY(); return 0; } + + +static unsigned int timer_run_once_timer_cb_called; + + +static void timer_run_once_timer_cb(uv_timer_t* handle, int status) { + timer_run_once_timer_cb_called++; +} + + +TEST_IMPL(timer_run_once) { + uv_timer_t timer_handle; + + ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_run_once_timer_cb, 0, 0)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT(1 == timer_run_once_timer_cb_called); + + ASSERT(0 == uv_timer_start(&timer_handle, timer_run_once_timer_cb, 1, 0)); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT(2 == timer_run_once_timer_cb_called); + + uv_close((uv_handle_t*) &timer_handle, NULL); + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + + MAKE_VALGRIND_HAPPY(); + return 0; +}