unix: fix signal handle closing deferral

The way libuv handled closing of `uv_signal_t` handles with pending
events introduced an infidelity where `uv_loop_alive()` returned false
while the signal handle was still in the closing-but-not-closed state.

Fixes: https://github.com/libuv/libuv/issues/2721
PR-URL: https://github.com/libuv/libuv/pull/2722
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
This commit is contained in:
Ben Noordhuis 2020-03-10 10:58:29 +01:00
parent e8644693ea
commit 70469dcaa6
4 changed files with 45 additions and 26 deletions

View File

@ -171,9 +171,7 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
case UV_SIGNAL:
uv__signal_close((uv_signal_t*) handle);
/* Signal handles may not be closed immediately. The signal code will
* itself close uv__make_close_pending whenever appropriate. */
return;
break;
default:
assert(0);
@ -238,6 +236,8 @@ int uv__getiovmax(void) {
static void uv__finish_close(uv_handle_t* handle) {
uv_signal_t* sh;
/* Note: while the handle is in the UV_HANDLE_CLOSING state now, it's still
* possible for it to be active in the sense that uv__is_active() returns
* true.
@ -260,7 +260,20 @@ static void uv__finish_close(uv_handle_t* handle) {
case UV_FS_EVENT:
case UV_FS_POLL:
case UV_POLL:
break;
case UV_SIGNAL:
/* If there are any caught signals "trapped" in the signal pipe,
* we can't call the close callback yet. Reinserting the handle
* into the closing queue makes the event loop spin but that's
* okay because we only need to deliver the pending events.
*/
sh = (uv_signal_t*) handle;
if (sh->caught_signals > sh->dispatched_signals) {
handle->flags ^= UV_HANDLE_CLOSED;
uv__make_close_pending(handle); /* Back into the queue. */
return;
}
break;
case UV_NAMED_PIPE:

View File

@ -331,16 +331,7 @@ int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
void uv__signal_close(uv_signal_t* handle) {
uv__signal_stop(handle);
/* If there are any caught signals "trapped" in the signal pipe, we can't
* call the close callback yet. Otherwise, add the handle to the finish_close
* queue.
*/
if (handle->caught_signals == handle->dispatched_signals) {
uv__make_close_pending((uv_handle_t*) handle);
}
}
@ -472,17 +463,6 @@ static void uv__signal_event(uv_loop_t* loop,
if (handle->flags & UV_SIGNAL_ONE_SHOT)
uv__signal_stop(handle);
/* If uv_close was called while there were caught signals that were not
* yet dispatched, the uv__finish_close was deferred. Make close pending
* now if this has happened.
*/
if (handle->caught_signals == handle->dispatched_signals) {
if (handle->signum == 0)
uv__handle_stop(handle);
if (handle->flags & UV_HANDLE_CLOSING)
uv__make_close_pending((uv_handle_t*) handle);
}
}
bytes -= end;
@ -572,6 +552,5 @@ static void uv__signal_stop(uv_signal_t* handle) {
uv__signal_unlock_and_unblock(&saved_sigmask);
handle->signum = 0;
if (handle->caught_signals == handle->dispatched_signals)
uv__handle_stop(handle);
uv__handle_stop(handle);
}

View File

@ -471,6 +471,7 @@ TEST_DECLARE (we_get_signal_one_shot)
TEST_DECLARE (we_get_signals_mixed)
TEST_DECLARE (signal_multiple_loops)
TEST_DECLARE (signal_pending_on_close)
TEST_DECLARE (signal_close_loop_alive)
TEST_DECLARE (closed_fd_events)
#endif
#ifdef __APPLE__
@ -937,6 +938,7 @@ TASK_LIST_START
TEST_ENTRY (we_get_signals_mixed)
TEST_ENTRY (signal_multiple_loops)
TEST_ENTRY (signal_pending_on_close)
TEST_ENTRY (signal_close_loop_alive)
TEST_ENTRY (closed_fd_events)
#endif

View File

@ -34,6 +34,11 @@ static char* buf;
static int close_cb_called;
static void stop_loop_cb(uv_signal_t* signal, int signum) {
ASSERT(signum == SIGPIPE);
uv_stop(signal->loop);
}
static void signal_cb(uv_signal_t* signal, int signum) {
ASSERT(0);
}
@ -91,4 +96,24 @@ TEST_IMPL(signal_pending_on_close) {
return 0;
}
#endif
TEST_IMPL(signal_close_loop_alive) {
ASSERT(0 == uv_loop_init(&loop));
ASSERT(0 == uv_signal_init(&loop, &signal_hdl));
ASSERT(0 == uv_signal_start(&signal_hdl, stop_loop_cb, SIGPIPE));
uv_unref((uv_handle_t*) &signal_hdl);
ASSERT(0 == uv_kill(uv_os_getpid(), SIGPIPE));
ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT));
uv_close((uv_handle_t*) &signal_hdl, close_cb);
ASSERT(1 == uv_loop_alive(&loop));
ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT));
ASSERT(0 == uv_loop_close(&loop));
ASSERT(1 == close_cb_called);
MAKE_VALGRIND_HAPPY();
return 0;
}
#endif