From 31e419747ab92dbc29d0d9db58d88ff2d2caf5c9 Mon Sep 17 00:00:00 2001 From: Joel Jacobson Date: Thu, 24 Jul 2025 21:17:19 +0200 Subject: [PATCH 2/2] Optimize LISTEN/NOTIFY wakeup by replacing signal with direct SetLatch Building upon the robust atomic state machine introduced in the previous commit, this change completes the modernization of NOTIFY IPC by replacing its wakeup mechanism. With inter-process state now managed reliably, the heavyweight SIGUSR1 signal is no longer necessary and is replaced with a much more efficient, direct "poke." The async.c notifier now replaces its call to SendProcSignal with a direct call to SetLatch on the target backend's procLatch. This is a significant optimization because WaitLatch, which listeners already use for blocking, is underpinned by the modern WaitEventSet abstraction (kqueue, epoll, etc.). We now leverage this existing, highly efficient infrastructure for the wakeup, completely bypassing the kill() syscall and the SIGUSR1 signal handler for all NOTIFY events. This demonstrates a powerful, two-step migration pattern: 1. First, solve a subsystem's state synchronization problem with a lock-free, atomic FSM to eliminate redundant signaling. 2. Then, with state management handled, make the wakeup itself cheaper by replacing the expensive signal with a direct SetLatch. This staged approach allows us to modernize subsystems incrementally and safely. By applying this pattern to async.c, we prove its viability and simplicity, creating a clear template for other parts of the system to follow in moving towards a more performant, signal-free IPC model. --- src/backend/commands/async.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index ae20017af9b..c871774b72c 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -142,6 +142,7 @@ #include "miscadmin.h" #include "storage/ipc.h" #include "storage/lmgr.h" +#include "storage/proc.h" #include "storage/procsignal.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" @@ -1719,13 +1720,25 @@ SignalBackends(void) else { /* - * Note: assuming things aren't broken, a signal failure here could - * only occur if the target backend exited since we released - * NotifyQueueLock; which is unlikely but certainly possible. So we - * just log a low-level debug message if it happens. + * Get the target backend's PGPROC and set its latch. + * + * Note: The target backend might exit after we released + * NotifyQueueLock but before we set the latch. We need to + * handle the race condition where the PGPROC slot might be + * recycled by a new process with a different PID. */ - if (SendProcSignal(pid, PROCSIG_NOTIFY_INTERRUPT, procno) < 0) - elog(DEBUG3, "could not signal backend with PID %d: %m", pid); + PGPROC *proc = GetPGProcByNumber(procno); + + /* Verify the PID hasn't changed (backend hasn't exited) */ + if (proc->pid == pid) + { + SetLatch(&proc->procLatch); + } + else + { + /* Backend exited and slot was recycled */ + elog(DEBUG3, "could not signal backend with PID %d: process no longer exists", pid); + } } } } -- 2.47.1