From 4cb6a2d33c6f8717d5b1899142e139075a7425f7 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 18 Jan 2026 22:32:36 +0100 Subject: [PATCH] unix: factor out common i/o poll code (#5000) --- src/unix/aix.c | 20 ++------------------ src/unix/core.c | 33 +++++++++++++++++++++++++++++++++ src/unix/internal.h | 2 ++ src/unix/kqueue.c | 26 ++------------------------ src/unix/linux.c | 20 ++------------------ src/unix/os390.c | 21 +++------------------ src/unix/posix-poll.c | 26 ++------------------------ src/unix/sunos.c | 20 ++------------------ 8 files changed, 48 insertions(+), 120 deletions(-) diff --git a/src/unix/aix.c b/src/unix/aix.c index 1c827b20c..30a2a894f 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -229,28 +229,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - - /* Store the current timeout in a location that's globally accessible so - * other locations like uv__work_done() can determine whether the queue - * of events in the callback were waiting when poll was called. - */ - lfields->current_timeout = timeout; - + uv__io_poll_prepare(loop, NULL, timeout); nfds = pollset_poll(loop->backend_fd, events, ARRAY_SIZE(events), timeout); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - SAVE_ERRNO(uv__update_time(loop)); + uv__io_poll_check(loop, NULL); if (nfds == 0) { if (reset_timeout != 0) { diff --git a/src/unix/core.c b/src/unix/core.c index cde4dc444..5d6160cb1 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -1056,6 +1056,39 @@ int uv__io_active(const uv__io_t* w, unsigned int events) { } +void uv__io_poll_prepare(uv_loop_t* loop, sigset_t* pset, int timeout) { + uv__loop_internal_fields_t* lfields; + + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + + /* Store the current timeout in a location that's globally accessible so + * other locations like uv__work_done() can determine whether the queue + * of events in the callback were waiting when poll was called. + */ + lfields = uv__get_internal_fields(loop); + lfields->current_timeout = timeout; + + if (pset != NULL) + if (pthread_sigmask(SIG_BLOCK, pset, NULL)) + abort(); +} + +void uv__io_poll_check(uv_loop_t* loop, sigset_t* pset) { + if (pset != NULL) + if (pthread_sigmask(SIG_UNBLOCK, pset, NULL)) + abort(); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + SAVE_ERRNO(uv__update_time(loop)); +} + int uv__fd_exists(uv_loop_t* loop, int fd) { return (unsigned) fd < loop->nwatchers && loop->watchers[fd] != NULL; } diff --git a/src/unix/internal.h b/src/unix/internal.h index c9704a8bd..d17c7cbb9 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -328,6 +328,8 @@ int uv__io_active(const uv__io_t* w, unsigned int events); int uv__io_check_fd(uv_loop_t* loop, int fd); void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */ int uv__io_fork(uv_loop_t* loop); +void uv__io_poll_prepare(uv_loop_t* loop, sigset_t* pset, int timeout); +void uv__io_poll_check(uv_loop_t* loop, sigset_t* pset); int uv__fd_exists(uv_loop_t* loop, int fd); /* async */ diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index 9c5f19c00..27d288143 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -261,32 +261,19 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } for (;; nevents = 0) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - if (timeout != -1) { spec.tv_sec = timeout / 1000; spec.tv_nsec = (timeout % 1000) * 1000000; } - if (pset != NULL) - pthread_sigmask(SIG_BLOCK, pset, NULL); - - /* Store the current timeout in a location that's globally accessible so - * other locations like uv__work_done() can determine whether the queue - * of events in the callback were waiting when poll was called. - */ - lfields->current_timeout = timeout; - + uv__io_poll_prepare(loop, pset, timeout); nfds = kevent(loop->backend_fd, events, nevents, events, ARRAY_SIZE(events), timeout == -1 ? NULL : &spec); + uv__io_poll_check(loop, pset); if (nfds == -1) assert(errno == EINTR); @@ -294,15 +281,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { /* Unlimited timeout should only return with events or signal. */ assert(timeout != -1); - if (pset != NULL) - pthread_sigmask(SIG_UNBLOCK, pset, NULL); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - uv__update_time(loop); - if (nfds == 0 || nfds == -1) { /* If kqueue is empty or interrupted, we might still have children ready * to reap immediately. */ diff --git a/src/unix/linux.c b/src/unix/linux.c index 5beb4d2cc..60551cbf7 100644 --- a/src/unix/linux.c +++ b/src/unix/linux.c @@ -1447,25 +1447,9 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { while (*ctl->sqhead != *ctl->sqtail) uv__epoll_ctl_flush(epollfd, ctl, &prep); - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - - /* Store the current timeout in a location that's globally accessible so - * other locations like uv__work_done() can determine whether the queue - * of events in the callback were waiting when poll was called. - */ - lfields->current_timeout = timeout; - + uv__io_poll_prepare(loop, NULL, timeout); nfds = epoll_pwait(epollfd, events, ARRAY_SIZE(events), timeout, sigmask); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - SAVE_ERRNO(uv__update_time(loop)); + uv__io_poll_check(loop, NULL); if (nfds == -1) assert(errno == EINTR); diff --git a/src/unix/os390.c b/src/unix/os390.c index 029add414..c049884c4 100644 --- a/src/unix/os390.c +++ b/src/unix/os390.c @@ -892,30 +892,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nfds = 0; for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) timeout = max_safe_timeout; - /* Store the current timeout in a location that's globally accessible so - * other locations like uv__work_done() can determine whether the queue - * of events in the callback were waiting when poll was called. - */ - lfields->current_timeout = timeout; - + uv__io_poll_prepare(loop, NULL, timeout); nfds = epoll_wait(loop->ep, events, ARRAY_SIZE(events), timeout); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ base = loop->time; - SAVE_ERRNO(uv__update_time(loop)); + uv__io_poll_check(loop, NULL); + if (nfds == 0) { assert(timeout != -1); diff --git a/src/unix/posix-poll.c b/src/unix/posix-poll.c index 2efb843fb..5b7d75c22 100644 --- a/src/unix/posix-poll.c +++ b/src/unix/posix-poll.c @@ -195,31 +195,9 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { * our caller then we need to loop around and poll() again. */ for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - - /* Store the current timeout in a location that's globally accessible so - * other locations like uv__work_done() can determine whether the queue - * of events in the callback were waiting when poll was called. - */ - lfields->current_timeout = timeout; - - if (pset != NULL) - if (pthread_sigmask(SIG_BLOCK, pset, NULL)) - abort(); + uv__io_poll_prepare(loop, pset, timeout); nfds = poll(loop->poll_fds, (nfds_t)loop->poll_fds_used, timeout); - if (pset != NULL) - if (pthread_sigmask(SIG_UNBLOCK, pset, NULL)) - abort(); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - SAVE_ERRNO(uv__update_time(loop)); + uv__io_poll_check(loop, pset); if (nfds == 0) { if (reset_timeout != 0) { diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 220ff74bd..f3fbca390 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -210,12 +210,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - if (timeout != -1) { spec.tv_sec = timeout / 1000; spec.tv_nsec = (timeout % 1000) * 1000000; @@ -227,17 +221,13 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nfds = 1; saved_errno = 0; - if (pset != NULL) - pthread_sigmask(SIG_BLOCK, pset, NULL); - + uv__io_poll_prepare(loop, pset, timeout); err = port_getn(loop->backend_fd, events, ARRAY_SIZE(events), &nfds, timeout == -1 ? NULL : &spec); - - if (pset != NULL) - pthread_sigmask(SIG_UNBLOCK, pset, NULL); + uv__io_poll_check(loop, pset); if (err) { /* Work around another kernel bug: port_getn() may return events even @@ -251,12 +241,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } } - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - SAVE_ERRNO(uv__update_time(loop)); - if (events[0].portev_source == 0) { if (reset_timeout != 0) { timeout = user_timeout;