From 953f901dd2330a9979838cd43ff04eacde71b25a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 1 Feb 2022 15:27:12 -0500 Subject: [PATCH] process,bsd: handle kevent NOTE_EXIT failure (#3451) The kernel may return ESRCH if the child has already exited here. This is rather annoying, and means we must indirectly handle notification to our event loop of the process exit. Refs: https://github.com/libuv/libuv/pull/3441 Refs: https://github.com/libuv/libuv/pull/3257 --- src/unix/internal.h | 3 ++- src/unix/kqueue.c | 7 ++++++- src/unix/process.c | 8 ++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/unix/internal.h b/src/unix/internal.h index 16be13b9..2dcc8b32 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -145,7 +145,8 @@ typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t; /* loop flags */ enum { - UV_LOOP_BLOCK_SIGPROF = 1 + UV_LOOP_BLOCK_SIGPROF = 0x1, + UV_LOOP_REAP_CHILDREN = 0x2 }; /* flags of excluding ifaddr */ diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index efbc561d..857eb1d5 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -285,7 +285,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { for (i = 0; i < nfds; i++) { ev = events + i; if (ev->filter == EVFILT_PROC) { - uv__wait_children(loop); + loop->flags |= UV_LOOP_REAP_CHILDREN; nevents++; continue; } @@ -382,6 +382,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nevents++; } + if (loop->flags & UV_LOOP_REAP_CHILDREN) { + loop->flags &= ~UV_LOOP_REAP_CHILDREN; + uv__wait_children(loop); + } + if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; diff --git a/src/unix/process.c b/src/unix/process.c index c1f6bd4b..2920b942 100644 --- a/src/unix/process.c +++ b/src/unix/process.c @@ -507,8 +507,12 @@ int uv_spawn(uv_loop_t* loop, #if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) struct kevent event; EV_SET(&event, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, 0); - if (kevent(loop->backend_fd, &event, 1, NULL, 0, NULL)) - abort(); + if (kevent(loop->backend_fd, &event, 1, NULL, 0, NULL)) { + if (errno != ESRCH) + abort(); + /* Process already exited. Call waitpid on the next loop iteration. */ + loop->flags |= UV_LOOP_REAP_CHILDREN; + } #endif QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue);