On Thu, Oct 21, 2021 at 8:27 AM Andres Freund <and...@anarazel.de> wrote:
> On 2021-10-21 07:55:54 +1300, Thomas Munro wrote:
> > I wonder if we really need signals to implement interrupts.  Given
> > that they are non-preemptive/cooperative (work happens at the next
> > CFI()), why not just use shared memory flags and latches?  That skips
> > a bunch of code, global variables and scary warnings about programming
> > in signal handlers.
>
> Depending on how you implement them, one difference could be whether / when
> "slow" system calls (recv, poll, etc) are interrupted.

Hopefully by now all such waits are implemented with latch.c facilities?

> Another is that that signal handling provides a memory barrier in the
> receiving process. For things that rarely change (like most interrupts), it
> can be more efficient to move the cost of that out-of-line, instead of
> incurring them on every check.

Agreed, but in this experiment I was trying out the idea that a memory
barrier is not really needed at all, unless you're about to go to
sleep.  We already insert one of those before a latch wait.  That is,
if we see !set->latch->is_set, we do pg_memory_barrier() and check
again, before sleeping, so your next CFI must see the flag.  For
computation loops (sort, hash, query execution, ...), I speculate that
a relaxed read of memory is fine... you'll see the flag pretty soon,
and you certainly won't be allowed to finish your computation and go
to sleep.

> One nice thing of putting the state variables into shared memory would be that
> that'd allow to see the pending interrupts of other backends for debugging
> purposes.

+1

> > One idea is to convert those into "procsignals" too, for
> > consistency.  In the attached, they can be set (ie by the same
> > backend) with ProcSignalRaise(), but it's possible that in future we
> > might have a reason for another backend to set them too, so it seems
> > like a good idea to have a single system, effectively merging the
> > concepts of "procsignals" and "interrupts".
>
> This seems a bit confusing to me. For one, we need to have interrupts working
> before we have a proc, IIRC. But leaving details like that aside, it just
> seems a bit backwards to me. I'm on board with other backends directly setting
> interrupt flags, but it seems to me that the procsignal stuff should be
> "client" of the process-local interrupt infrastructure, rather than the other
> way round.

Hmm.  Yeah, I see your point.  But I can also think of some arguments
for merging the concepts of local and shared interrupts; see below.

In this new sketch, I tried doing it the other way around.  That is,
completely removing the concept of "ProcSignal", leaving only
"Interrupts".  Initially, MyPendingInterrupts points to something
private, and once you're connected to shared memory it points to
MyProc->pending_interrupts.

> > +bool
> > +ProcSignalAnyPending(void)
> > +{
> > +     volatile ProcSignalSlot *slot = MyProcSignalSlot;
> >
> > -     if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
> > -             
> > RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
> > +     /* XXX make this static inline? */
> > +     /* XXX point to a dummy entry instead of using NULL to avoid a branch 
> > */
> > +     return slot && slot->pss_signaled;
> > +}
>
> ISTM it might be easier to make this stuff efficiently race-free if we made
> this a count of pending operations.

Hmm, with a unified interrupt system and a bitmap it's not necessary
to have a separate flag/counter at all.

> > @@ -3131,12 +3124,13 @@ ProcessInterrupts(void)
> >       /* OK to accept any interrupts now? */
> >       if (InterruptHoldoffCount != 0 || CritSectionCount != 0)
> >               return;
> > -     InterruptPending = false;
> > +     ProcSignalClearAnyPending();
> > +
> > +     pg_read_barrier();
> >
> > -     if (ProcDiePending)
> > +     if (ProcSignalConsume(PROCSIG_DIE))
> >       {
>
> I think making all of these checks into function calls isn't great. How about
> making the set of pending signals a bitmask? That'd allow to efficiently check
> a bunch of interrupts together and even where not, it'd just be a single test
> of the mask, likely already in a register.

+1.

Some assorted notes:

1.  Aside from doing interrupts in this new way, I also have the
postmaster setting latches (!) instead of sending ad hoc SIGUSR1 here
and there.  My main reason for doing that was to be able to chase out
all reasons to register a SIGUSR1 handler, so I could prove that
check-world passes.  I like it, though. But maybe it's really a
separate topic.

2.  I moved this stuff into interrupt.{h,c}.  There is nothing left in
procsignal.c except code relating to ProcSignalBarrier.  I guess that
thing could use another name, anyway.  It's a ...
SystemInterruptBarrier?

3.  Child-level SIGINT and SIGTERM handlers probably aren't really
necessary, either: maybe the sender could do
InterruptSend(INTERRUPT_{DIE,QUERY_CANCEL}, pgprocno) instead?  But
perhaps people are attached to being able to send those signals from
external programs directly to backends.

4.  Like the above, a SIGALRM handler might need to do eg
InterruptRaise(INTERRUPT_STATEMENT_TIMEOUT).  That's a problem for
systems using spinlocks (self-deadlock against user context in
InterruptRaise()), so I'd need to come up with some flag protocol for
dinosaurs to make that safe, OR revert to having these "local only"
interrupts done with separate flags, as you were getting at earlier.

5.  The reason I prefer to put currently "local only" interrupts into
the same atomic system is that I speculate that ultimately all of the
backend-level signal handlers won't be needed.  They all fall into
three categories: (1) could be replaced with these interrupts
directly, (2) could be replaced by the new timer infrastructure that
multithreaded postgres would need to have to deliver interrupts to the
right recipients, (3) are quickdie and can be handled at the
containing process level.  Then the only signal handlers left are top
level external ones.

But perhaps you're right and I should try reintroducing separate local
interrupts for now.  I dunno, I like the simplicity of the unified
system; if only it weren't for those spinlock-backed atomics.
From 51ca49de0437dd139913d07aa4955e9f7841ccc5 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.mu...@gmail.com>
Date: Sat, 5 Jun 2021 17:13:11 +1200
Subject: [PATCH] Refactor interrupt system.

Previously, "procsignals" were implemented with shared memory flags and
a SIGUSR1 handler that would read shared memory "reason" flags an set
process-local "interrupt" flag variables and then set the process latch
for race-free wakeup.  The next call to CHECK_FOR_INTERRUPTS() would see
the interrupt flags and perform the relevant action.

Instead, have the sender set the latch directly, have
CHECK_FOR_INTERRUPTS() read the flags directly from shared memory.  This
drops a bunch of signal handlers and globals variables.

There are several other conditions that also handled by
CHECK_FOR_INTERRUPTS(), but are not generated by other backends sending
signals, or are set by other signal handlers; these include interrupts
triggered by timers and socket events, and are made into "interrupts"
too, for consistency.

Also replace some other users of SIGUSR1 that could just as easily use
latches.  This breaks a traditional rule that the postmaster can't touch
latches, but allows the SIGUSR1 handler to be completely removed.

XXX Experimental code, just trying out ideas!
XXX The timeout stuff needs more work
XXX The recovery conflict stuff needs more work
XXX Needs more work to handle emulated atomics from signal handlers

Discussion: https://postgr.es/m/CA%2BhUKG%2B3MkS21yK4jL4cgZywdnnGKiBg0jatoV6kzaniBmcqbQ%40mail.gmail.com
---
 contrib/pg_prewarm/autoprewarm.c            |   2 +-
 src/backend/access/transam/parallel.c       |  34 +---
 src/backend/commands/async.c                | 110 +++-------
 src/backend/commands/vacuum.c               |   2 +-
 src/backend/libpq/pqcomm.c                  |   4 +-
 src/backend/libpq/pqmq.c                    |  15 +-
 src/backend/optimizer/util/pathnode.c       |   1 +
 src/backend/postmaster/autovacuum.c         |  14 +-
 src/backend/postmaster/bgworker.c           |  20 +-
 src/backend/postmaster/bgwriter.c           |   2 +-
 src/backend/postmaster/checkpointer.c       |   6 +-
 src/backend/postmaster/interrupt.c          |  51 ++++-
 src/backend/postmaster/pgarch.c             |   4 +-
 src/backend/postmaster/pgstat.c             |  12 +-
 src/backend/postmaster/startup.c            |   4 +-
 src/backend/postmaster/walwriter.c          |   4 +-
 src/backend/regex/regcomp.c                 |   4 +-
 src/backend/replication/logical/launcher.c  |  20 +-
 src/backend/replication/logical/tablesync.c |   1 +
 src/backend/replication/syncrep.c           |   7 +-
 src/backend/replication/walreceiver.c       |   2 +-
 src/backend/replication/walsender.c         |   7 +-
 src/backend/storage/buffer/bufmgr.c         |   8 +-
 src/backend/storage/ipc/dsm_impl.c          |   3 +-
 src/backend/storage/ipc/ipc.c               |   6 +-
 src/backend/storage/ipc/procarray.c         |  21 +-
 src/backend/storage/ipc/procsignal.c        | 212 ++------------------
 src/backend/storage/ipc/sinval.c            |  45 +----
 src/backend/storage/ipc/sinvaladt.c         |   9 +-
 src/backend/storage/ipc/standby.c           |  46 ++---
 src/backend/storage/lmgr/proc.c             |  10 +-
 src/backend/tcop/postgres.c                 | 133 ++++++------
 src/backend/utils/adt/mcxtfuncs.c           |   8 +-
 src/backend/utils/init/globals.c            |  15 +-
 src/backend/utils/init/postinit.c           |   9 +-
 src/backend/utils/mmgr/mcxt.c               |  18 --
 src/include/access/parallel.h               |   1 -
 src/include/commands/async.h                |   4 -
 src/include/libpq/pqmq.h                    |   2 +-
 src/include/miscadmin.h                     |  66 +-----
 src/include/postmaster/bgworker.h           |   1 +
 src/include/postmaster/interrupt.h          | 171 +++++++++++++++-
 src/include/regex/regcustom.h               |   1 +
 src/include/replication/walsender_private.h |   1 +
 src/include/storage/proc.h                  |   2 +
 src/include/storage/procarray.h             |   6 +-
 src/include/storage/procsignal.h            |  34 ----
 src/include/storage/sinval.h                |   5 -
 src/include/storage/standby.h               |   3 +-
 src/include/tcop/tcopprot.h                 |   4 +-
 50 files changed, 477 insertions(+), 693 deletions(-)

diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index 0289ea657c..d2662a90c4 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -159,7 +159,7 @@ autoprewarm_main(Datum main_arg)
 	/* Establish signal handlers; once that's done, unblock signals. */
 	pqsignal(SIGTERM, SignalHandlerForShutdownRequest);
 	pqsignal(SIGHUP, SignalHandlerForConfigReload);
-	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+	pqsignal(SIGUSR1, SIG_IGN);
 	BackgroundWorkerUnblockSignals();
 
 	/* Create (if necessary) and attach to our shared memory area. */
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index bb1881f573..3bb411d8e6 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -91,7 +91,6 @@ typedef struct FixedParallelState
 	bool		is_superuser;
 	PGPROC	   *parallel_leader_pgproc;
 	pid_t		parallel_leader_pid;
-	BackendId	parallel_leader_backend_id;
 	TimestampTz xact_ts;
 	TimestampTz stmt_ts;
 	SerializableXactHandle serializable_xact_handle;
@@ -124,7 +123,7 @@ static FixedParallelState *MyFixedParallelState;
 static dlist_head pcxt_list = DLIST_STATIC_INIT(pcxt_list);
 
 /* Backend-local copy of data from FixedParallelState. */
-static pid_t ParallelLeaderPid;
+static int ParallelLeaderPgprocno;
 
 /*
  * List of internal parallel worker entry points.  We need this for
@@ -328,7 +327,6 @@ InitializeParallelDSM(ParallelContext *pcxt)
 						  &fps->temp_toast_namespace_id);
 	fps->parallel_leader_pgproc = MyProc;
 	fps->parallel_leader_pid = MyProcPid;
-	fps->parallel_leader_backend_id = MyBackendId;
 	fps->xact_ts = GetCurrentTransactionStartTimestamp();
 	fps->stmt_ts = GetCurrentStatementStartTimestamp();
 	fps->serializable_xact_handle = ShareSerializableXact();
@@ -569,7 +567,7 @@ LaunchParallelWorkers(ParallelContext *pcxt)
 	sprintf(worker.bgw_library_name, "postgres");
 	sprintf(worker.bgw_function_name, "ParallelWorkerMain");
 	worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(pcxt->seg));
-	worker.bgw_notify_pid = MyProcPid;
+	worker.bgw_notify_pgprocno = MyProc->pgprocno;
 
 	/*
 	 * Start workers.
@@ -992,21 +990,6 @@ ParallelContextActive(void)
 	return !dlist_is_empty(&pcxt_list);
 }
 
-/*
- * Handle receipt of an interrupt indicating a parallel worker message.
- *
- * Note: this is called within a signal handler!  All we can do is set
- * a flag that will cause the next CHECK_FOR_INTERRUPTS() to invoke
- * HandleParallelMessages().
- */
-void
-HandleParallelMessageInterrupt(void)
-{
-	InterruptPending = true;
-	ParallelMessagePending = true;
-	SetLatch(MyLatch);
-}
-
 /*
  * Handle any queued protocol messages received from parallel workers.
  */
@@ -1315,9 +1298,8 @@ ParallelWorkerMain(Datum main_arg)
 	fps = shm_toc_lookup(toc, PARALLEL_KEY_FIXED, false);
 	MyFixedParallelState = fps;
 
-	/* Arrange to signal the leader if we exit. */
-	ParallelLeaderPid = fps->parallel_leader_pid;
-	ParallelLeaderBackendId = fps->parallel_leader_backend_id;
+	/* Arrange to interrupt the leader if we exit. */
+	ParallelLeaderPgprocno = fps->parallel_leader_pgproc->pgprocno;
 	before_shmem_exit(ParallelWorkerShutdown, PointerGetDatum(seg));
 
 	/*
@@ -1332,8 +1314,7 @@ ParallelWorkerMain(Datum main_arg)
 	shm_mq_set_sender(mq, MyProc);
 	mqh = shm_mq_attach(mq, seg, NULL);
 	pq_redirect_to_shm_mq(seg, mqh);
-	pq_set_parallel_leader(fps->parallel_leader_pid,
-						   fps->parallel_leader_backend_id);
+	pq_set_parallel_leader(fps->parallel_leader_pgproc->pgprocno);
 
 	/*
 	 * Send a BackendKeyData message to the process that initiated parallelism
@@ -1545,9 +1526,8 @@ ParallelWorkerReportLastRecEnd(XLogRecPtr last_xlog_end)
 static void
 ParallelWorkerShutdown(int code, Datum arg)
 {
-	SendProcSignal(ParallelLeaderPid,
-				   PROCSIG_PARALLEL_MESSAGE,
-				   ParallelLeaderBackendId);
+	InterruptSend(INTERRUPT_PARALLEL_MESSAGE,
+				  MyFixedParallelState->parallel_leader_pgproc->pgprocno);
 
 	dsm_detach((dsm_segment *) DatumGetPointer(arg));
 }
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 8557008545..65e34f8ea9 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -245,7 +245,7 @@ typedef struct QueuePosition
  */
 typedef struct QueueBackendStatus
 {
-	int32		pid;			/* either a PID or InvalidPid */
+	int32		pgprocno;		/* either a pgprocno or INVALID_PGPROCNO */
 	Oid			dboid;			/* backend's database OID, or InvalidOid */
 	BackendId	nextListener;	/* id of next listener, or InvalidBackendId */
 	QueuePosition pos;			/* backend has read queue up to here */
@@ -300,7 +300,7 @@ static AsyncQueueControl *asyncQueueControl;
 #define QUEUE_TAIL					(asyncQueueControl->tail)
 #define QUEUE_STOP_PAGE				(asyncQueueControl->stopPage)
 #define QUEUE_FIRST_LISTENER		(asyncQueueControl->firstListener)
-#define QUEUE_BACKEND_PID(i)		(asyncQueueControl->backend[i].pid)
+#define QUEUE_BACKEND_PGPROCNO(i)	(asyncQueueControl->backend[i].pgprocno)
 #define QUEUE_BACKEND_DBOID(i)		(asyncQueueControl->backend[i].dboid)
 #define QUEUE_NEXT_LISTENER(i)		(asyncQueueControl->backend[i].nextListener)
 #define QUEUE_BACKEND_POS(i)		(asyncQueueControl->backend[i].pos)
@@ -421,15 +421,6 @@ typedef struct NotificationHash
 
 static NotificationList *pendingNotifies = NULL;
 
-/*
- * Inbound notifications are initially processed by HandleNotifyInterrupt(),
- * called from inside a signal handler. That just sets the
- * notifyInterruptPending flag and sets the process
- * latch. ProcessNotifyInterrupt() will then be called whenever it's safe to
- * actually deal with the interrupt.
- */
-volatile sig_atomic_t notifyInterruptPending = false;
-
 /* True if we've registered an on_shmem_exit cleanup */
 static bool unlistenExitRegistered = false;
 
@@ -558,7 +549,7 @@ AsyncShmemInit(void)
 		/* zero'th entry won't be used, but let's initialize it anyway */
 		for (int i = 0; i <= MaxBackends; i++)
 		{
-			QUEUE_BACKEND_PID(i) = InvalidPid;
+			QUEUE_BACKEND_PGPROCNO(i) = INVALID_PGPROCNO;
 			QUEUE_BACKEND_DBOID(i) = InvalidOid;
 			QUEUE_NEXT_LISTENER(i) = InvalidBackendId;
 			SET_QUEUE_POS(QUEUE_BACKEND_POS(i), 0, 0);
@@ -1131,7 +1122,7 @@ Exec_ListenPreCommit(void)
 			prevListener = i;
 	}
 	QUEUE_BACKEND_POS(MyBackendId) = max;
-	QUEUE_BACKEND_PID(MyBackendId) = MyProcPid;
+	QUEUE_BACKEND_PGPROCNO(MyBackendId) = MyProc->pgprocno;
 	QUEUE_BACKEND_DBOID(MyBackendId) = MyDatabaseId;
 	/* Insert backend into list of listeners at correct position */
 	if (prevListener > 0)
@@ -1274,7 +1265,7 @@ asyncQueueUnregister(void)
 	 */
 	LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
 	/* Mark our entry as invalid */
-	QUEUE_BACKEND_PID(MyBackendId) = InvalidPid;
+	QUEUE_BACKEND_PGPROCNO(MyBackendId) = InvalidPid;
 	QUEUE_BACKEND_DBOID(MyBackendId) = InvalidOid;
 	/* and remove it from the list */
 	if (QUEUE_FIRST_LISTENER == MyBackendId)
@@ -1588,22 +1579,22 @@ asyncQueueFillWarning(void)
 								   t, QUEUE_FULL_WARN_INTERVAL))
 	{
 		QueuePosition min = QUEUE_HEAD;
-		int32		minPid = InvalidPid;
+		int32		minPgprocno = INVALID_PGPROCNO;
 
 		for (BackendId i = QUEUE_FIRST_LISTENER; i > 0; i = QUEUE_NEXT_LISTENER(i))
 		{
-			Assert(QUEUE_BACKEND_PID(i) != InvalidPid);
+			Assert(QUEUE_BACKEND_PGPROCNO(i) != INVALID_PGPROCNO);
 			min = QUEUE_POS_MIN(min, QUEUE_BACKEND_POS(i));
 			if (QUEUE_POS_EQUAL(min, QUEUE_BACKEND_POS(i)))
-				minPid = QUEUE_BACKEND_PID(i);
+				minPgprocno = QUEUE_BACKEND_PGPROCNO(i);
 		}
 
 		ereport(WARNING,
 				(errmsg("NOTIFY queue is %.0f%% full", fillDegree * 100),
-				 (minPid != InvalidPid ?
-				  errdetail("The server process with PID %d is among those with the oldest transactions.", minPid)
+				 (minPgprocno != INVALID_PGPROCNO ?
+				  errdetail("The server process with pgprocno %d is among those with the oldest transactions.", minPgprocno)
 				  : 0),
-				 (minPid != InvalidPid ?
+				 (minPgprocno != INVALID_PGPROCNO ?
 				  errhint("The NOTIFY queue cannot be emptied until that process ends its current transaction.")
 				  : 0)));
 
@@ -1629,8 +1620,7 @@ asyncQueueFillWarning(void)
 static void
 SignalBackends(void)
 {
-	int32	   *pids;
-	BackendId  *ids;
+	int32	   *pgprocnos;
 	int			count;
 
 	/*
@@ -1641,17 +1631,16 @@ SignalBackends(void)
 	 * XXX in principle these pallocs could fail, which would be bad. Maybe
 	 * preallocate the arrays?  They're not that large, though.
 	 */
-	pids = (int32 *) palloc(MaxBackends * sizeof(int32));
-	ids = (BackendId *) palloc(MaxBackends * sizeof(BackendId));
+	pgprocnos = (int32 *) palloc(MaxBackends * sizeof(int32));
 	count = 0;
 
 	LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
 	for (BackendId i = QUEUE_FIRST_LISTENER; i > 0; i = QUEUE_NEXT_LISTENER(i))
 	{
-		int32		pid = QUEUE_BACKEND_PID(i);
+		int32		pgprocno = QUEUE_BACKEND_PGPROCNO(i);
 		QueuePosition pos;
 
-		Assert(pid != InvalidPid);
+		Assert(pgprocno != INVALID_PGPROCNO);
 		pos = QUEUE_BACKEND_POS(i);
 		if (QUEUE_BACKEND_DBOID(i) == MyDatabaseId)
 		{
@@ -1672,40 +1661,17 @@ SignalBackends(void)
 								   QUEUE_POS_PAGE(pos)) < QUEUE_CLEANUP_DELAY)
 				continue;
 		}
-		/* OK, need to signal this one */
-		pids[count] = pid;
-		ids[count] = i;
+		/* OK, need to wake this one */
+		pgprocnos[count] = pgprocno;
 		count++;
 	}
 	LWLockRelease(NotifyQueueLock);
 
-	/* Now send signals */
+	/* Now send interrupts */
 	for (int i = 0; i < count; i++)
-	{
-		int32		pid = pids[i];
-
-		/*
-		 * If we are signaling our own process, no need to involve the kernel;
-		 * just set the flag directly.
-		 */
-		if (pid == MyProcPid)
-		{
-			notifyInterruptPending = true;
-			continue;
-		}
-
-		/*
-		 * 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.
-		 */
-		if (SendProcSignal(pid, PROCSIG_NOTIFY_INTERRUPT, ids[i]) < 0)
-			elog(DEBUG3, "could not signal backend with PID %d: %m", pid);
-	}
+		InterruptSend(INTERRUPT_NOTIFY, pgprocnos[i]);
 
-	pfree(pids);
-	pfree(ids);
+	pfree(pgprocnos);
 }
 
 /*
@@ -1841,37 +1807,12 @@ AtSubAbort_Notify(void)
 	}
 }
 
-/*
- * HandleNotifyInterrupt
- *
- *		Signal handler portion of interrupt handling. Let the backend know
- *		that there's a pending notify interrupt. If we're currently reading
- *		from the client, this will interrupt the read and
- *		ProcessClientReadInterrupt() will call ProcessNotifyInterrupt().
- */
-void
-HandleNotifyInterrupt(void)
-{
-	/*
-	 * Note: this is called by a SIGNAL HANDLER. You must be very wary what
-	 * you do here.
-	 */
-
-	/* signal that work needs to be done */
-	notifyInterruptPending = true;
-
-	/* make sure the event is processed in due course */
-	SetLatch(MyLatch);
-}
-
 /*
  * ProcessNotifyInterrupt
  *
- *		This is called if we see notifyInterruptPending set, just before
+ *		This is called if we see PROCSIG_NOTIFY_INTERRUPT set, just before
  *		transmitting ReadyForQuery at the end of a frontend command, and
  *		also if a notify signal occurs while reading from the frontend.
- *		HandleNotifyInterrupt() will cause the read to be interrupted
- *		via the process's latch, and this routine will get called.
  *		If we are truly idle (ie, *not* inside a transaction block),
  *		process the incoming notifies.
  *
@@ -1886,7 +1827,7 @@ ProcessNotifyInterrupt(bool flush)
 		return;					/* not really idle */
 
 	/* Loop in case another signal arrives while sending messages */
-	while (notifyInterruptPending)
+	while (InterruptConsume(INTERRUPT_NOTIFY))
 		ProcessIncomingNotify(flush);
 }
 
@@ -1913,7 +1854,7 @@ asyncQueueReadAllNotifications(void)
 	/* Fetch current state */
 	LWLockAcquire(NotifyQueueLock, LW_SHARED);
 	/* Assert checks that we have a valid state entry */
-	Assert(MyProcPid == QUEUE_BACKEND_PID(MyBackendId));
+	Assert(MyProc->pgprocno == QUEUE_BACKEND_PGPROCNO(MyBackendId));
 	pos = QUEUE_BACKEND_POS(MyBackendId);
 	head = QUEUE_HEAD;
 	LWLockRelease(NotifyQueueLock);
@@ -2185,7 +2126,7 @@ asyncQueueAdvanceTail(void)
 	min = QUEUE_HEAD;
 	for (BackendId i = QUEUE_FIRST_LISTENER; i > 0; i = QUEUE_NEXT_LISTENER(i))
 	{
-		Assert(QUEUE_BACKEND_PID(i) != InvalidPid);
+		Assert(QUEUE_BACKEND_PGPROCNO(i) != INVALID_PGPROCNO);
 		min = QUEUE_POS_MIN(min, QUEUE_BACKEND_POS(i));
 	}
 	QUEUE_TAIL = min;
@@ -2236,9 +2177,6 @@ asyncQueueAdvanceTail(void)
 static void
 ProcessIncomingNotify(bool flush)
 {
-	/* We *must* reset the flag */
-	notifyInterruptPending = false;
-
 	/* Do nothing else if we aren't actively listening */
 	if (listenChannels == NIL)
 		return;
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 5c4bc15b44..35ea833e44 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2153,7 +2153,7 @@ vacuum_delay_point(void)
 	/* Always check for interrupts */
 	CHECK_FOR_INTERRUPTS();
 
-	if (!VacuumCostActive || InterruptPending)
+	if (!VacuumCostActive || INTERRUPTS_PENDING_CONDITION())
 		return;
 
 	/*
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 9ebba025cc..e18f1e7bcd 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -79,6 +79,7 @@
 #include "libpq/libpq.h"
 #include "miscadmin.h"
 #include "port/pg_bswap.h"
+#include "postmaster/interrupt.h"
 #include "storage/ipc.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
@@ -1402,8 +1403,7 @@ internal_flush(void)
 			 * the connection.
 			 */
 			PqSendStart = PqSendPointer = 0;
-			ClientConnectionLost = 1;
-			InterruptPending = 1;
+			InterruptRaise(INTERRUPT_CONNECTION_LOST);
 			return EOF;
 		}
 
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index 846494bf44..a69fa07ca7 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -23,8 +23,7 @@
 
 static shm_mq_handle *pq_mq_handle;
 static bool pq_mq_busy = false;
-static pid_t pq_mq_parallel_leader_pid = 0;
-static pid_t pq_mq_parallel_leader_backend_id = InvalidBackendId;
+static int pq_mq_parallel_leader_pgprocno = 0;
 
 static void pq_cleanup_redirect_to_shm_mq(dsm_segment *seg, Datum arg);
 static void mq_comm_reset(void);
@@ -73,11 +72,10 @@ pq_cleanup_redirect_to_shm_mq(dsm_segment *seg, Datum arg)
  * message data via the shm_mq.
  */
 void
-pq_set_parallel_leader(pid_t pid, BackendId backend_id)
+pq_set_parallel_leader(int pgprocno)
 {
 	Assert(PqCommMethods == &PqCommMqMethods);
-	pq_mq_parallel_leader_pid = pid;
-	pq_mq_parallel_leader_backend_id = backend_id;
+	pq_mq_parallel_leader_pgprocno = pgprocno;
 }
 
 static void
@@ -161,10 +159,9 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		 */
 		result = shm_mq_sendv(pq_mq_handle, iov, 2, true, true);
 
-		if (pq_mq_parallel_leader_pid != 0)
-			SendProcSignal(pq_mq_parallel_leader_pid,
-						   PROCSIG_PARALLEL_MESSAGE,
-						   pq_mq_parallel_leader_backend_id);
+		if (pq_mq_parallel_leader_pgprocno != INVALID_PGPROCNO)
+			InterruptSend(INTERRUPT_PARALLEL_MESSAGE,
+						  pq_mq_parallel_leader_pgprocno);
 
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index e53d381e19..2739114ab7 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -18,6 +18,7 @@
 
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
+#include "postmaster/interrupt.h"
 #include "nodes/extensible.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/appendinfo.h"
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 96332320a7..d4500a4f22 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -463,7 +463,7 @@ AutoVacLauncherMain(int argc, char *argv[])
 	InitializeTimeouts();		/* establishes SIGALRM handler */
 
 	pqsignal(SIGPIPE, SIG_IGN);
-	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+	pqsignal(SIGUSR1, SIG_IGN);
 	pqsignal(SIGUSR2, avl_sigusr2_handler);
 	pqsignal(SIGFPE, FloatExceptionHandler);
 	pqsignal(SIGCHLD, SIG_DFL);
@@ -517,7 +517,7 @@ AutoVacLauncherMain(int argc, char *argv[])
 
 		/* Forget any pending QueryCancel or timeout request */
 		disable_all_timeouts(false);
-		QueryCancelPending = false; /* second to avoid race condition */
+		InterruptClear(INTERRUPT_QUERY_CANCEL); /* second to avoid race condition */
 
 		/* Report the error to the server log */
 		EmitErrorReport();
@@ -833,11 +833,11 @@ HandleAutoVacLauncherInterrupts(void)
 	}
 
 	/* Process barrier events */
-	if (ProcSignalBarrierPending)
+	if (InterruptConsume(INTERRUPT_BARRIER))
 		ProcessProcSignalBarrier();
 
 	/* Perform logging of memory contexts of this process */
-	if (LogMemoryContextPending)
+	if (InterruptConsume(INTERRUPT_LOG_MEMORY_CONTEXT))
 		ProcessLogMemoryContextInterrupt();
 
 	/* Process sinval catchup interrupts that happened while sleeping */
@@ -1545,7 +1545,7 @@ AutoVacWorkerMain(int argc, char *argv[])
 	InitializeTimeouts();		/* establishes SIGALRM handler */
 
 	pqsignal(SIGPIPE, SIG_IGN);
-	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+	pqsignal(SIGUSR1, SIG_IGN);
 	pqsignal(SIGUSR2, SIG_IGN);
 	pqsignal(SIGFPE, FloatExceptionHandler);
 	pqsignal(SIGCHLD, SIG_DFL);
@@ -2499,7 +2499,7 @@ do_autovacuum(void)
 			 * current table (we're done with it, so it would make no sense to
 			 * cancel at this point.)
 			 */
-			QueryCancelPending = false;
+			InterruptClear(INTERRUPT_QUERY_CANCEL);
 		}
 		PG_CATCH();
 		{
@@ -2696,7 +2696,7 @@ perform_work_item(AutoVacuumWorkItem *workitem)
 		 * (we're done with it, so it would make no sense to cancel at this
 		 * point.)
 		 */
-		QueryCancelPending = false;
+		InterruptClear(INTERRUPT_QUERY_CANCEL);
 	}
 	PG_CATCH();
 	{
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index c05f500639..ac57ad170c 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -190,6 +190,7 @@ BackgroundWorkerShmemInit(void)
 			slot->generation = 0;
 			rw->rw_shmem_slot = slotno;
 			rw->rw_worker.bgw_notify_pid = 0;	/* might be reinit after crash */
+			rw->rw_worker.bgw_notify_pgprocno = 0;
 			memcpy(&slot->worker, &rw->rw_worker, sizeof(BackgroundWorker));
 			++slotno;
 		}
@@ -318,6 +319,7 @@ BackgroundWorkerStateChange(bool allow_new_workers)
 		if (slot->terminate)
 		{
 			int			notify_pid;
+			int			notify_pgprocno;
 
 			/*
 			 * We need a memory barrier here to make sure that the load of
@@ -325,6 +327,7 @@ BackgroundWorkerStateChange(bool allow_new_workers)
 			 * complete before the store to in_use.
 			 */
 			notify_pid = slot->worker.bgw_notify_pid;
+			notify_pgprocno = slot->worker.bgw_notify_pgprocno;
 			if ((slot->worker.bgw_flags & BGWORKER_CLASS_PARALLEL) != 0)
 				BackgroundWorkerData->parallel_terminate_count++;
 			slot->pid = 0;
@@ -334,6 +337,8 @@ BackgroundWorkerStateChange(bool allow_new_workers)
 
 			if (notify_pid != 0)
 				kill(notify_pid, SIGUSR1);
+			if (notify_pgprocno != 0)
+				SetLatch(&ProcGlobal->allProcs[notify_pgprocno].procLatch);
 
 			continue;
 		}
@@ -393,6 +398,7 @@ BackgroundWorkerStateChange(bool allow_new_workers)
 				 (long) rw->rw_worker.bgw_notify_pid);
 			rw->rw_worker.bgw_notify_pid = 0;
 		}
+		rw->rw_worker.bgw_notify_pgprocno = slot->worker.bgw_notify_pgprocno;
 
 		/* Initialize postmaster bookkeeping. */
 		rw->rw_backend = NULL;
@@ -468,6 +474,8 @@ ReportBackgroundWorkerPID(RegisteredBgWorker *rw)
 
 	if (rw->rw_worker.bgw_notify_pid != 0)
 		kill(rw->rw_worker.bgw_notify_pid, SIGUSR1);
+	if (rw->rw_worker.bgw_notify_pgprocno != 0)
+		SetLatch(&ProcGlobal->allProcs[rw->rw_worker.bgw_notify_pgprocno].procLatch);
 }
 
 /*
@@ -482,6 +490,7 @@ ReportBackgroundWorkerExit(slist_mutable_iter *cur)
 	RegisteredBgWorker *rw;
 	BackgroundWorkerSlot *slot;
 	int			notify_pid;
+	int			notify_pgprocno;
 
 	rw = slist_container(RegisteredBgWorker, rw_lnode, cur->cur);
 
@@ -489,6 +498,7 @@ ReportBackgroundWorkerExit(slist_mutable_iter *cur)
 	slot = &BackgroundWorkerData->slot[rw->rw_shmem_slot];
 	slot->pid = rw->rw_pid;
 	notify_pid = rw->rw_worker.bgw_notify_pid;
+	notify_pgprocno = rw->rw_worker.bgw_notify_pgprocno;
 
 	/*
 	 * If this worker is slated for deregistration, do that before notifying
@@ -503,6 +513,8 @@ ReportBackgroundWorkerExit(slist_mutable_iter *cur)
 
 	if (notify_pid != 0)
 		kill(notify_pid, SIGUSR1);
+	if (notify_pgprocno != 0)
+		SetLatch(&ProcGlobal->allProcs[notify_pgprocno].procLatch);
 }
 
 /*
@@ -553,14 +565,18 @@ ForgetUnstartedBackgroundWorkers(void)
 
 		/* If it's not yet started, and there's someone waiting ... */
 		if (slot->pid == InvalidPid &&
-			rw->rw_worker.bgw_notify_pid != 0)
+			(rw->rw_worker.bgw_notify_pid != 0 ||
+			 rw->rw_worker.bgw_notify_pgprocno != 0))
 		{
 			/* ... then zap it, and notify the waiter */
 			int			notify_pid = rw->rw_worker.bgw_notify_pid;
+			int			notify_pgprocno = rw->rw_worker.bgw_notify_pgprocno;
 
 			ForgetBackgroundWorker(&iter);
 			if (notify_pid != 0)
 				kill(notify_pid, SIGUSR1);
+			if (notify_pgprocno != 0)
+				SetLatch(&ProcGlobal->allProcs[notify_pgprocno].procLatch);
 		}
 	}
 }
@@ -618,6 +634,7 @@ ResetBackgroundWorkerCrashTimes(void)
 			 * If there was anyone waiting for it, they're history.
 			 */
 			rw->rw_worker.bgw_notify_pid = 0;
+			rw->rw_worker.bgw_notify_pgprocno = 0;
 		}
 	}
 }
@@ -767,7 +784,6 @@ StartBackgroundWorker(void)
 		 * SIGINT is used to signal canceling the current action
 		 */
 		pqsignal(SIGINT, StatementCancelHandler);
-		pqsignal(SIGUSR1, procsignal_sigusr1_handler);
 		pqsignal(SIGFPE, FloatExceptionHandler);
 
 		/* XXX Any other handlers needed here? */
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 5584f4bc24..3879acfa6c 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -104,7 +104,7 @@ BackgroundWriterMain(void)
 	/* SIGQUIT handler was already set up by InitPostmasterChild */
 	pqsignal(SIGALRM, SIG_IGN);
 	pqsignal(SIGPIPE, SIG_IGN);
-	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+	pqsignal(SIGUSR1, SIG_IGN);
 	pqsignal(SIGUSR2, SIG_IGN);
 
 	/*
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index be7366379d..4ad161a89b 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -198,7 +198,7 @@ CheckpointerMain(void)
 	/* SIGQUIT handler was already set up by InitPostmasterChild */
 	pqsignal(SIGALRM, SIG_IGN);
 	pqsignal(SIGPIPE, SIG_IGN);
-	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+	pqsignal(SIGUSR1, SIG_IGN);
 	pqsignal(SIGUSR2, SignalHandlerForShutdownRequest);
 
 	/*
@@ -537,7 +537,7 @@ CheckpointerMain(void)
 static void
 HandleCheckpointerInterrupts(void)
 {
-	if (ProcSignalBarrierPending)
+	if (InterruptPending(INTERRUPT_BARRIER))
 		ProcessProcSignalBarrier();
 
 	if (ConfigReloadPending)
@@ -739,7 +739,7 @@ CheckpointWriteDelay(int flags, double progress)
 	}
 
 	/* Check for barrier events. */
-	if (ProcSignalBarrierPending)
+	if (InterruptPending(INTERRUPT_BARRIER))
 		ProcessProcSignalBarrier();
 }
 
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index dd9136a942..8ec90bc4e3 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -17,22 +17,71 @@
 #include <unistd.h>
 
 #include "miscadmin.h"
+#include "port/atomics.h"
 #include "postmaster/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/latch.h"
+#include "storage/proc.h"
 #include "storage/procsignal.h"
 #include "utils/guc.h"
 
 volatile sig_atomic_t ConfigReloadPending = false;
 volatile sig_atomic_t ShutdownRequestPending = false;
 
+static pg_atomic_uint32 LocalPendingInterrupts;
+
+pg_atomic_uint32 *MyPendingInterrupts = &LocalPendingInterrupts;
+
+/*
+ * Switch to local interrupts.
+ */
+void
+InterruptLocal(void)
+{
+	pg_atomic_fetch_or_u32(&LocalPendingInterrupts, pg_atomic_read_u32(MyPendingInterrupts));
+	MyPendingInterrupts = &LocalPendingInterrupts;
+}
+
+/*
+ * Switch to shared memory interrupts.
+ */
+void
+InterruptShared(void)
+{
+	pg_atomic_fetch_or_u32(&MyProc->pending_interrupts, pg_atomic_read_u32(MyPendingInterrupts));
+	MyPendingInterrupts = &MyProc->pending_interrupts;
+}
+
+/*
+ * Set an interrupt flag in this backend.
+ */
+void
+InterruptRaise(InterruptType reason)
+{
+	pg_atomic_fetch_or_u32(MyPendingInterrupts, 1 << reason);
+	SetLatch(MyLatch);
+}
+
+/*
+ * Set an interrupt flag in another backend.
+ */
+void
+InterruptSend(InterruptType reason, int pgprocno)
+{
+	PGPROC *proc;
+
+	proc = &ProcGlobal->allProcs[pgprocno];
+	pg_atomic_fetch_or_u32(&proc->pending_interrupts, 1 << reason);
+	SetLatch(&proc->procLatch);
+}
+
 /*
  * Simple interrupt handler for main loops of background processes.
  */
 void
 HandleMainLoopInterrupts(void)
 {
-	if (ProcSignalBarrierPending)
+	if (InterruptConsume(INTERRUPT_BARRIER))
 		ProcessProcSignalBarrier();
 
 	if (ConfigReloadPending)
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 74a7d7c4d0..217de4cac8 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -177,7 +177,7 @@ PgArchiverMain(void)
 	/* SIGQUIT handler was already set up by InitPostmasterChild */
 	pqsignal(SIGALRM, SIG_IGN);
 	pqsignal(SIGPIPE, SIG_IGN);
-	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+	pqsignal(SIGUSR1, SIG_IGN);
 	pqsignal(SIGUSR2, pgarch_waken_stop);
 
 	/* Reset some signals that are accepted by postmaster but not here */
@@ -707,7 +707,7 @@ pgarch_die(int code, Datum arg)
 static void
 HandlePgArchInterrupts(void)
 {
-	if (ProcSignalBarrierPending)
+	if (InterruptPending(INTERRUPT_BARRIER))
 		ProcessProcSignalBarrier();
 
 	if (ConfigReloadPending)
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 8c166e5e16..9585ef7c4a 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -5564,26 +5564,26 @@ pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len)
 
 	switch (msg->m_reason)
 	{
-		case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+		case INTERRUPT_RECOVERY_CONFLICT_DATABASE:
 
 			/*
 			 * Since we drop the information about the database as soon as it
 			 * replicates, there is no point in counting these conflicts.
 			 */
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+		case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE:
 			dbentry->n_conflict_tablespace++;
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_LOCK:
+		case INTERRUPT_RECOVERY_CONFLICT_LOCK:
 			dbentry->n_conflict_lock++;
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+		case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT:
 			dbentry->n_conflict_snapshot++;
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+		case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN:
 			dbentry->n_conflict_bufferpin++;
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+		case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
 			dbentry->n_conflict_startup_deadlock++;
 			break;
 	}
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 47ec737888..579d8b7040 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -198,7 +198,7 @@ HandleStartupProcInterrupts(void)
 		exit(1);
 
 	/* Process barrier events */
-	if (ProcSignalBarrierPending)
+	if (InterruptPending(INTERRUPT_BARRIER))
 		ProcessProcSignalBarrier();
 }
 
@@ -235,7 +235,7 @@ StartupProcessMain(void)
 	/* SIGQUIT handler was already set up by InitPostmasterChild */
 	InitializeTimeouts();		/* establishes SIGALRM handler */
 	pqsignal(SIGPIPE, SIG_IGN);
-	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+	pqsignal(SIGUSR1, SIG_IGN);
 	pqsignal(SIGUSR2, StartupProcTriggerHandler);
 
 	/*
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 626fae8454..727ad0dfd7 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -107,7 +107,7 @@ WalWriterMain(void)
 	/* SIGQUIT handler was already set up by InitPostmasterChild */
 	pqsignal(SIGALRM, SIG_IGN);
 	pqsignal(SIGPIPE, SIG_IGN);
-	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+	pqsignal(SIGUSR1, SIG_IGN);
 	pqsignal(SIGUSR2, SIG_IGN); /* not used */
 
 	/*
@@ -283,7 +283,7 @@ WalWriterMain(void)
 static void
 HandleWalWriterInterrupts(void)
 {
-	if (ProcSignalBarrierPending)
+	if (InterruptPending(INTERRUPT_BARRIER))
 		ProcessProcSignalBarrier();
 
 	if (ConfigReloadPending)
diff --git a/src/backend/regex/regcomp.c b/src/backend/regex/regcomp.c
index 473738040b..0319ee7595 100644
--- a/src/backend/regex/regcomp.c
+++ b/src/backend/regex/regcomp.c
@@ -2437,7 +2437,9 @@ rfree(regex_t *re)
 static int
 rcancelrequested(void)
 {
-	return InterruptPending && (QueryCancelPending || ProcDiePending);
+	return
+		InterruptPending(INTERRUPT_QUERY_CANCEL) ||
+		InterruptPending(INTERRUPT_DIE);
 }
 
 /*
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 3fb4caa803..bd4460ea72 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -59,7 +59,7 @@ LogicalRepWorker *MyLogicalRepWorker = NULL;
 typedef struct LogicalRepCtxStruct
 {
 	/* Supervisor process. */
-	pid_t		launcher_pid;
+	int			launcher_pgprocno;
 
 	/* Background workers. */
 	LogicalRepWorker workers[FLEXIBLE_ARRAY_MEMBER];
@@ -407,7 +407,7 @@ retry:
 	snprintf(bgw.bgw_type, BGW_MAXLEN, "logical replication worker");
 
 	bgw.bgw_restart_time = BGW_NEVER_RESTART;
-	bgw.bgw_notify_pid = MyProcPid;
+	bgw.bgw_notify_pgprocno = MyProc->pgprocno;
 	bgw.bgw_main_arg = Int32GetDatum(slot);
 
 	if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
@@ -632,7 +632,7 @@ logicalrep_worker_cleanup(LogicalRepWorker *worker)
 static void
 logicalrep_launcher_onexit(int code, Datum arg)
 {
-	LogicalRepCtx->launcher_pid = 0;
+	LogicalRepCtx->launcher_pgprocno = 0;
 }
 
 /*
@@ -722,7 +722,7 @@ ApplyLauncherRegister(void)
 	snprintf(bgw.bgw_type, BGW_MAXLEN,
 			 "logical replication launcher");
 	bgw.bgw_restart_time = 5;
-	bgw.bgw_notify_pid = 0;
+	bgw.bgw_notify_pgprocno = 0;
 	bgw.bgw_main_arg = (Datum) 0;
 
 	RegisterBackgroundWorker(&bgw);
@@ -791,8 +791,10 @@ ApplyLauncherWakeupAtCommit(void)
 static void
 ApplyLauncherWakeup(void)
 {
-	if (LogicalRepCtx->launcher_pid != 0)
-		kill(LogicalRepCtx->launcher_pid, SIGUSR1);
+	int pgprocno;
+
+	if ((pgprocno = LogicalRepCtx->launcher_pgprocno) != 0)
+		SetLatch(&ProcGlobal->allProcs[pgprocno].procLatch);
 }
 
 /*
@@ -808,8 +810,8 @@ ApplyLauncherMain(Datum main_arg)
 
 	before_shmem_exit(logicalrep_launcher_onexit, (Datum) 0);
 
-	Assert(LogicalRepCtx->launcher_pid == 0);
-	LogicalRepCtx->launcher_pid = MyProcPid;
+	Assert(LogicalRepCtx->launcher_pgprocno == 0);
+	LogicalRepCtx->launcher_pgprocno = MyProc->pgprocno;
 
 	/* Establish signal handlers. */
 	pqsignal(SIGHUP, SignalHandlerForConfigReload);
@@ -917,7 +919,7 @@ ApplyLauncherMain(Datum main_arg)
 bool
 IsLogicalLauncher(void)
 {
-	return LogicalRepCtx->launcher_pid == MyProcPid;
+	return LogicalRepCtx->launcher_pgprocno == MyProc->pgprocno;
 }
 
 /*
diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c
index f07983a43c..1e29f9bca0 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -103,6 +103,7 @@
 #include "miscadmin.h"
 #include "parser/parse_relation.h"
 #include "pgstat.h"
+#include "postmaster/interrupt.h"
 #include "replication/logicallauncher.h"
 #include "replication/logicalrelation.h"
 #include "replication/walreceiver.h"
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index bdbc9ef844..d3ded8fd98 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -261,7 +261,7 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * We do NOT reset ProcDiePending, so that the process will die after
 		 * the commit is cleaned up.
 		 */
-		if (ProcDiePending)
+		if (InterruptPending(INTERRUPT_DIE))
 		{
 			ereport(WARNING,
 					(errcode(ERRCODE_ADMIN_SHUTDOWN),
@@ -278,9 +278,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * altogether is not helpful, so we just terminate the wait with a
 		 * suitable warning.
 		 */
-		if (QueryCancelPending)
+		if (InterruptConsume(INTERRUPT_QUERY_CANCEL))
 		{
-			QueryCancelPending = false;
 			ereport(WARNING,
 					(errmsg("canceling wait for synchronous replication due to user request"),
 					 errdetail("The transaction has already committed locally, but might not have been replicated to the standby.")));
@@ -301,7 +300,7 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 */
 		if (rc & WL_POSTMASTER_DEATH)
 		{
-			ProcDiePending = true;
+			InterruptRaise(INTERRUPT_DIE);
 			whereToSendOutput = DestNone;
 			SyncRepCancelWait();
 			break;
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 7a7eb3784e..89b91c1b7d 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -267,7 +267,7 @@ WalReceiverMain(void)
 	/* SIGQUIT handler was already set up by InitPostmasterChild */
 	pqsignal(SIGALRM, SIG_IGN);
 	pqsignal(SIGPIPE, SIG_IGN);
-	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+	pqsignal(SIGUSR1, SIG_IGN);
 	pqsignal(SIGUSR2, SIG_IGN);
 
 	/* Reset some signals that are accepted by postmaster but not here */
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 7950afb173..f580cb8cfc 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -2530,6 +2530,7 @@ InitWalSenderSlot(void)
 			 * Found a free slot. Reserve it for us.
 			 */
 			walsnd->pid = MyProcPid;
+			walsnd->pgprocno = MyProc->pgprocno;
 			walsnd->state = WALSNDSTATE_STARTUP;
 			walsnd->sentPtr = InvalidXLogRecPtr;
 			walsnd->needreload = false;
@@ -3171,7 +3172,7 @@ WalSndSignals(void)
 	/* SIGQUIT handler was already set up by InitPostmasterChild */
 	InitializeTimeouts();		/* establishes SIGALRM handler */
 	pqsignal(SIGPIPE, SIG_IGN);
-	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+	pqsignal(SIGUSR1, SIG_IGN);
 	pqsignal(SIGUSR2, WalSndLastCycleHandler);	/* request a last cycle and
 												 * shutdown */
 
@@ -3278,15 +3279,17 @@ WalSndInitStopping(void)
 	{
 		WalSnd	   *walsnd = &WalSndCtl->walsnds[i];
 		pid_t		pid;
+		int			pgprocno;
 
 		SpinLockAcquire(&walsnd->mutex);
 		pid = walsnd->pid;
+		pgprocno = walsnd->pgprocno;
 		SpinLockRelease(&walsnd->mutex);
 
 		if (pid == 0)
 			continue;
 
-		SendProcSignal(pid, PROCSIG_WALSND_INIT_STOPPING, InvalidBackendId);
+		InterruptSend(INTERRUPT_WALSND_INIT_STOPPING, pgprocno);
 	}
 }
 
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 08ebabfe96..d99b6bcb3a 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -2001,7 +2001,7 @@ BufferSync(int flags)
 		UnlockBufHdr(bufHdr, buf_state);
 
 		/* Check for barrier events in case NBuffers is large. */
-		if (ProcSignalBarrierPending)
+		if (InterruptConsume(INTERRUPT_BARRIER))
 			ProcessProcSignalBarrier();
 	}
 
@@ -2082,7 +2082,7 @@ BufferSync(int flags)
 		s->num_to_scan++;
 
 		/* Check for barrier events. */
-		if (ProcSignalBarrierPending)
+		if (InterruptConsume(INTERRUPT_BARRIER))
 			ProcessProcSignalBarrier();
 	}
 
@@ -4108,7 +4108,7 @@ LockBufferForCleanup(Buffer buffer)
 			 * deadlock_timeout for it.
 			 */
 			if (logged_recovery_conflict)
-				LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
+				LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN,
 									waitStart, GetCurrentTimestamp(),
 									NULL, false);
 
@@ -4165,7 +4165,7 @@ LockBufferForCleanup(Buffer buffer)
 				if (TimestampDifferenceExceeds(waitStart, now,
 											   DeadlockTimeout))
 				{
-					LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
+					LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN,
 										waitStart, now, NULL, true);
 					logged_recovery_conflict = true;
 				}
diff --git a/src/backend/storage/ipc/dsm_impl.c b/src/backend/storage/ipc/dsm_impl.c
index f7e292981e..fe05255be6 100644
--- a/src/backend/storage/ipc/dsm_impl.c
+++ b/src/backend/storage/ipc/dsm_impl.c
@@ -385,7 +385,8 @@ dsm_impl_posix_resize(int fd, off_t size)
 		do
 		{
 			rc = posix_fallocate(fd, 0, size);
-		} while (rc == EINTR && !(ProcDiePending || QueryCancelPending));
+		} while (rc == EINTR && !(InterruptPending(INTERRUPT_DIE) ||
+								  InterruptPending(INTERRUPT_QUERY_CANCEL)));
 		pgstat_report_wait_end();
 
 		/*
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 4045d7d68a..2d57ee53fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -171,9 +171,9 @@ proc_exit_prepare(int code)
 	 * close up shop already.  Note that the signal handlers will not set
 	 * these flags again, now that proc_exit_inprogress is set.
 	 */
-	InterruptPending = false;
-	ProcDiePending = false;
-	QueryCancelPending = false;
+	InterruptLocal();
+	InterruptClear(INTERRUPT_DIE);
+	InterruptClear(INTERRUPT_QUERY_CANCEL);
 	InterruptHoldoffCount = 1;
 	CritSectionCount = 0;
 
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 892f0f6799..d4616f5169 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3420,13 +3420,13 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
  * Returns pid of the process signaled, or 0 if not found.
  */
 pid_t
-CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode)
+CancelVirtualTransaction(VirtualTransactionId vxid, InterruptType sigmode)
 {
 	return SignalVirtualTransaction(vxid, sigmode, true);
 }
 
 pid_t
-SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode,
+SignalVirtualTransaction(VirtualTransactionId vxid, InterruptType sigmode,
 						 bool conflictPending)
 {
 	ProcArrayStruct *arrayP = procArray;
@@ -3447,15 +3447,7 @@ SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode,
 			procvxid.localTransactionId == vxid.localTransactionId)
 		{
 			proc->recoveryConflictPending = conflictPending;
-			pid = proc->pid;
-			if (pid != 0)
-			{
-				/*
-				 * Kill the pid if it's still here. If not, that's what we
-				 * wanted so ignore any errors.
-				 */
-				(void) SendProcSignal(pid, sigmode, vxid.backendId);
-			}
+			InterruptSend(sigmode, proc->pgprocno);
 			break;
 		}
 	}
@@ -3589,7 +3581,7 @@ CountDBConnections(Oid databaseid)
  * CancelDBBackends --- cancel backends that are using specified database
  */
 void
-CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending)
+CancelDBBackends(Oid databaseid, InterruptType sigmode, bool conflictPending)
 {
 	ProcArrayStruct *arrayP = procArray;
 	int			index;
@@ -3604,11 +3596,8 @@ CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending)
 
 		if (databaseid == InvalidOid || proc->databaseId == databaseid)
 		{
-			VirtualTransactionId procvxid;
 			pid_t		pid;
 
-			GET_VXID_FROM_PGPROC(procvxid, *proc);
-
 			proc->recoveryConflictPending = conflictPending;
 			pid = proc->pid;
 			if (pid != 0)
@@ -3617,7 +3606,7 @@ CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending)
 				 * Kill the pid if it's still here. If not, that's what we
 				 * wanted so ignore any errors.
 				 */
-				(void) SendProcSignal(pid, sigmode, procvxid.backendId);
+				InterruptSend(sigmode, proc->pgprocno);
 			}
 		}
 	}
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 6e69398cdd..223fd5494d 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -33,22 +33,9 @@
 #include "utils/memutils.h"
 
 /*
- * The SIGUSR1 signal is multiplexed to support signaling multiple event
- * types. The specific reason is communicated via flags in shared memory.
- * We keep a boolean flag for each possible "reason", so that different
- * reasons can be signaled to a process concurrently.  (However, if the same
- * reason is signaled more than once nearly simultaneously, the process may
- * observe it only once.)
+ * State for the ProcSignalBarrier mechanism.
  *
- * Each process that wants to receive signals registers its process ID
- * in the ProcSignalSlots array. The array is indexed by backend ID to make
- * slot allocation simple, and to avoid having to search the array when you
- * know the backend ID of the process you're signaling.  (We do support
- * signaling without backend ID, but it's a bit less efficient.)
- *
- * The flags are actually declared as "volatile sig_atomic_t" for maximum
- * portability.  This should ensure that loads and stores of the flag
- * values are atomic, allowing us to dispense with any explicit locking.
+ * XXX Rename all of this stuff?
  *
  * pss_signalFlags are intended to be set in cases where we don't need to
  * keep track of whether or not the target process has handled the signal,
@@ -62,7 +49,7 @@
 typedef struct
 {
 	volatile pid_t pss_pid;
-	volatile sig_atomic_t pss_signalFlags[NUM_PROCSIGNALS];
+	volatile int pss_pgprocno;
 	pg_atomic_uint64 pss_barrierGeneration;
 	pg_atomic_uint32 pss_barrierCheckMask;
 	ConditionVariable pss_barrierCV;
@@ -98,7 +85,6 @@ typedef struct
 static ProcSignalHeader *ProcSignal = NULL;
 static ProcSignalSlot *MyProcSignalSlot = NULL;
 
-static bool CheckProcSignal(ProcSignalReason reason);
 static void CleanupProcSignalState(int status, Datum arg);
 static void ResetProcSignalBarrierBits(uint32 flags);
 static bool ProcessBarrierPlaceholder(void);
@@ -142,7 +128,7 @@ ProcSignalShmemInit(void)
 			ProcSignalSlot *slot = &ProcSignal->psh_slot[i];
 
 			slot->pss_pid = 0;
-			MemSet(slot->pss_signalFlags, 0, sizeof(slot->pss_signalFlags));
+			slot->pss_pgprocno = 0;
 			pg_atomic_init_u64(&slot->pss_barrierGeneration, PG_UINT64_MAX);
 			pg_atomic_init_u32(&slot->pss_barrierCheckMask, 0);
 			ConditionVariableInit(&slot->pss_barrierCV);
@@ -172,9 +158,6 @@ ProcSignalInit(int pss_idx)
 		elog(LOG, "process %d taking over ProcSignal slot %d, but it's not empty",
 			 MyProcPid, pss_idx);
 
-	/* Clear out any leftover signal reasons */
-	MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));
-
 	/*
 	 * Initialize barrier state. Since we're a brand-new process, there
 	 * shouldn't be any leftover backend-private state that needs to be
@@ -192,8 +175,9 @@ ProcSignalInit(int pss_idx)
 	pg_atomic_write_u64(&slot->pss_barrierGeneration, barrier_generation);
 	pg_memory_barrier();
 
-	/* Mark slot with my PID */
+	/* Mark slot with my procno, so my latch will be set for proc signals */
 	slot->pss_pid = MyProcPid;
+	slot->pss_pgprocno = MyProc->pgprocno;
 
 	/* Remember slot location for CheckProcSignal */
 	MyProcSignalSlot = slot;
@@ -225,14 +209,14 @@ CleanupProcSignalState(int status, Datum arg)
 	MyProcSignalSlot = NULL;
 
 	/* sanity check */
-	if (slot->pss_pid != MyProcPid)
+	if (slot->pss_pgprocno != MyProc->pgprocno)
 	{
 		/*
 		 * don't ERROR here. We're exiting anyway, and don't want to get into
 		 * infinite loop trying to exit
 		 */
 		elog(LOG, "process %d releasing ProcSignal slot %d, but it contains %d",
-			 MyProcPid, pss_idx, (int) slot->pss_pid);
+			 MyProcPid, pss_idx, slot->pss_pgprocno);
 		return;					/* XXX better to zero the slot anyway? */
 	}
 
@@ -244,72 +228,7 @@ CleanupProcSignalState(int status, Datum arg)
 	ConditionVariableBroadcast(&slot->pss_barrierCV);
 
 	slot->pss_pid = 0;
-}
-
-/*
- * SendProcSignal
- *		Send a signal to a Postgres process
- *
- * Providing backendId is optional, but it will speed up the operation.
- *
- * On success (a signal was sent), zero is returned.
- * On error, -1 is returned, and errno is set (typically to ESRCH or EPERM).
- *
- * Not to be confused with ProcSendSignal
- */
-int
-SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId)
-{
-	volatile ProcSignalSlot *slot;
-
-	if (backendId != InvalidBackendId)
-	{
-		slot = &ProcSignal->psh_slot[backendId - 1];
-
-		/*
-		 * Note: Since there's no locking, it's possible that the target
-		 * process detaches from shared memory and exits right after this
-		 * test, before we set the flag and send signal. And the signal slot
-		 * might even be recycled by a new process, so it's remotely possible
-		 * that we set a flag for a wrong process. That's OK, all the signals
-		 * are such that no harm is done if they're mistakenly fired.
-		 */
-		if (slot->pss_pid == pid)
-		{
-			/* Atomically set the proper flag */
-			slot->pss_signalFlags[reason] = true;
-			/* Send signal */
-			return kill(pid, SIGUSR1);
-		}
-	}
-	else
-	{
-		/*
-		 * BackendId not provided, so search the array using pid.  We search
-		 * the array back to front so as to reduce search overhead.  Passing
-		 * InvalidBackendId means that the target is most likely an auxiliary
-		 * process, which will have a slot near the end of the array.
-		 */
-		int			i;
-
-		for (i = NumProcSignalSlots - 1; i >= 0; i--)
-		{
-			slot = &ProcSignal->psh_slot[i];
-
-			if (slot->pss_pid == pid)
-			{
-				/* the above note about race conditions applies here too */
-
-				/* Atomically set the proper flag */
-				slot->pss_signalFlags[reason] = true;
-				/* Send signal */
-				return kill(pid, SIGUSR1);
-			}
-		}
-	}
-
-	errno = ESRCH;
-	return -1;
+	slot->pss_pgprocno = 0;
 }
 
 /*
@@ -356,7 +275,7 @@ EmitProcSignalBarrier(ProcSignalBarrierType type)
 		pg_atomic_add_fetch_u64(&ProcSignal->psh_barrierGeneration, 1);
 
 	/*
-	 * Signal all the processes, so that they update their advertised barrier
+	 * Interrupt all the processes, so that they update their advertised barrier
 	 * generation.
 	 *
 	 * Concurrency is not a problem here. Backends that have exited don't
@@ -368,18 +287,8 @@ EmitProcSignalBarrier(ProcSignalBarrierType type)
 	 * backends that need to update state - but they won't actually need to
 	 * change any state.
 	 */
-	for (int i = NumProcSignalSlots - 1; i >= 0; i--)
-	{
-		volatile ProcSignalSlot *slot = &ProcSignal->psh_slot[i];
-		pid_t		pid = slot->pss_pid;
-
-		if (pid != 0)
-		{
-			/* see SendProcSignal for details */
-			slot->pss_signalFlags[PROCSIG_BARRIER] = true;
-			kill(pid, SIGUSR1);
-		}
-	}
+	for (int i = 0; i < ProcGlobal->allProcCount; ++i)
+		InterruptSend(INTERRUPT_BARRIER, i);
 
 	return generation;
 }
@@ -424,23 +333,6 @@ WaitForProcSignalBarrier(uint64 generation)
 	pg_memory_barrier();
 }
 
-/*
- * Handle receipt of an interrupt indicating a global barrier event.
- *
- * All the actual work is deferred to ProcessProcSignalBarrier(), because we
- * cannot safely access the barrier generation inside the signal handler as
- * 64bit atomics might use spinlock based emulation, even for reads. As this
- * routine only gets called when PROCSIG_BARRIER is sent that won't cause a
- * lot of unnecessary work.
- */
-static void
-HandleProcSignalBarrierInterrupt(void)
-{
-	InterruptPending = true;
-	ProcSignalBarrierPending = true;
-	/* latch will be set by procsignal_sigusr1_handler */
-}
-
 /*
  * Perform global barrier related interrupt checking.
  *
@@ -458,11 +350,6 @@ ProcessProcSignalBarrier(void)
 
 	Assert(MyProcSignalSlot);
 
-	/* Exit quickly if there's no work to do. */
-	if (!ProcSignalBarrierPending)
-		return;
-	ProcSignalBarrierPending = false;
-
 	/*
 	 * It's not unlikely to process multiple barriers at once, before the
 	 * signals for all the barriers have arrived. To avoid unnecessary work in
@@ -590,8 +477,7 @@ static void
 ResetProcSignalBarrierBits(uint32 flags)
 {
 	pg_atomic_fetch_or_u32(&MyProcSignalSlot->pss_barrierCheckMask, flags);
-	ProcSignalBarrierPending = true;
-	InterruptPending = true;
+	InterruptRaise(INTERRUPT_BARRIER);
 }
 
 static bool
@@ -611,75 +497,3 @@ ProcessBarrierPlaceholder(void)
 	 */
 	return true;
 }
-
-/*
- * CheckProcSignal - check to see if a particular reason has been
- * signaled, and clear the signal flag.  Should be called after receiving
- * SIGUSR1.
- */
-static bool
-CheckProcSignal(ProcSignalReason reason)
-{
-	volatile ProcSignalSlot *slot = MyProcSignalSlot;
-
-	if (slot != NULL)
-	{
-		/* Careful here --- don't clear flag if we haven't seen it set */
-		if (slot->pss_signalFlags[reason])
-		{
-			slot->pss_signalFlags[reason] = false;
-			return true;
-		}
-	}
-
-	return false;
-}
-
-/*
- * procsignal_sigusr1_handler - handle SIGUSR1 signal.
- */
-void
-procsignal_sigusr1_handler(SIGNAL_ARGS)
-{
-	int			save_errno = errno;
-
-	if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT))
-		HandleCatchupInterrupt();
-
-	if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
-		HandleNotifyInterrupt();
-
-	if (CheckProcSignal(PROCSIG_PARALLEL_MESSAGE))
-		HandleParallelMessageInterrupt();
-
-	if (CheckProcSignal(PROCSIG_WALSND_INIT_STOPPING))
-		HandleWalSndInitStopping();
-
-	if (CheckProcSignal(PROCSIG_BARRIER))
-		HandleProcSignalBarrierInterrupt();
-
-	if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
-		HandleLogMemoryContextInterrupt();
-
-	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
-		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
-
-	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE))
-		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
-
-	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK))
-		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);
-
-	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
-		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
-
-	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK))
-		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
-
-	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
-		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
-
-	SetLatch(MyLatch);
-
-	errno = save_errno;
-}
diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c
index f585d63e5c..286f4c5efe 100644
--- a/src/backend/storage/ipc/sinval.c
+++ b/src/backend/storage/ipc/sinval.c
@@ -17,6 +17,7 @@
 #include "access/xact.h"
 #include "commands/async.h"
 #include "miscadmin.h"
+#include "postmaster/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
 #include "storage/sinvaladt.h"
@@ -26,21 +27,6 @@
 uint64		SharedInvalidMessageCounter;
 
 
-/*
- * Because backends sitting idle will not be reading sinval events, we
- * need a way to give an idle backend a swift kick in the rear and make
- * it catch up before the sinval queue overflows and forces it to go
- * through a cache reset exercise.  This is done by sending
- * PROCSIG_CATCHUP_INTERRUPT to any backend that gets too far behind.
- *
- * The signal handler will set an interrupt pending flag and will set the
- * processes latch. Whenever starting to read from the client, or when
- * interrupted while doing so, ProcessClientReadInterrupt() will call
- * ProcessCatchupEvent().
- */
-volatile sig_atomic_t catchupInterruptPending = false;
-
-
 /*
  * SendSharedInvalidMessages
  *	Add shared-cache-invalidation message(s) to the global SI message queue.
@@ -134,38 +120,13 @@ ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *m
 	 * catchup signal this way avoids creating spikes in system load for what
 	 * should be just a background maintenance activity.
 	 */
-	if (catchupInterruptPending)
+	if (InterruptConsume(INTERRUPT_SINVAL_CATCHUP))
 	{
-		catchupInterruptPending = false;
 		elog(DEBUG4, "sinval catchup complete, cleaning queue");
 		SICleanupQueue(false, 0);
 	}
 }
 
-
-/*
- * HandleCatchupInterrupt
- *
- * This is called when PROCSIG_CATCHUP_INTERRUPT is received.
- *
- * We used to directly call ProcessCatchupEvent directly when idle. These days
- * we just set a flag to do it later and notify the process of that fact by
- * setting the process's latch.
- */
-void
-HandleCatchupInterrupt(void)
-{
-	/*
-	 * Note: this is called by a SIGNAL HANDLER. You must be very wary what
-	 * you do here.
-	 */
-
-	catchupInterruptPending = true;
-
-	/* make sure the event is processed in due course */
-	SetLatch(MyLatch);
-}
-
 /*
  * ProcessCatchupInterrupt
  *
@@ -175,7 +136,7 @@ HandleCatchupInterrupt(void)
 void
 ProcessCatchupInterrupt(void)
 {
-	while (catchupInterruptPending)
+	while (InterruptConsume(INTERRUPT_SINVAL_CATCHUP))
 	{
 		/*
 		 * What we need to do here is cause ReceiveSharedInvalidMessages() to
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index 946bd8e3cb..5d3983c753 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -19,6 +19,7 @@
 
 #include "access/transam.h"
 #include "miscadmin.h"
+#include "postmaster/interrupt.h"
 #include "storage/backendid.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
@@ -724,19 +725,15 @@ SICleanupQueue(bool callerHasWriteLock, int minFree)
 
 	/*
 	 * Lastly, signal anyone who needs a catchup interrupt.  Since
-	 * SendProcSignal() might not be fast, we don't want to hold locks while
+	 * InterruptSend() might not be fast, we don't want to hold locks while
 	 * executing it.
 	 */
 	if (needSig)
 	{
-		pid_t		his_pid = needSig->procPid;
-		BackendId	his_backendId = (needSig - &segP->procState[0]) + 1;
-
 		needSig->signaled = true;
 		LWLockRelease(SInvalReadLock);
 		LWLockRelease(SInvalWriteLock);
-		elog(DEBUG4, "sending sinval catchup signal to PID %d", (int) his_pid);
-		SendProcSignal(his_pid, PROCSIG_CATCHUP_INTERRUPT, his_backendId);
+		InterruptSend(INTERRUPT_SINVAL_CATCHUP, needSig->proc->pgprocno);
 		if (callerHasWriteLock)
 			LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE);
 	}
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 1496855925..08adc8d8e5 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -48,13 +48,13 @@ static volatile sig_atomic_t got_standby_deadlock_timeout = false;
 static volatile sig_atomic_t got_standby_lock_timeout = false;
 
 static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
-												   ProcSignalReason reason,
+												   InterruptType reason,
 												   uint32 wait_event_info,
 												   bool report_waiting);
-static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
+static void SendRecoveryConflictWithBufferPin(InterruptType reason);
 static XLogRecPtr LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
 static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
-static const char *get_recovery_conflict_desc(ProcSignalReason reason);
+static const char *get_recovery_conflict_desc(InterruptType reason);
 
 /*
  * Keep track of all the locks owned by a given transaction.
@@ -246,7 +246,7 @@ WaitExceedsMaxStandbyDelay(uint32 wait_event_info)
  * to be resolved or not.
  */
 void
-LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start,
+LogRecoveryConflict(InterruptType reason, TimestampTz wait_start,
 					TimestampTz now, VirtualTransactionId *wait_list,
 					bool still_waiting)
 {
@@ -333,7 +333,7 @@ LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start,
  */
 static void
 ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
-									   ProcSignalReason reason, uint32 wait_event_info,
+									   InterruptType reason, uint32 wait_event_info,
 									   bool report_waiting)
 {
 	TimestampTz waitStart = 0;
@@ -462,7 +462,7 @@ ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid, RelFileNode
 										 node.dbNode);
 
 	ResolveRecoveryConflictWithVirtualXIDs(backends,
-										   PROCSIG_RECOVERY_CONFLICT_SNAPSHOT,
+										   INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT,
 										   WAIT_EVENT_RECOVERY_CONFLICT_SNAPSHOT,
 										   true);
 }
@@ -520,7 +520,7 @@ ResolveRecoveryConflictWithTablespace(Oid tsid)
 	temp_file_users = GetConflictingVirtualXIDs(InvalidTransactionId,
 												InvalidOid);
 	ResolveRecoveryConflictWithVirtualXIDs(temp_file_users,
-										   PROCSIG_RECOVERY_CONFLICT_TABLESPACE,
+										   INTERRUPT_RECOVERY_CONFLICT_TABLESPACE,
 										   WAIT_EVENT_RECOVERY_CONFLICT_TABLESPACE,
 										   true);
 }
@@ -541,7 +541,7 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
 	 */
 	while (CountDBBackends(dbid) > 0)
 	{
-		CancelDBBackends(dbid, PROCSIG_RECOVERY_CONFLICT_DATABASE, true);
+		CancelDBBackends(dbid, INTERRUPT_RECOVERY_CONFLICT_DATABASE, true);
 
 		/*
 		 * Wait awhile for them to die so that we avoid flooding an
@@ -625,7 +625,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
 		 * because the caller, WaitOnLock(), has already reported that.
 		 */
 		ResolveRecoveryConflictWithVirtualXIDs(backends,
-											   PROCSIG_RECOVERY_CONFLICT_LOCK,
+											   INTERRUPT_RECOVERY_CONFLICT_LOCK,
 											   PG_WAIT_LOCK | locktag.locktag_type,
 											   false);
 	}
@@ -684,7 +684,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
 		while (VirtualTransactionIdIsValid(*backends))
 		{
 			SignalVirtualTransaction(*backends,
-									 PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
+									 INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 									 false);
 			backends++;
 		}
@@ -763,7 +763,7 @@ ResolveRecoveryConflictWithBufferPin(void)
 		/*
 		 * We're already behind, so clear a path as quickly as possible.
 		 */
-		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+		SendRecoveryConflictWithBufferPin(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN);
 	}
 	else
 	{
@@ -817,7 +817,7 @@ ResolveRecoveryConflictWithBufferPin(void)
 		 * is basically no so long. But we should fix this?
 		 */
 		SendRecoveryConflictWithBufferPin(
-										  PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+										  INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
 	}
 
 	/*
@@ -831,10 +831,10 @@ ResolveRecoveryConflictWithBufferPin(void)
 }
 
 static void
-SendRecoveryConflictWithBufferPin(ProcSignalReason reason)
+SendRecoveryConflictWithBufferPin(InterruptType reason)
 {
-	Assert(reason == PROCSIG_RECOVERY_CONFLICT_BUFFERPIN ||
-		   reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+	Assert(reason == INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN ||
+		   reason == INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
 
 	/*
 	 * We send signal to all backends to ask them if they are holding the
@@ -906,7 +906,7 @@ StandbyTimeoutHandler(void)
 	/* forget any pending STANDBY_DEADLOCK_TIMEOUT request */
 	disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false);
 
-	SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+	SendRecoveryConflictWithBufferPin(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN);
 }
 
 /*
@@ -1418,28 +1418,28 @@ LogStandbyInvalidations(int nmsgs, SharedInvalidationMessage *msgs,
 
 /* Return the description of recovery conflict */
 static const char *
-get_recovery_conflict_desc(ProcSignalReason reason)
+get_recovery_conflict_desc(InterruptType reason)
 {
 	const char *reasonDesc = _("unknown reason");
 
 	switch (reason)
 	{
-		case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+		case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN:
 			reasonDesc = _("recovery conflict on buffer pin");
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_LOCK:
+		case INTERRUPT_RECOVERY_CONFLICT_LOCK:
 			reasonDesc = _("recovery conflict on lock");
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+		case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE:
 			reasonDesc = _("recovery conflict on tablespace");
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+		case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT:
 			reasonDesc = _("recovery conflict on snapshot");
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+		case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
 			reasonDesc = _("recovery conflict on buffer deadlock");
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+		case INTERRUPT_RECOVERY_CONFLICT_DATABASE:
 			reasonDesc = _("recovery conflict on database");
 			break;
 		default:
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index b7d9da0aa9..599a867137 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -469,6 +469,9 @@ InitProcess(void)
 	 */
 	InitLWLockAccess();
 	InitDeadLockChecking();
+
+	/* Switch to shared-memory interrupts. */
+	InterruptShared();
 }
 
 /*
@@ -963,6 +966,9 @@ AuxiliaryProcKill(int code, Datum arg)
 	/* Cancel any pending condition variable sleep, too */
 	ConditionVariableCancelSleep();
 
+	/* Revert to local interrupt vector. */
+	InterruptLocal();
+
 	/* look at the equivalent ProcKill() code for comments */
 	SwitchBackToLocalLatch();
 	pgstat_reset_wait_event_storage();
@@ -1349,7 +1355,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 					 * because the startup process here has already waited
 					 * longer than deadlock_timeout.
 					 */
-					LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_LOCK,
+					LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_LOCK,
 										standbyWaitStart, now,
 										cnt > 0 ? vxids : NULL, true);
 					logged_recovery_conflict = true;
@@ -1642,7 +1648,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 	 * startup process waited longer than deadlock_timeout for it.
 	 */
 	if (InHotStandby && logged_recovery_conflict)
-		LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_LOCK,
+		LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_LOCK,
 							standbyWaitStart, GetCurrentTimestamp(),
 							NULL, false);
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..f28b6d2138 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -176,7 +176,7 @@ static bool UseSemiNewlineNewline = false;	/* -j switch */
 /* whether or not, and why, we were canceled by conflict with recovery */
 static bool RecoveryConflictPending = false;
 static bool RecoveryConflictRetryable = true;
-static ProcSignalReason RecoveryConflictReason;
+static InterruptType RecoveryConflictReason;
 
 /* reused buffer to pass to SendRowDescriptionMessage() */
 static MemoryContext row_description_context = NULL;
@@ -499,14 +499,14 @@ ProcessClientReadInterrupt(bool blocked)
 		CHECK_FOR_INTERRUPTS();
 
 		/* Process sinval catchup interrupts, if any */
-		if (catchupInterruptPending)
+		if (InterruptPending(INTERRUPT_SINVAL_CATCHUP))
 			ProcessCatchupInterrupt();
 
 		/* Process notify interrupts, if any */
-		if (notifyInterruptPending)
+		if (InterruptPending(INTERRUPT_NOTIFY))
 			ProcessNotifyInterrupt(true);
 	}
-	else if (ProcDiePending)
+	else if (InterruptPending(INTERRUPT_DIE))
 	{
 		/*
 		 * We're dying.  If there is no data available to read, then it's safe
@@ -539,7 +539,7 @@ ProcessClientWriteInterrupt(bool blocked)
 {
 	int			save_errno = errno;
 
-	if (ProcDiePending)
+	if (InterruptPending(INTERRUPT_DIE))
 	{
 		/*
 		 * We're dying.  If it's not possible to write, then we should handle
@@ -2455,22 +2455,22 @@ errdetail_recovery_conflict(void)
 {
 	switch (RecoveryConflictReason)
 	{
-		case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+		case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN:
 			errdetail("User was holding shared buffer pin for too long.");
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_LOCK:
+		case INTERRUPT_RECOVERY_CONFLICT_LOCK:
 			errdetail("User was holding a relation lock for too long.");
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+		case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE:
 			errdetail("User was or might have been using tablespace that must be dropped.");
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+		case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT:
 			errdetail("User query might have needed to see row versions that must be removed.");
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+		case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
 			errdetail("User transaction caused buffer deadlock with recovery.");
 			break;
-		case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+		case INTERRUPT_RECOVERY_CONFLICT_DATABASE:
 			errdetail("User was connected to a database that must be dropped.");
 			break;
 		default:
@@ -2918,10 +2918,7 @@ die(SIGNAL_ARGS)
 
 	/* Don't joggle the elbow of proc_exit */
 	if (!proc_exit_inprogress)
-	{
-		InterruptPending = true;
-		ProcDiePending = true;
-	}
+		InterruptRaise(INTERRUPT_DIE);
 
 	/* for the statistics collector */
 	pgStatSessionEndCause = DISCONNECT_KILLED;
@@ -2954,10 +2951,7 @@ StatementCancelHandler(SIGNAL_ARGS)
 	 * Don't joggle the elbow of proc_exit
 	 */
 	if (!proc_exit_inprogress)
-	{
-		InterruptPending = true;
-		QueryCancelPending = true;
-	}
+		InterruptRaise(INTERRUPT_QUERY_CANCEL);
 
 	/* If we're still here, waken anything waiting on the process latch */
 	SetLatch(MyLatch);
@@ -2985,7 +2979,7 @@ FloatExceptionHandler(SIGNAL_ARGS)
  * that begins a transaction during recovery.
  */
 void
-RecoveryConflictInterrupt(ProcSignalReason reason)
+RecoveryConflictInterrupt(InterruptType reason)
 {
 	int			save_errno = errno;
 
@@ -2997,7 +2991,7 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
 		RecoveryConflictReason = reason;
 		switch (reason)
 		{
-			case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+			case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
 
 				/*
 				 * If we aren't waiting for a lock we can never deadlock.
@@ -3008,14 +3002,14 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
 				/* Intentional fall through to check wait for pin */
 				/* FALLTHROUGH */
 
-			case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+			case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN:
 
 				/*
-				 * If PROCSIG_RECOVERY_CONFLICT_BUFFERPIN is requested but we
+				 * If INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN is requested but we
 				 * aren't blocking the Startup process there is nothing more
 				 * to do.
 				 *
-				 * When PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK is
+				 * When INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK is
 				 * requested, if we're waiting for locks and the startup
 				 * process is not waiting for buffer pin (i.e., also waiting
 				 * for locks), we set the flag so that ProcSleep() will check
@@ -3023,7 +3017,7 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
 				 */
 				if (!HoldingBufferPinThatDelaysRecovery())
 				{
-					if (reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK &&
+					if (reason == INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK &&
 						GetStartupBufferPinWaitBufId() < 0)
 						CheckDeadLockAlert();
 					return;
@@ -3034,9 +3028,9 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
 				/* Intentional fall through to error handling */
 				/* FALLTHROUGH */
 
-			case PROCSIG_RECOVERY_CONFLICT_LOCK:
-			case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
-			case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+			case INTERRUPT_RECOVERY_CONFLICT_LOCK:
+			case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE:
+			case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT:
 
 				/*
 				 * If we aren't in a transaction any longer then ignore.
@@ -3050,14 +3044,14 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
 				 * drop through to the FATAL case.
 				 *
 				 * XXX other times that we can throw just an ERROR *may* be
-				 * PROCSIG_RECOVERY_CONFLICT_LOCK if no locks are held in
+				 * INTERRUPT_RECOVERY_CONFLICT_LOCK if no locks are held in
 				 * parent transactions
 				 *
-				 * PROCSIG_RECOVERY_CONFLICT_SNAPSHOT if no snapshots are held
+				 * INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT if no snapshots are held
 				 * by parent transactions and the transaction is not
 				 * transaction-snapshot mode
 				 *
-				 * PROCSIG_RECOVERY_CONFLICT_TABLESPACE if no temp files or
+				 * INTERRUPT_RECOVERY_CONFLICT_TABLESPACE if no temp files or
 				 * cursors open in parent transactions
 				 */
 				if (!IsSubTransaction())
@@ -3070,19 +3064,16 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
 					if (IsAbortedTransactionBlockState())
 						return;
 
-					RecoveryConflictPending = true;
-					QueryCancelPending = true;
-					InterruptPending = true;
+					InterruptRaise(INTERRUPT_RECOVERY_CONFLICT);
+					InterruptRaise(INTERRUPT_QUERY_CANCEL);
 					break;
 				}
 
 				/* Intentional fall through to session cancel */
 				/* FALLTHROUGH */
 
-			case PROCSIG_RECOVERY_CONFLICT_DATABASE:
-				RecoveryConflictPending = true;
-				ProcDiePending = true;
-				InterruptPending = true;
+			case INTERRUPT_RECOVERY_CONFLICT_DATABASE:
+				InterruptRaise(INTERRUPT_RECOVERY_CONFLICT_DATABASE);
 				break;
 
 			default:
@@ -3090,7 +3081,9 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
 					 (int) reason);
 		}
 
-		Assert(RecoveryConflictPending && (QueryCancelPending || ProcDiePending));
+		Assert(InterruptPending(INTERRUPT_RECOVERY_CONFLICT) &&
+			   (InterruptPending(INTERRUPT_QUERY_CANCEL) ||
+				InterruptPending(INTERRUPT_DIE)));
 
 		/*
 		 * All conflicts apart from database cause dynamic errors where the
@@ -3098,7 +3091,7 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
 		 * potential for success. No need to reset this, since non-retryable
 		 * conflict errors are currently FATAL.
 		 */
-		if (reason == PROCSIG_RECOVERY_CONFLICT_DATABASE)
+		if (reason == INTERRUPT_RECOVERY_CONFLICT_DATABASE)
 			RecoveryConflictRetryable = false;
 	}
 
@@ -3117,7 +3110,7 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
  *
  * If an interrupt condition is pending, and it's safe to service it,
  * then clear the flag and accept the interrupt.  Called only when
- * InterruptPending is true.
+ * InterruptPending() is true.
  *
  * Note: if INTERRUPTS_CAN_BE_PROCESSED() is true, then ProcessInterrupts
  * is guaranteed to clear the InterruptPending flag before returning.
@@ -3131,12 +3124,12 @@ ProcessInterrupts(void)
 	/* OK to accept any interrupts now? */
 	if (InterruptHoldoffCount != 0 || CritSectionCount != 0)
 		return;
-	InterruptPending = false;
 
-	if (ProcDiePending)
+	pg_read_barrier();
+
+	if (InterruptConsume(INTERRUPT_DIE))
 	{
-		ProcDiePending = false;
-		QueryCancelPending = false; /* ProcDie trumps QueryCancel */
+		InterruptClear(INTERRUPT_QUERY_CANCEL); /* ProcDie trumps QueryCancel */
 		LockErrorCleanup();
 		/* As in quickdie, don't risk sending to client during auth */
 		if (ClientAuthInProgress && whereToSendOutput == DestRemote)
@@ -3175,7 +3168,7 @@ ProcessInterrupts(void)
 		else if (RecoveryConflictPending)
 		{
 			/* Currently there is only one non-retryable recovery conflict */
-			Assert(RecoveryConflictReason == PROCSIG_RECOVERY_CONFLICT_DATABASE);
+			Assert(RecoveryConflictReason == INTERRUPT_RECOVERY_CONFLICT_DATABASE);
 			pgstat_report_recovery_conflict(RecoveryConflictReason);
 			ereport(FATAL,
 					(errcode(ERRCODE_DATABASE_DROPPED),
@@ -3193,10 +3186,8 @@ ProcessInterrupts(void)
 					 errmsg("terminating connection due to administrator command")));
 	}
 
-	if (CheckClientConnectionPending)
+	if (InterruptConsume(INTERRUPT_CHECK_CONNECTION_TIMEOUT))
 	{
-		CheckClientConnectionPending = false;
-
 		/*
 		 * Check for lost connection and re-arm, if still configured, but not
 		 * if we've arrived back at DoingCommandRead state.  We don't want to
@@ -3206,16 +3197,16 @@ ProcessInterrupts(void)
 		if (!DoingCommandRead && client_connection_check_interval > 0)
 		{
 			if (!pq_check_connection())
-				ClientConnectionLost = true;
+				InterruptRaise(INTERRUPT_CONNECTION_LOST);
 			else
 				enable_timeout_after(CLIENT_CONNECTION_CHECK_TIMEOUT,
 									 client_connection_check_interval);
 		}
 	}
 
-	if (ClientConnectionLost)
+	if (InterruptConsume(INTERRUPT_CONNECTION_LOST))
 	{
-		QueryCancelPending = false; /* lost connection trumps QueryCancel */
+		InterruptClear(INTERRUPT_QUERY_CANCEL); /* lost connection trumps */
 		LockErrorCleanup();
 		/* don't send to client, we already know the connection to be dead. */
 		whereToSendOutput = DestNone;
@@ -3230,10 +3221,10 @@ ProcessInterrupts(void)
 	 * preventing recovery from making progress.  Terminate the connection to
 	 * dislodge it.
 	 */
-	if (RecoveryConflictPending && DoingCommandRead)
+	if (InterruptPending(INTERRUPT_RECOVERY_CONFLICT) && DoingCommandRead)
 	{
-		QueryCancelPending = false; /* this trumps QueryCancel */
-		RecoveryConflictPending = false;
+		InterruptClear(INTERRUPT_QUERY_CANCEL); /* this trumps QueryCancel */
+		InterruptClear(INTERRUPT_RECOVERY_CONFLICT);
 		LockErrorCleanup();
 		pgstat_report_recovery_conflict(RecoveryConflictReason);
 		ereport(FATAL,
@@ -3250,7 +3241,8 @@ ProcessInterrupts(void)
 	 * interrupts are OK, because we won't read any further messages from the
 	 * client in that case.)
 	 */
-	if (QueryCancelPending && QueryCancelHoldoffCount != 0)
+	if (InterruptPending(INTERRUPT_QUERY_CANCEL) &&
+		QueryCancelHoldoffCount != 0)
 	{
 		/*
 		 * Re-arm InterruptPending so that we process the cancel request as
@@ -3260,15 +3252,13 @@ ProcessInterrupts(void)
 		 * meaning that this code also creates opportunities for other bugs to
 		 * appear.)
 		 */
-		InterruptPending = true;
+		InterruptRaise(INTERRUPT_QUERY_CANCEL);
 	}
-	else if (QueryCancelPending)
+	else if (InterruptConsume(INTERRUPT_QUERY_CANCEL))
 	{
 		bool		lock_timeout_occurred;
 		bool		stmt_timeout_occurred;
 
-		QueryCancelPending = false;
-
 		/*
 		 * If LOCK_TIMEOUT and STATEMENT_TIMEOUT indicators are both set, we
 		 * need to clear both, so always fetch both.
@@ -3332,7 +3322,7 @@ ProcessInterrupts(void)
 		}
 	}
 
-	if (IdleInTransactionSessionTimeoutPending)
+	if (InterruptConsume(INTERRUPT_IDLE_TRANSACTION_TIMEOUT))
 	{
 		/*
 		 * If the GUC has been reset to zero, ignore the signal.  This is
@@ -3343,28 +3333,27 @@ ProcessInterrupts(void)
 			ereport(FATAL,
 					(errcode(ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT),
 					 errmsg("terminating connection due to idle-in-transaction timeout")));
-		else
-			IdleInTransactionSessionTimeoutPending = false;
 	}
 
-	if (IdleSessionTimeoutPending)
+	if (InterruptConsume(INTERRUPT_IDLE_SESSION_TIMEOUT))
 	{
 		/* As above, ignore the signal if the GUC has been reset to zero. */
 		if (IdleSessionTimeout > 0)
 			ereport(FATAL,
 					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
 					 errmsg("terminating connection due to idle-session timeout")));
-		else
-			IdleSessionTimeoutPending = false;
 	}
 
-	if (ProcSignalBarrierPending)
+	if (InterruptConsume(INTERRUPT_BARRIER))
 		ProcessProcSignalBarrier();
 
-	if (ParallelMessagePending)
+	if (InterruptConsume(INTERRUPT_PARALLEL_MESSAGE))
 		HandleParallelMessages();
 
-	if (LogMemoryContextPending)
+	if (InterruptConsume(INTERRUPT_WALSND_INIT_STOPPING))
+		HandleWalSndInitStopping();
+
+	if (InterruptConsume(INTERRUPT_LOG_MEMORY_CONTEXT))
 		ProcessLogMemoryContextInterrupt();
 }
 
@@ -4070,7 +4059,7 @@ PostgresMain(const char *dbname, const char *username)
 		 * midst of output during who-knows-what operation...
 		 */
 		pqsignal(SIGPIPE, SIG_IGN);
-		pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+		pqsignal(SIGUSR1, SIG_IGN); //procsignal_sigusr1_handler); /* XXX which, see */
 		pqsignal(SIGUSR2, SIG_IGN);
 		pqsignal(SIGFPE, FloatExceptionHandler);
 
@@ -4228,7 +4217,7 @@ PostgresMain(const char *dbname, const char *username)
 		 * forgetting a timeout cancel.
 		 */
 		disable_all_timeouts(false);
-		QueryCancelPending = false; /* second to avoid race condition */
+		InterruptClear(INTERRUPT_QUERY_CANCEL); /* second to avoid race condition */
 
 		/* Not reading from the client anymore. */
 		DoingCommandRead = false;
@@ -4391,7 +4380,7 @@ PostgresMain(const char *dbname, const char *username)
 				 * were received during the just-finished transaction, they'll
 				 * be seen by the client before ReadyForQuery is.
 				 */
-				if (notifyInterruptPending)
+				if (InterruptPending(INTERRUPT_NOTIFY))
 					ProcessNotifyInterrupt(false);
 
 				pgstat_report_stat(false);
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 6ddbf70b30..e516877dad 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -199,13 +199,7 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
 		PG_RETURN_BOOL(false);
 	}
 
-	if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, proc->backendId) < 0)
-	{
-		/* Again, just a warning to allow loops */
-		ereport(WARNING,
-				(errmsg("could not send signal to process %d: %m", pid)));
-		PG_RETURN_BOOL(false);
-	}
+	InterruptSend(INTERRUPT_LOG_MEMORY_CONTEXT, proc->pgprocno);
 
 	PG_RETURN_BOOL(true);
 }
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 381d9e548d..55c7d06cbb 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -27,18 +27,9 @@
 
 ProtocolVersion FrontendProtocol;
 
-volatile sig_atomic_t InterruptPending = false;
-volatile sig_atomic_t QueryCancelPending = false;
-volatile sig_atomic_t ProcDiePending = false;
-volatile sig_atomic_t CheckClientConnectionPending = false;
-volatile sig_atomic_t ClientConnectionLost = false;
-volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
-volatile sig_atomic_t IdleSessionTimeoutPending = false;
-volatile sig_atomic_t ProcSignalBarrierPending = false;
-volatile sig_atomic_t LogMemoryContextPending = false;
-volatile uint32 InterruptHoldoffCount = 0;
-volatile uint32 QueryCancelHoldoffCount = 0;
-volatile uint32 CritSectionCount = 0;
+uint32 InterruptHoldoffCount = 0;
+uint32 QueryCancelHoldoffCount = 0;
+uint32 CritSectionCount = 0;
 
 int			MyProcPid;
 pg_time_t	MyStartTime;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 78bc64671e..e30f389029 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1224,24 +1224,21 @@ LockTimeoutHandler(void)
 static void
 IdleInTransactionSessionTimeoutHandler(void)
 {
-	IdleInTransactionSessionTimeoutPending = true;
-	InterruptPending = true;
+	InterruptRaise(INTERRUPT_IDLE_TRANSACTION_TIMEOUT);
 	SetLatch(MyLatch);
 }
 
 static void
 IdleSessionTimeoutHandler(void)
 {
-	IdleSessionTimeoutPending = true;
-	InterruptPending = true;
+	InterruptRaise(INTERRUPT_IDLE_SESSION_TIMEOUT);
 	SetLatch(MyLatch);
 }
 
 static void
 ClientCheckTimeoutHandler(void)
 {
-	CheckClientConnectionPending = true;
-	InterruptPending = true;
+	InterruptRaise(INTERRUPT_CHECK_CONNECTION_TIMEOUT);
 	SetLatch(MyLatch);
 }
 
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 6919a73280..84c0537440 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -1012,22 +1012,6 @@ MemoryContextAllocExtended(MemoryContext context, Size size, int flags)
 	return ret;
 }
 
-/*
- * HandleLogMemoryContextInterrupt
- *		Handle receipt of an interrupt indicating logging of memory
- *		contexts.
- *
- * All the actual work is deferred to ProcessLogMemoryContextInterrupt(),
- * because we cannot safely emit a log message inside the signal handler.
- */
-void
-HandleLogMemoryContextInterrupt(void)
-{
-	InterruptPending = true;
-	LogMemoryContextPending = true;
-	/* latch will be set by procsignal_sigusr1_handler */
-}
-
 /*
  * ProcessLogMemoryContextInterrupt
  * 		Perform logging of memory contexts of this backend process.
@@ -1040,8 +1024,6 @@ HandleLogMemoryContextInterrupt(void)
 void
 ProcessLogMemoryContextInterrupt(void)
 {
-	LogMemoryContextPending = false;
-
 	ereport(LOG,
 			(errmsg("logging memory contexts of PID %d", MyProcPid)));
 
diff --git a/src/include/access/parallel.h b/src/include/access/parallel.h
index 93d88ac60e..1239d45cb5 100644
--- a/src/include/access/parallel.h
+++ b/src/include/access/parallel.h
@@ -71,7 +71,6 @@ extern void WaitForParallelWorkersToFinish(ParallelContext *pcxt);
 extern void DestroyParallelContext(ParallelContext *pcxt);
 extern bool ParallelContextActive(void);
 
-extern void HandleParallelMessageInterrupt(void);
 extern void HandleParallelMessages(void);
 extern void AtEOXact_Parallel(bool isCommit);
 extern void AtEOSubXact_Parallel(bool isCommit, SubTransactionId mySubId);
diff --git a/src/include/commands/async.h b/src/include/commands/async.h
index f371ac896b..76532aaaeb 100644
--- a/src/include/commands/async.h
+++ b/src/include/commands/async.h
@@ -21,7 +21,6 @@
 #define NUM_NOTIFY_BUFFERS	8
 
 extern bool Trace_notify;
-extern volatile sig_atomic_t notifyInterruptPending;
 
 extern Size AsyncShmemSize(void);
 extern void AsyncShmemInit(void);
@@ -44,9 +43,6 @@ extern void AtSubCommit_Notify(void);
 extern void AtSubAbort_Notify(void);
 extern void AtPrepare_Notify(void);
 
-/* signal handler for inbound notifies (PROCSIG_NOTIFY_INTERRUPT) */
-extern void HandleNotifyInterrupt(void);
-
 /* process interrupts */
 extern void ProcessNotifyInterrupt(bool flush);
 
diff --git a/src/include/libpq/pqmq.h b/src/include/libpq/pqmq.h
index 31a4723fea..cf691a3646 100644
--- a/src/include/libpq/pqmq.h
+++ b/src/include/libpq/pqmq.h
@@ -17,7 +17,7 @@
 #include "storage/shm_mq.h"
 
 extern void pq_redirect_to_shm_mq(dsm_segment *seg, shm_mq_handle *mqh);
-extern void pq_set_parallel_leader(pid_t pid, BackendId backend_id);
+extern void pq_set_parallel_leader(int pgprocno);
 
 extern void pq_parse_errornotice(StringInfo str, ErrorData *edata);
 
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 90a3016065..01fddffc61 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -23,11 +23,15 @@
 #ifndef MISCADMIN_H
 #define MISCADMIN_H
 
-#include <signal.h>
-
 #include "datatype/timestamp.h" /* for TimestampTz */
 #include "pgtime.h"				/* for pg_time_t */
 
+#ifndef FRONTEND
+#include "postmaster/interrupt.h"
+#endif
+
+#include "storage/procsignal.h"
+
 
 #define InvalidPid				(-1)
 
@@ -85,64 +89,8 @@
  *
  *****************************************************************************/
 
-/* in globals.c */
-/* these are marked volatile because they are set by signal handlers: */
-extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
-extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
-extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
-extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
-extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
-extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
-extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
-
-extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
-extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
-
-/* these are marked volatile because they are examined by signal handlers: */
-extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
-extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
-extern PGDLLIMPORT volatile uint32 CritSectionCount;
-
-/* in tcop/postgres.c */
-extern void ProcessInterrupts(void);
-
-/* Test whether an interrupt is pending */
-#ifndef WIN32
-#define INTERRUPTS_PENDING_CONDITION() \
-	(unlikely(InterruptPending))
-#else
-#define INTERRUPTS_PENDING_CONDITION() \
-	(unlikely(UNBLOCKED_SIGNAL_QUEUE()) ? pgwin32_dispatch_queued_signals() : 0, \
-	 unlikely(InterruptPending))
-#endif
-
-/* Service interrupt, if one is pending and it's safe to service it now */
-#define CHECK_FOR_INTERRUPTS() \
-do { \
-	if (INTERRUPTS_PENDING_CONDITION()) \
-		ProcessInterrupts(); \
-} while(0)
-
-/* Is ProcessInterrupts() guaranteed to clear InterruptPending? */
-#define INTERRUPTS_CAN_BE_PROCESSED() \
-	(InterruptHoldoffCount == 0 && CritSectionCount == 0 && \
-	 QueryCancelHoldoffCount == 0)
-
-#define HOLD_INTERRUPTS()  (InterruptHoldoffCount++)
+extern PGDLLIMPORT uint32 CritSectionCount;
 
-#define RESUME_INTERRUPTS() \
-do { \
-	Assert(InterruptHoldoffCount > 0); \
-	InterruptHoldoffCount--; \
-} while(0)
-
-#define HOLD_CANCEL_INTERRUPTS()  (QueryCancelHoldoffCount++)
-
-#define RESUME_CANCEL_INTERRUPTS() \
-do { \
-	Assert(QueryCancelHoldoffCount > 0); \
-	QueryCancelHoldoffCount--; \
-} while(0)
 
 #define START_CRIT_SECTION()  (CritSectionCount++)
 
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 6d4122b4c7..52dddfacd9 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -98,6 +98,7 @@ typedef struct BackgroundWorker
 	Datum		bgw_main_arg;
 	char		bgw_extra[BGW_EXTRALEN];
 	pid_t		bgw_notify_pid; /* SIGUSR1 this backend on start/stop */
+	int			bgw_notify_pgprocno; /* set this proc's latch on start/stop */
 } BackgroundWorker;
 
 typedef enum BgwHandleStatus
diff --git a/src/include/postmaster/interrupt.h b/src/include/postmaster/interrupt.h
index 85a1293ef1..5a1d3c2520 100644
--- a/src/include/postmaster/interrupt.h
+++ b/src/include/postmaster/interrupt.h
@@ -3,9 +3,34 @@
  * interrupt.h
  *	  Interrupt handling routines.
  *
- * Responses to interrupts are fairly varied and many types of backends
- * have their own implementations, but we provide a few generic things
- * here to facilitate code reuse.
+ * "Interrupts" are a set of flags that represent conditions that should be
+ * handled at a later time.  They are roughly analogous to Unix signals,
+ * except that they are handled cooperatively by checking for them at many
+ * points in the code.
+ *
+ * Interrupt flags can be "raised" synchronously by code that wants to defer
+ * an action (for example: INTERRUPT_CONNECTION_LOST), or asynchronously by
+ * timer signal handlers (for example: INTERRUPT_IDLE_SESSION_TIMEOUT), other
+ * signal handlers (for example: INTERRUPT_QUERY_CANCEL) or "sent" by other
+ * backends setting them directly.
+ *
+ * In the case of asynchronous interrupts, the target backend's latch is also
+ * set, to make sure that the backend wakes from latch sleeps.  Well behaved
+ * backend code performs CHECK_FOR_INTERRUPTS() periodically in long
+ * computations, and should never sleep using mechanisms other than the latch
+ * wait mechanism (except for bounded short periods, eg LWLock waits), so they
+ * should react in good time.
+ *
+ * The "standard" set of interrupts is handled by CHECK_FOR_INTERRUPTS(), and
+ * consists of tasks that are safe to perform at most times.  They can be
+ * suppressed by HOLD_INTERRUPTS()/RESUME_INTERRUPTS().
+ *
+ * Other special interrupts are checked for explicitly.
+
+ * Responses to signals that are translated to interrupts are fairly varied
+ * and many types of backends have their own implementations, but we provide a
+ * few generic signal handlers and interrupt checks here to facilitate code
+ * reuse.
  *
  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -19,12 +44,152 @@
 #ifndef INTERRUPT_H
 #define INTERRUPT_H
 
+#include "port/atomics.h"
+
 #include <signal.h>
 
 extern PGDLLIMPORT volatile sig_atomic_t ConfigReloadPending;
 extern PGDLLIMPORT volatile sig_atomic_t ShutdownRequestPending;
 
+extern PGDLLIMPORT uint32 InterruptHoldoffCount;
+extern PGDLLIMPORT uint32 QueryCancelHoldoffCount;
+
+extern PGDLLIMPORT pg_atomic_uint32 *MyPendingInterrupts;
+
+typedef enum
+{
+	/* Raised by other backends. */
+	INTERRUPT_SINVAL_CATCHUP,
+	INTERRUPT_NOTIFY,	/* listen/notify interrupt */
+	INTERRUPT_PARALLEL_MESSAGE,	/* message from cooperating parallel backend */
+	INTERRUPT_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
+	INTERRUPT_BARRIER,			/* global barrier interrupt  */
+	INTERRUPT_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
+
+	/* Raised by timers. */
+	INTERRUPT_IDLE_SESSION_TIMEOUT,
+	INTERRUPT_IDLE_TRANSACTION_TIMEOUT,
+	INTERRUPT_CHECK_CONNECTION_TIMEOUT,
+
+	/* Raised by signal handlers (usually from the postmaster). */
+	INTERRUPT_QUERY_CANCEL,
+	INTERRUPT_DIE,
+
+	/* Raised synchronously. */
+	INTERRUPT_CONNECTION_LOST,
+
+	/* Raised by recovery. */
+	INTERRUPT_RECOVERY_CONFLICT,
+
+	/* Recovery conflict reasons */
+	INTERRUPT_RECOVERY_CONFLICT_DATABASE,
+	INTERRUPT_RECOVERY_CONFLICT_TABLESPACE,
+	INTERRUPT_RECOVERY_CONFLICT_LOCK,
+	INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT,
+	INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN,
+	INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK
+} InterruptType;
+
+/*
+ * The set of "standard" interrupts that CHECK_FOR_INTERRUPTS() and
+ * ProcessInterrupts() handles.  These perform work that is safe to run
+ * whenever interrupts are not "held".  Other kinds of interrupts are only
+ * handled at more restricted times.
+ */
+#define INTERRUPT_STANDARD_MASK						\
+	((1 << INTERRUPT_DIE) |							\
+	 (1 << INTERRUPT_CHECK_CONNECTION_TIMEOUT) |	\
+	 (1 << INTERRUPT_RECOVERY_CONFLICT) |			\
+	 (1 << INTERRUPT_CONNECTION_LOST) |				\
+	 (1 << INTERRUPT_QUERY_CANCEL) |				\
+	 (1 << INTERRUPT_IDLE_TRANSACTION_TIMEOUT) |	\
+	 (1 << INTERRUPT_IDLE_SESSION_TIMEOUT) |		\
+	 (1 << INTERRUPT_BARRIER) |						\
+	 (1 << INTERRUPT_PARALLEL_MESSAGE) |			\
+	 (1 << INTERRUPT_WALSND_INIT_STOPPING) |		\
+	 (1 << INTERRUPT_LOG_MEMORY_CONTEXT))
+
+/* Test whether an interrupt is pending */
+#define INTERRUPTS_PENDING_CONDITION() \
+	unlikely((pg_atomic_read_u32(MyPendingInterrupts) & INTERRUPT_STANDARD_MASK) != 0)
+
+/* Service interrupt, if one is pending and it's safe to service it now */
+#define CHECK_FOR_INTERRUPTS() \
+do { \
+	if (INTERRUPTS_PENDING_CONDITION()) \
+		ProcessInterrupts(); \
+} while(0)
+
+/* Is ProcessInterrupts() guaranteed to clear InterruptPending? */
+#define INTERRUPTS_CAN_BE_PROCESSED() \
+	(InterruptHoldoffCount == 0 && CritSectionCount == 0 && \
+	 QueryCancelHoldoffCount == 0)
+
+#define HOLD_INTERRUPTS()  (InterruptHoldoffCount++)
+
+#define RESUME_INTERRUPTS() \
+do { \
+	Assert(InterruptHoldoffCount > 0); \
+	InterruptHoldoffCount--; \
+} while(0)
+
+#define HOLD_CANCEL_INTERRUPTS()  (QueryCancelHoldoffCount++)
+
+#define RESUME_CANCEL_INTERRUPTS() \
+do { \
+	Assert(QueryCancelHoldoffCount > 0); \
+	QueryCancelHoldoffCount--; \
+} while(0)
+
+/*
+ * Test an interrupt flag.
+ */
+static inline bool
+InterruptPending(InterruptType reason)
+{
+	return (pg_atomic_read_u32(MyPendingInterrupts) & (1 << reason)) != 0;
+}
+
+/*
+ * Test and clear an interrupt flag.
+ */
+static inline bool
+InterruptConsume(InterruptType reason)
+{
+	/* Careful here --- don't clear flag if we haven't seen it set */
+	if (pg_atomic_read_u32(MyPendingInterrupts) & (1 << reason))
+	{
+		pg_atomic_fetch_and_u32(MyPendingInterrupts, ~(1 << reason));
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * Clear an interrupt flag.
+ */
+static inline void
+InterruptClear(InterruptType reason)
+{
+	pg_atomic_fetch_and_u32(MyPendingInterrupts, ~(1 << reason));
+}
+
+extern void InterruptRaise(InterruptType reason);
+extern void InterruptSend(InterruptType reason, int pgprocno);
+extern void InterruptLocal(void);
+extern void InterruptShared(void);
+
+/* in tcop/postgres.c */
+extern void ProcessInterrupts(void);
+
+/*
+ * A handler for INTERRUPT_BARRIER, and the reload/exit/shutdown flags set by
+ * the signal handlers below.
+ */
 extern void HandleMainLoopInterrupts(void);
+
+/* Common signal handlers. */
 extern void SignalHandlerForConfigReload(SIGNAL_ARGS);
 extern void SignalHandlerForCrashExit(SIGNAL_ARGS);
 extern void SignalHandlerForShutdownRequest(SIGNAL_ARGS);
diff --git a/src/include/regex/regcustom.h b/src/include/regex/regcustom.h
index 100c52d640..80e32710f1 100644
--- a/src/include/regex/regcustom.h
+++ b/src/include/regex/regcustom.h
@@ -53,6 +53,7 @@
 #include "mb/pg_wchar.h"
 
 #include "miscadmin.h"			/* needed by rcancelrequested/rstacktoodeep */
+#include "postmaster/interrupt.h"
 
 
 /* overrides for regguts.h definitions, if any */
diff --git a/src/include/replication/walsender_private.h b/src/include/replication/walsender_private.h
index 68571072e7..f6ceb4756b 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -39,6 +39,7 @@ typedef enum WalSndState
 typedef struct WalSnd
 {
 	pid_t		pid;			/* this walsender's PID, or 0 if not active */
+	int			pgprocno;		/* this walsender's pgprocno */
 
 	WalSndState state;			/* this walsender's state */
 	XLogRecPtr	sentPtr;		/* WAL has been sent up to this point */
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index be67d8a861..1e8761da2a 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -251,6 +251,8 @@ struct PGPROC
 	PGPROC	   *lockGroupLeader;	/* lock group leader, if I'm a member */
 	dlist_head	lockGroupMembers;	/* list of members, if I'm a leader */
 	dlist_node	lockGroupLink;	/* my member link, if I'm a member */
+
+	pg_atomic_uint32 pending_interrupts;
 };
 
 /* NOTE: "typedef struct PGPROC PGPROC" appears in storage/lock.h. */
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index b01fa52139..21963e1682 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -71,14 +71,14 @@ extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
 												   bool excludeXmin0, bool allDbs, int excludeVacuum,
 												   int *nvxids);
 extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid);
-extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode);
-extern pid_t SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode,
+extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, InterruptType sigmode);
+extern pid_t SignalVirtualTransaction(VirtualTransactionId vxid, InterruptType sigmode,
 									  bool conflictPending);
 
 extern bool MinimumActiveBackends(int min);
 extern int	CountDBBackends(Oid databaseid);
 extern int	CountDBConnections(Oid databaseid);
-extern void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending);
+extern void CancelDBBackends(Oid databaseid, InterruptType sigmode, bool conflictPending);
 extern int	CountUserBackends(Oid roleid);
 extern bool CountOtherDBBackends(Oid databaseId,
 								 int *nbackends, int *nprepared);
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index eec186be2e..c3af841e2b 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -17,36 +17,6 @@
 #include "storage/backendid.h"
 
 
-/*
- * Reasons for signaling a Postgres child process (a backend or an auxiliary
- * process, like checkpointer).  We can cope with concurrent signals for different
- * reasons.  However, if the same reason is signaled multiple times in quick
- * succession, the process is likely to observe only one notification of it.
- * This is okay for the present uses.
- *
- * Also, because of race conditions, it's important that all the signals be
- * defined so that no harm is done if a process mistakenly receives one.
- */
-typedef enum
-{
-	PROCSIG_CATCHUP_INTERRUPT,	/* sinval catchup interrupt */
-	PROCSIG_NOTIFY_INTERRUPT,	/* listen/notify interrupt */
-	PROCSIG_PARALLEL_MESSAGE,	/* message from cooperating parallel backend */
-	PROCSIG_WALSND_INIT_STOPPING,	/* ask walsenders to prepare for shutdown  */
-	PROCSIG_BARRIER,			/* global barrier interrupt  */
-	PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
-
-	/* Recovery conflict reasons */
-	PROCSIG_RECOVERY_CONFLICT_DATABASE,
-	PROCSIG_RECOVERY_CONFLICT_TABLESPACE,
-	PROCSIG_RECOVERY_CONFLICT_LOCK,
-	PROCSIG_RECOVERY_CONFLICT_SNAPSHOT,
-	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
-	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
-
-	NUM_PROCSIGNALS				/* Must be last! */
-} ProcSignalReason;
-
 typedef enum
 {
 	/*
@@ -64,13 +34,9 @@ extern Size ProcSignalShmemSize(void);
 extern void ProcSignalShmemInit(void);
 
 extern void ProcSignalInit(int pss_idx);
-extern int	SendProcSignal(pid_t pid, ProcSignalReason reason,
-						   BackendId backendId);
 
 extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
 extern void WaitForProcSignalBarrier(uint64 generation);
 extern void ProcessProcSignalBarrier(void);
 
-extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
-
 #endif							/* PROCSIGNAL_H */
diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h
index f03dc23b14..d476543b52 100644
--- a/src/include/storage/sinval.h
+++ b/src/include/storage/sinval.h
@@ -125,16 +125,11 @@ typedef union
 /* Counter of messages processed; don't worry about overflow. */
 extern uint64 SharedInvalidMessageCounter;
 
-extern volatile sig_atomic_t catchupInterruptPending;
-
 extern void SendSharedInvalidMessages(const SharedInvalidationMessage *msgs,
 									  int n);
 extern void ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *msg),
 										 void (*resetFunction) (void));
 
-/* signal handler for catchup events (PROCSIG_CATCHUP_INTERRUPT) */
-extern void HandleCatchupInterrupt(void);
-
 /*
  * enable/disable processing of catchup events directly from signal handler.
  * The enable routine first performs processing of any catchup events that
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index 38fd85a431..3530d26693 100644
--- a/src/include/storage/standby.h
+++ b/src/include/storage/standby.h
@@ -15,6 +15,7 @@
 #define STANDBY_H
 
 #include "datatype/timestamp.h"
+#include "postmaster/interrupt.h"
 #include "storage/lock.h"
 #include "storage/procsignal.h"
 #include "storage/relfilenode.h"
@@ -42,7 +43,7 @@ extern void CheckRecoveryConflictDeadlock(void);
 extern void StandbyDeadLockHandler(void);
 extern void StandbyTimeoutHandler(void);
 extern void StandbyLockTimeoutHandler(void);
-extern void LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start,
+extern void LogRecoveryConflict(InterruptType reason, TimestampTz wait_start,
 								TimestampTz cur_ts, VirtualTransactionId *wait_list,
 								bool still_waiting);
 
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 5c77075aed..b5138f7a2a 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -17,6 +17,7 @@
 #include "nodes/params.h"
 #include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
+#include "postmaster/interrupt.h"
 #include "storage/procsignal.h"
 #include "utils/guc.h"
 #include "utils/queryenvironment.h"
@@ -68,8 +69,7 @@ extern void die(SIGNAL_ARGS);
 extern void quickdie(SIGNAL_ARGS) pg_attribute_noreturn();
 extern void StatementCancelHandler(SIGNAL_ARGS);
 extern void FloatExceptionHandler(SIGNAL_ARGS) pg_attribute_noreturn();
-extern void RecoveryConflictInterrupt(ProcSignalReason reason); /* called from SIGUSR1
-																 * handler */
+extern void RecoveryConflictInterrupt(InterruptType reason);
 extern void ProcessClientReadInterrupt(bool blocked);
 extern void ProcessClientWriteInterrupt(bool blocked);
 
-- 
2.30.2

Reply via email to