diff --git a/libuv_reactor.c b/libuv_reactor.c index 774ee4c..4743e90 100644 --- a/libuv_reactor.c +++ b/libuv_reactor.c @@ -1083,6 +1083,12 @@ static bool libuv_timer_start(zend_async_event_t *event) return false; } + // Hidden means "keeps firing, but is not a reason to stay alive": unref drops + // the timer out of uv_loop_alive() without stopping it. + if (ZEND_ASYNC_EVENT_IS_HIDDEN(event)) { + uv_unref((uv_handle_t *) &timer->uv_handle); + } + event->loop_ref_count++; ZEND_ASYNC_INCREASE_EVENT_COUNT(event); return true; @@ -1655,6 +1661,8 @@ static void libuv_handle_process_events(void) ZEND_ASYNC_CALLBACKS_NOTIFY(event, NULL, NULL); // Process event will be removed when stopped } + // ECHILD is not settled here: a notified event outlives this sweep, so a + // second sweep would overwrite its exit code with a made-up one. #endif } @@ -1694,44 +1702,63 @@ static void libuv_add_process_event(zend_async_event_t *event) /* }}} */ -/* {{{ libuv_remove_process_event */ -static void libuv_remove_process_event(zend_async_event_t *event) +/* {{{ libuv_release_process_watch + * Drops the SIGCHLD handler once the last process event is gone. Call from every + * path that empties the table. */ +static void libuv_release_process_watch(void) { - if (ASYNC_G(process_events) == NULL) { + if (ASYNC_G(process_events) == NULL || zend_hash_num_elements(ASYNC_G(process_events)) > 0) { return; } - // Get process handle from event to use as key - async_process_event_t *process_event = (async_process_event_t *) event; - - zend_hash_index_del(ASYNC_G(process_events), (uintptr_t) process_event->event.process); + bool has_sigchld_signal_events = false; - // Only remove SIGCHLD handler if no more process events AND no regular signal events for SIGCHLD - if (zend_hash_num_elements(ASYNC_G(process_events)) == 0) { - bool has_sigchld_signal_events = false; - - // Check if there are regular signal events for SIGCHLD - if (ASYNC_G(signal_events) != NULL) { - HashTable *sigchld_events = zend_hash_index_find_ptr(ASYNC_G(signal_events), SIGCHLD); - if (sigchld_events != NULL && zend_hash_num_elements(sigchld_events) > 0) { - has_sigchld_signal_events = true; - } + // Check if there are regular signal events for SIGCHLD + if (ASYNC_G(signal_events) != NULL) { + HashTable *sigchld_events = zend_hash_index_find_ptr(ASYNC_G(signal_events), SIGCHLD); + if (sigchld_events != NULL && zend_hash_num_elements(sigchld_events) > 0) { + has_sigchld_signal_events = true; } + } - // Only remove handler if no signal events exist for SIGCHLD - if (!has_sigchld_signal_events && ASYNC_G(signal_handlers) != NULL) { - uv_signal_t *handler = zend_hash_index_find_ptr(ASYNC_G(signal_handlers), SIGCHLD); - if (handler != NULL) { + // Only remove handler if no signal events exist for SIGCHLD + if (!has_sigchld_signal_events && ASYNC_G(signal_handlers) != NULL) { + uv_signal_t *handler = zend_hash_index_find_ptr(ASYNC_G(signal_handlers), SIGCHLD); + if (handler != NULL) { + if ((bool) (uintptr_t) handler->data) { + // pcntl still needs delivery; closing reverts the OS disposition. + uv_unref((uv_handle_t *) handler); + } else { uv_signal_stop(handler); +#ifdef ZEND_SIGNALS + libuv_restore_signal_handler(SIGCHLD); +#endif uv_close((uv_handle_t *) handler, libuv_signal_close_cb); zend_hash_index_del(ASYNC_G(signal_handlers), SIGCHLD); } } + } - zend_hash_destroy(ASYNC_G(process_events)); - pefree(ASYNC_G(process_events), 0); - ASYNC_G(process_events) = NULL; + zend_hash_destroy(ASYNC_G(process_events)); + pefree(ASYNC_G(process_events), 0); + ASYNC_G(process_events) = NULL; +} + +/* }}} */ + +/* {{{ libuv_remove_process_event */ +static void libuv_remove_process_event(zend_async_event_t *event) +{ + if (ASYNC_G(process_events) == NULL) { + return; } + + // Get process handle from event to use as key + async_process_event_t *process_event = (async_process_event_t *) event; + + zend_hash_index_del(ASYNC_G(process_events), (uintptr_t) process_event->event.process); + + libuv_release_process_watch(); } /* }}} */ @@ -2170,6 +2197,7 @@ static bool libuv_process_event_dispose(zend_async_event_t *event) const zend_process_t proc_handle = ((zend_async_process_event_t *) event)->process; if ((uintptr_t) proc_handle != 0 && ASYNC_G(process_events) != NULL) { zend_hash_index_del(ASYNC_G(process_events), (zend_ulong) (uintptr_t) proc_handle); + libuv_release_process_watch(); } #ifdef PHP_WIN32