process: monitor for exit with kqueue on BSDs (#3441)
This adds a workaround for an xnu kernel bug that sometimes results in SIGCHLD not being delivered. The workaround is to use kevent to listen for EVFILT_PROC/NOTE_EXIT events instead of relying on SIGCHLD on *BSD. Apple rdar: FB9529664 Refs: https://github.com/libuv/libuv/pull/3257
This commit is contained in:
parent
bb0b4bb783
commit
d9e90857f0
@ -282,6 +282,7 @@ uv_handle_type uv__handle_type(int fd);
|
||||
FILE* uv__open_file(const char* path);
|
||||
int uv__getpwuid_r(uv_passwd_t* pwd);
|
||||
int uv__search_path(const char* prog, char* buf, size_t* buflen);
|
||||
void uv__wait_children(uv_loop_t* loop);
|
||||
|
||||
/* random */
|
||||
int uv__random_devurandom(void* buf, size_t buflen);
|
||||
|
||||
@ -284,6 +284,11 @@ 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;
|
||||
if (ev->filter == EVFILT_PROC) {
|
||||
uv__wait_children(loop);
|
||||
nevents++;
|
||||
continue;
|
||||
}
|
||||
fd = ev->ident;
|
||||
/* Skip invalidated events, see uv__platform_invalidate_fd */
|
||||
if (fd == -1)
|
||||
|
||||
@ -49,10 +49,20 @@ extern char **environ;
|
||||
# include "zos-base.h"
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <sys/event.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if !(defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__))
|
||||
static void uv__chld(uv_signal_t* handle, int signum) {
|
||||
assert(signum == SIGCHLD);
|
||||
uv__wait_children(handle->loop);
|
||||
}
|
||||
#endif
|
||||
|
||||
void uv__wait_children(uv_loop_t* loop) {
|
||||
uv_process_t* process;
|
||||
uv_loop_t* loop;
|
||||
int exit_status;
|
||||
int term_signal;
|
||||
int status;
|
||||
@ -61,10 +71,7 @@ static void uv__chld(uv_signal_t* handle, int signum) {
|
||||
QUEUE* q;
|
||||
QUEUE* h;
|
||||
|
||||
assert(signum == SIGCHLD);
|
||||
|
||||
QUEUE_INIT(&pending);
|
||||
loop = handle->loop;
|
||||
|
||||
h = &loop->process_handles;
|
||||
q = QUEUE_HEAD(h);
|
||||
@ -420,7 +427,9 @@ int uv_spawn(uv_loop_t* loop,
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
#if !(defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__))
|
||||
uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD);
|
||||
#endif
|
||||
|
||||
/* Acquire write lock to prevent opening new fds in worker threads */
|
||||
uv_rwlock_wrlock(&loop->cloexec_lock);
|
||||
@ -495,6 +504,13 @@ int uv_spawn(uv_loop_t* loop,
|
||||
|
||||
/* Only activate this handle if exec() happened successfully */
|
||||
if (exec_errorno == 0) {
|
||||
#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();
|
||||
#endif
|
||||
|
||||
QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue);
|
||||
uv__handle_start(process);
|
||||
}
|
||||
|
||||
@ -320,6 +320,7 @@ TEST_DECLARE (spawn_reads_child_path)
|
||||
TEST_DECLARE (spawn_inherit_streams)
|
||||
TEST_DECLARE (spawn_quoted_path)
|
||||
TEST_DECLARE (spawn_tcp_server)
|
||||
TEST_DECLARE (spawn_exercise_sigchld_issue)
|
||||
TEST_DECLARE (fs_poll)
|
||||
TEST_DECLARE (fs_poll_getpath)
|
||||
TEST_DECLARE (fs_poll_close_request)
|
||||
@ -950,6 +951,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (spawn_inherit_streams)
|
||||
TEST_ENTRY (spawn_quoted_path)
|
||||
TEST_ENTRY (spawn_tcp_server)
|
||||
TEST_ENTRY (spawn_exercise_sigchld_issue)
|
||||
TEST_ENTRY (fs_poll)
|
||||
TEST_ENTRY (fs_poll_getpath)
|
||||
TEST_ENTRY (fs_poll_close_request)
|
||||
|
||||
@ -1891,6 +1891,44 @@ TEST_IMPL(spawn_quoted_path) {
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_IMPL(spawn_exercise_sigchld_issue) {
|
||||
int r;
|
||||
int i;
|
||||
uv_process_options_t dummy_options = {0};
|
||||
uv_process_t dummy_processes[100];
|
||||
char* args[2];
|
||||
|
||||
init_process_options("spawn_helper1", exit_cb);
|
||||
|
||||
r = uv_spawn(uv_default_loop(), &process, &options);
|
||||
ASSERT_EQ(r, 0);
|
||||
|
||||
// This test exercises a bug in the darwin kernel that causes SIGCHLD not to
|
||||
// be delivered sometimes. Calling posix_spawn many times increases the
|
||||
// likelihood of encountering this issue, so spin a few times to make this
|
||||
// test more reliable.
|
||||
dummy_options.file = args[0] = "program-that-had-better-not-exist";
|
||||
args[1] = NULL;
|
||||
dummy_options.args = args;
|
||||
dummy_options.exit_cb = fail_cb;
|
||||
dummy_options.flags = 0;
|
||||
for (i = 0; i < 100; i++) {
|
||||
r = uv_spawn(uv_default_loop(), &dummy_processes[i], &dummy_options);
|
||||
if (r != UV_ENOENT)
|
||||
ASSERT_EQ(r, UV_EACCES);
|
||||
uv_close((uv_handle_t*) &dummy_processes[i], close_cb);
|
||||
}
|
||||
|
||||
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||
ASSERT_EQ(r, 0);
|
||||
|
||||
ASSERT_EQ(exit_cb_called, 1);
|
||||
ASSERT_EQ(close_cb_called, 101);
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper for child process of spawn_inherit_streams */
|
||||
#ifndef _WIN32
|
||||
void spawn_stdin_stdout(void) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user