From ee3718dd71061dccfd1f85b973ae49e27909f147 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 13 May 2022 06:41:33 -0400 Subject: [PATCH] loop: better align order-of-events behavior between platforms (#3598) Previously, Windows would always defer event processing to the loop after they were received. This could cause confusion for users who were using prepare and idle callbacks, as seen from this bug in nodejs[^1] and this discussion in libuv[^2], and even some discrepancies in the libuv tests too[^3]. [^1]: https://github.com/nodejs/node/pull/42340 [^2]: https://github.com/libuv/libuv/discussions/3550 [^3]: See change to test-spawn.c in this PR So rather than declare those usages to be wrong, we change libuv to meet those users expectations. Replaces: https://github.com/libuv/libuv/pull/3585 --- src/unix/core.c | 5 +++++ src/win/core.c | 5 +++++ test/test-pipe-set-non-blocking.c | 3 +-- test/test-spawn.c | 3 --- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/unix/core.c b/src/unix/core.c index ca3699899..3149152bf 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -404,6 +404,11 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) { uv__io_poll(loop, timeout); + /* Process immediate callbacks (e.g. write_cb) a small fixed number of + * times to avoid loop starvation.*/ + for (r = 0; r < 8 && !QUEUE_EMPTY(&loop->pending_queue); r++) + uv__run_pending(loop); + /* Run one final update on the provider_idle_time in case uv__io_poll * returned because the timeout expired, but no events were received. This * call will be ignored if the provider_entry_time was either never set (if diff --git a/src/win/core.c b/src/win/core.c index 664ffac97..67af93e65 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -617,6 +617,11 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { else uv__poll_wine(loop, timeout); + /* Process immediate callbacks (e.g. write_cb) a small fixed number of + * times to avoid loop starvation.*/ + for (r = 0; r < 8 && loop->pending_reqs_tail != NULL; r++) + uv__process_reqs(loop); + /* Run one final update on the provider_idle_time in case uv__poll* * returned because the timeout expired, but no events were received. This * call will be ignored if the provider_entry_time was either never set (if diff --git a/test/test-pipe-set-non-blocking.c b/test/test-pipe-set-non-blocking.c index 827e72641..c78046095 100644 --- a/test/test-pipe-set-non-blocking.c +++ b/test/test-pipe-set-non-blocking.c @@ -102,8 +102,7 @@ TEST_IMPL(pipe_set_non_blocking) { ASSERT(n == UV_EAGAIN); /* E_NOTIMPL */ ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &pipe_handle, &buf, 1, write_cb)); ASSERT_NOT_NULL(write_req.handle); - ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* queue write_cb */ - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* process write_cb */ + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); ASSERT_NULL(write_req.handle); /* check for signaled completion of write_cb */ n = buf.len; #endif diff --git a/test/test-spawn.c b/test/test-spawn.c index de9c71002..7a07482b1 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -1675,9 +1675,6 @@ TEST_IMPL(closed_fd_events) { ASSERT(req.result == 1); uv_fs_req_cleanup(&req); -#ifdef _WIN32 - ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_ONCE)); -#endif ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* should have received just one byte */