unix: reset signal counters after fork (#3485)

If a signal was received but was not dispatched before fork then
caught_signals counter should be reset. Closing of signal pipe makes
impossible to receive the signal that was counted.
There is no need in this signal because it was sent to parent process

Fixes: https://github.com/libuv/libuv/issues/3483
This commit is contained in:
SmorkalovG 2023-09-29 12:54:35 +02:00 committed by GitHub
parent d83fadaf09
commit 2a4cab70ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 0 deletions

View File

@ -279,6 +279,8 @@ static int uv__signal_loop_once_init(uv_loop_t* loop) {
int uv__signal_loop_fork(uv_loop_t* loop) {
struct uv__queue* q;
if (loop->signal_pipefd[0] == -1)
return 0;
uv__io_stop(loop, &loop->signal_io_watcher, POLLIN);
@ -286,6 +288,19 @@ int uv__signal_loop_fork(uv_loop_t* loop) {
uv__close(loop->signal_pipefd[1]);
loop->signal_pipefd[0] = -1;
loop->signal_pipefd[1] = -1;
uv__queue_foreach(q, &loop->handle_queue) {
uv_handle_t* handle = uv__queue_data(q, uv_handle_t, handle_queue);
uv_signal_t* sh;
if (handle->type != UV_SIGNAL)
continue;
sh = (uv_signal_t*) handle;
sh->caught_signals = 0;
sh->dispatched_signals = 0;
}
return uv__signal_loop_once_init(loop);
}

View File

@ -371,6 +371,47 @@ TEST_IMPL(fork_signal_to_child_closed) {
return 0;
}
static void fork_signal_cb(uv_signal_t* h, int s) {
fork_signal_cb_called = s;
}
static void empty_close_cb(uv_handle_t* h){}
TEST_IMPL(fork_close_signal_in_child) {
uv_loop_t loop;
uv_signal_t signal_handle;
pid_t child_pid;
ASSERT_EQ(0, uv_loop_init(&loop));
ASSERT_EQ(0, uv_signal_init(&loop, &signal_handle));
ASSERT_EQ(0, uv_signal_start(&signal_handle, &fork_signal_cb, SIGHUP));
ASSERT_EQ(0, kill(getpid(), SIGHUP));
child_pid = fork();
ASSERT_NE(child_pid, -1);
ASSERT_EQ(0, fork_signal_cb_called);
if (!child_pid) {
uv_loop_fork(&loop);
uv_close((uv_handle_t*)&signal_handle, &empty_close_cb);
uv_run(&loop, UV_RUN_DEFAULT);
/* Child doesn't receive the signal */
ASSERT_EQ(0, fork_signal_cb_called);
} else {
/* Parent. Runing once to receive the signal */
uv_run(&loop, UV_RUN_ONCE);
ASSERT_EQ(SIGHUP, fork_signal_cb_called);
/* loop should stop after closing the only handle */
uv_close((uv_handle_t*)&signal_handle, &empty_close_cb);
ASSERT_EQ(0, uv_run(&loop, UV_RUN_DEFAULT));
assert_wait_child(child_pid);
}
MAKE_VALGRIND_HAPPY(&loop);
return 0;
}
static void create_file(const char* name) {
int r;

View File

@ -546,6 +546,7 @@ TEST_DECLARE (fork_socketpair)
TEST_DECLARE (fork_socketpair_started)
TEST_DECLARE (fork_signal_to_child)
TEST_DECLARE (fork_signal_to_child_closed)
TEST_DECLARE (fork_close_signal_in_child)
#ifndef __APPLE__ /* This is forbidden in a fork child: The process has forked
and you cannot use this CoreFoundation functionality
safely. You MUST exec(). */
@ -1184,6 +1185,7 @@ TASK_LIST_START
TEST_ENTRY (fork_socketpair_started)
TEST_ENTRY (fork_signal_to_child)
TEST_ENTRY (fork_signal_to_child_closed)
TEST_ENTRY (fork_close_signal_in_child)
#ifndef __APPLE__
TEST_ENTRY (fork_fs_events_child)
TEST_ENTRY (fork_fs_events_child_dir)