Skip to content

Commit 6e31ffb

Browse files
joelonsqlCommitfest Bot
authored andcommitted
Optimize ProcSignal to avoid redundant SIGUSR1 signals
Previously, ProcSignal used an array of volatile sig_atomic_t flags, one per signal reason. A sender would set a flag and then unconditionally send a SIGUSR1 to the target process. This could result in a storm of redundant signals if multiple processes signaled the same target before it had a chance to run its signal handler. Change this to use a single pg_atomic_uint32 as a bitmask of pending signals. When sending, use pg_atomic_fetch_or_u32 to set the appropriate signal bit and inspect the prior state of the flags word. Then only issue a SIGUSR1 if the previous flags state was zero. This works safely because the receiving backend's signal handler atomically resets the entire bitmask upon receipt, thus processing all pending signals at once. Consequently, subsequent senders seeing a nonzero prior state know a signal is already in flight, significantly reducing redundant kill(pid, SIGUSR1) system calls under heavy contention. On the receiving end, the SIGUSR1 handler now atomically fetches and clears the entire bitmask with a single pg_atomic_exchange_u32, then calls the appropriate sub-handlers. The further optimization to only check if the old flags word was zero is due to Andreas Karlsson.
1 parent 8a27d41 commit 6e31ffb

File tree

2 files changed

+57
-49
lines changed

2 files changed

+57
-49
lines changed

src/backend/storage/ipc/procsignal.c

Lines changed: 54 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ typedef struct
6565
pg_atomic_uint32 pss_pid;
6666
int pss_cancel_key_len; /* 0 means no cancellation is possible */
6767
uint8 pss_cancel_key[MAX_CANCEL_KEY_LENGTH];
68-
volatile sig_atomic_t pss_signalFlags[NUM_PROCSIGNALS];
69-
slock_t pss_mutex; /* protects the above fields */
68+
pg_atomic_uint32 pss_signalFlags; /* bitmask of pending signals */
69+
slock_t pss_mutex; /* protects cancel_key fields only */
7070

7171
/* Barrier-related fields (not protected by pss_mutex) */
7272
pg_atomic_uint64 pss_barrierGeneration;
@@ -105,7 +105,7 @@ struct ProcSignalHeader
105105
NON_EXEC_STATIC ProcSignalHeader *ProcSignal = NULL;
106106
static ProcSignalSlot *MyProcSignalSlot = NULL;
107107

108-
static bool CheckProcSignal(ProcSignalReason reason);
108+
static bool HasProcSignalFlag(uint32 flags, ProcSignalReason reason);
109109
static void CleanupProcSignalState(int status, Datum arg);
110110
static void ResetProcSignalBarrierBits(uint32 flags);
111111

@@ -150,7 +150,7 @@ ProcSignalShmemInit(void)
150150
SpinLockInit(&slot->pss_mutex);
151151
pg_atomic_init_u32(&slot->pss_pid, 0);
152152
slot->pss_cancel_key_len = 0;
153-
MemSet(slot->pss_signalFlags, 0, sizeof(slot->pss_signalFlags));
153+
pg_atomic_init_u32(&slot->pss_signalFlags, 0);
154154
pg_atomic_init_u64(&slot->pss_barrierGeneration, PG_UINT64_MAX);
155155
pg_atomic_init_u32(&slot->pss_barrierCheckMask, 0);
156156
ConditionVariableInit(&slot->pss_barrierCV);
@@ -182,7 +182,7 @@ ProcSignalInit(const uint8 *cancel_key, int cancel_key_len)
182182
old_pss_pid = pg_atomic_read_u32(&slot->pss_pid);
183183

184184
/* Clear out any leftover signal reasons */
185-
MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));
185+
pg_atomic_write_u32(&slot->pss_signalFlags, 0);
186186

187187
/*
188188
* Initialize barrier state. Since we're a brand-new process, there
@@ -212,7 +212,7 @@ ProcSignalInit(const uint8 *cancel_key, int cancel_key_len)
212212
elog(LOG, "process %d taking over ProcSignal slot %d, but it's not empty",
213213
MyProcPid, MyProcNumber);
214214

215-
/* Remember slot location for CheckProcSignal */
215+
/* Remember slot location for HasProcSignalFlag */
216216
MyProcSignalSlot = slot;
217217

218218
/* Set up to release the slot on process exit */
@@ -294,10 +294,15 @@ SendProcSignal(pid_t pid, ProcSignalReason reason, ProcNumber procNumber)
294294
if (pg_atomic_read_u32(&slot->pss_pid) == pid)
295295
{
296296
/* Atomically set the proper flag */
297-
slot->pss_signalFlags[reason] = true;
297+
uint32 oldflags = pg_atomic_fetch_or_u32(&slot->pss_signalFlags,
298+
(uint32) 1 << reason);
299+
298300
SpinLockRelease(&slot->pss_mutex);
299-
/* Send signal */
300-
return kill(pid, SIGUSR1);
301+
/* Send signal only if there were no pending signals before this. */
302+
if (oldflags == 0)
303+
return kill(pid, SIGUSR1);
304+
else
305+
return 0;
301306
}
302307
SpinLockRelease(&slot->pss_mutex);
303308
}
@@ -322,10 +327,15 @@ SendProcSignal(pid_t pid, ProcSignalReason reason, ProcNumber procNumber)
322327
if (pg_atomic_read_u32(&slot->pss_pid) == pid)
323328
{
324329
/* Atomically set the proper flag */
325-
slot->pss_signalFlags[reason] = true;
330+
uint32 oldflags = pg_atomic_fetch_or_u32(&slot->pss_signalFlags,
331+
(uint32) 1 << reason);
332+
326333
SpinLockRelease(&slot->pss_mutex);
327-
/* Send signal */
328-
return kill(pid, SIGUSR1);
334+
/* Send signal only if there were no pending signals before this. */
335+
if (oldflags == 0)
336+
return kill(pid, SIGUSR1);
337+
else
338+
return 0;
329339
}
330340
SpinLockRelease(&slot->pss_mutex);
331341
}
@@ -404,9 +414,13 @@ EmitProcSignalBarrier(ProcSignalBarrierType type)
404414
if (pid != 0)
405415
{
406416
/* see SendProcSignal for details */
407-
slot->pss_signalFlags[PROCSIG_BARRIER] = true;
417+
uint32 oldflags = pg_atomic_fetch_or_u32(&slot->pss_signalFlags,
418+
(uint32) 1 << PROCSIG_BARRIER);
419+
408420
SpinLockRelease(&slot->pss_mutex);
409-
kill(pid, SIGUSR1);
421+
/* Send signal only if there were no pending signals before this. */
422+
if (oldflags == 0)
423+
kill(pid, SIGUSR1);
410424
}
411425
else
412426
SpinLockRelease(&slot->pss_mutex);
@@ -641,30 +655,13 @@ ResetProcSignalBarrierBits(uint32 flags)
641655
}
642656

643657
/*
644-
* CheckProcSignal - check to see if a particular reason has been
645-
* signaled, and clear the signal flag. Should be called after receiving
646-
* SIGUSR1.
658+
* HasProcSignalFlag - check to see if a particular reason has been
659+
* signaled in the given flags bitmask.
647660
*/
648661
static bool
649-
CheckProcSignal(ProcSignalReason reason)
662+
HasProcSignalFlag(uint32 flags, ProcSignalReason reason)
650663
{
651-
volatile ProcSignalSlot *slot = MyProcSignalSlot;
652-
653-
if (slot != NULL)
654-
{
655-
/*
656-
* Careful here --- don't clear flag if we haven't seen it set.
657-
* pss_signalFlags is of type "volatile sig_atomic_t" to allow us to
658-
* read it here safely, without holding the spinlock.
659-
*/
660-
if (slot->pss_signalFlags[reason])
661-
{
662-
slot->pss_signalFlags[reason] = false;
663-
return true;
664-
}
665-
}
666-
667-
return false;
664+
return (flags & ((uint32) 1 << reason)) != 0;
668665
}
669666

670667
/*
@@ -673,46 +670,54 @@ CheckProcSignal(ProcSignalReason reason)
673670
void
674671
procsignal_sigusr1_handler(SIGNAL_ARGS)
675672
{
676-
if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT))
673+
uint32 flags;
674+
675+
/* Atomically consume all pending signals */
676+
if (MyProcSignalSlot)
677+
flags = pg_atomic_exchange_u32(&MyProcSignalSlot->pss_signalFlags, 0);
678+
else
679+
flags = 0;
680+
681+
if (HasProcSignalFlag(flags, PROCSIG_CATCHUP_INTERRUPT))
677682
HandleCatchupInterrupt();
678683

679-
if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
684+
if (HasProcSignalFlag(flags, PROCSIG_NOTIFY_INTERRUPT))
680685
HandleNotifyInterrupt();
681686

682-
if (CheckProcSignal(PROCSIG_PARALLEL_MESSAGE))
687+
if (HasProcSignalFlag(flags, PROCSIG_PARALLEL_MESSAGE))
683688
HandleParallelMessageInterrupt();
684689

685-
if (CheckProcSignal(PROCSIG_WALSND_INIT_STOPPING))
690+
if (HasProcSignalFlag(flags, PROCSIG_WALSND_INIT_STOPPING))
686691
HandleWalSndInitStopping();
687692

688-
if (CheckProcSignal(PROCSIG_BARRIER))
693+
if (HasProcSignalFlag(flags, PROCSIG_BARRIER))
689694
HandleProcSignalBarrierInterrupt();
690695

691-
if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
696+
if (HasProcSignalFlag(flags, PROCSIG_LOG_MEMORY_CONTEXT))
692697
HandleLogMemoryContextInterrupt();
693698

694-
if (CheckProcSignal(PROCSIG_PARALLEL_APPLY_MESSAGE))
699+
if (HasProcSignalFlag(flags, PROCSIG_PARALLEL_APPLY_MESSAGE))
695700
HandleParallelApplyMessageInterrupt();
696701

697-
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
702+
if (HasProcSignalFlag(flags, PROCSIG_RECOVERY_CONFLICT_DATABASE))
698703
HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
699704

700-
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE))
705+
if (HasProcSignalFlag(flags, PROCSIG_RECOVERY_CONFLICT_TABLESPACE))
701706
HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
702707

703-
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK))
708+
if (HasProcSignalFlag(flags, PROCSIG_RECOVERY_CONFLICT_LOCK))
704709
HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);
705710

706-
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
711+
if (HasProcSignalFlag(flags, PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
707712
HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
708713

709-
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT))
714+
if (HasProcSignalFlag(flags, PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT))
710715
HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT);
711716

712-
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK))
717+
if (HasProcSignalFlag(flags, PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK))
713718
HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
714719

715-
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
720+
if (HasProcSignalFlag(flags, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
716721
HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
717722

718723
SetLatch(MyLatch);

src/include/storage/procsignal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ typedef enum
5151

5252
#define NUM_PROCSIGNALS (PROCSIG_RECOVERY_CONFLICT_LAST + 1)
5353

54+
StaticAssertDecl(NUM_PROCSIGNALS <= 32,
55+
"NUM_PROCSIGNALS must fit into ProcSignalSlot.pss_signalFlags");
56+
5457
typedef enum
5558
{
5659
PROCSIGNAL_BARRIER_SMGRRELEASE, /* ask smgr to close files */

0 commit comments

Comments
 (0)