process: fix hang after NOTE_EXIT (#3521)

Bug #3504 seems to affect more platforms than just OpenBSD. As this
seems to be a race condition in these kernels, we do not want to fail
because of it. Instead, we remove the WNOHANG flag from waitpid, and
track exactly which processes have exited. Should also be a slight speed
improvement for excessively large numbers of live children.
This commit is contained in:
Jameson Nash 2022-03-09 12:25:51 -05:00 committed by GitHub
parent 3c569c00df
commit bae2992cb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 9 deletions

View File

@ -117,6 +117,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
unsigned int revents;
QUEUE* q;
uv__io_t* w;
uv_process_t* process;
sigset_t* pset;
sigset_t set;
uint64_t base;
@ -284,12 +285,22 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
for (i = 0; i < nfds; i++) {
ev = events + i;
fd = ev->ident;
/* Handle kevent NOTE_EXIT results */
if (ev->filter == EVFILT_PROC) {
loop->flags |= UV_LOOP_REAP_CHILDREN;
QUEUE_FOREACH(q, &loop->process_handles) {
process = QUEUE_DATA(q, uv_process_t, queue);
if (process->pid == fd) {
process->flags |= UV_HANDLE_REAP;
loop->flags |= UV_LOOP_REAP_CHILDREN;
break;
}
}
nevents++;
continue;
}
fd = ev->ident;
/* Skip invalidated events, see uv__platform_invalidate_fd */
if (fd == -1)
continue;

View File

@ -63,12 +63,18 @@ extern char **environ;
# include "zos-base.h"
#endif
#if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
#if defined(__APPLE__) || \
defined(__DragonFly__) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || \
defined(__OpenBSD__)
#include <sys/event.h>
#else
#define UV_USE_SIGCHLD
#endif
#if !(defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__))
#ifdef UV_USE_SIGCHLD
static void uv__chld(uv_signal_t* handle, int signum) {
assert(signum == SIGCHLD);
uv__wait_children(handle->loop);
@ -80,6 +86,7 @@ void uv__wait_children(uv_loop_t* loop) {
int exit_status;
int term_signal;
int status;
int options;
pid_t pid;
QUEUE pending;
QUEUE* q;
@ -93,19 +100,33 @@ void uv__wait_children(uv_loop_t* loop) {
process = QUEUE_DATA(q, uv_process_t, queue);
q = QUEUE_NEXT(q);
#ifndef UV_USE_SIGCHLD
if ((process->flags & UV_HANDLE_REAP) == 0)
continue;
options = 0;
process->flags &= ~UV_HANDLE_REAP;
#else
options = WNOHANG;
#endif
do
pid = waitpid(process->pid, &status, WNOHANG);
pid = waitpid(process->pid, &status, options);
while (pid == -1 && errno == EINTR);
if (pid == 0)
#ifdef UV_USE_SIGCHLD
if (pid == 0) /* Not yet exited */
continue;
#endif
if (pid == -1) {
if (errno != ECHILD)
abort();
/* The child died, and we missed it. This probably means someone else
* stole the waitpid from us. Handle this by not handling it at all. */
continue;
}
assert(pid == process->pid);
process->status = status;
QUEUE_REMOVE(&process->queue);
QUEUE_INSERT_TAIL(&pending, &process->queue);
@ -965,7 +986,7 @@ int uv_spawn(uv_loop_t* loop,
goto error;
}
#if !(defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__))
#ifdef UV_USE_SIGCHLD
uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD);
#endif
@ -984,13 +1005,14 @@ int uv_spawn(uv_loop_t* loop,
* fail to open a stdio handle. This ensures we can eventually reap the child
* with waitpid. */
if (exec_errorno == 0) {
#if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
#ifndef UV_USE_SIGCHLD
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)) {
if (errno != ESRCH)
abort();
/* Process already exited. Call waitpid on the next loop iteration. */
process->flags |= UV_HANDLE_REAP;
loop->flags |= UV_LOOP_REAP_CHILDREN;
}
#endif

View File

@ -130,7 +130,10 @@ enum {
UV_SIGNAL_ONE_SHOT = 0x02000000,
/* Only used by uv_poll_t handles. */
UV_HANDLE_POLL_SLOW = 0x01000000
UV_HANDLE_POLL_SLOW = 0x01000000,
/* Only used by uv_process_t handles. */
UV_HANDLE_REAP = 0x10000000
};
int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap);