From 31e419747ab92dbc29d0d9db58d88ff2d2caf5c9 Mon Sep 17 00:00:00 2001
From: Joel Jacobson <joel@compiler.org>
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

