From 1518286ac368554c9c829e85570ece5d8daab84d Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Mon, 4 Nov 2024 21:00:03 +0200
Subject: [PATCH v4 1/3] Replace Latches with Interrupts

The Latch was a flag, with an inter-process signalling mechanism that
allowed a process to wait for the Latch to be set. My original vision
for Latches was that you could have different latches for different
things that you need to wait for, but but in practice, almost all code
used one "process latch" that was shared for all wakeups. The only
exception was the "recoveryWakeupLatch". I think the reason that we
ended up just sharing the same latch for all wakeups is that it was
cumbersome in practice to deal with multiple latches. For starters,
there was no machinery for waiting for multiple latches at the same
time. Secondly, changing the "ownership" of a latch needed extra
locking or other coordination. Thirdly, each inter-process latch
needed to be initialized at postmaster startup time.

This patch embraces the reality of how Latches were used, and replaces
the Latches with per-process interrupt mask. You can no longer
allocate Latches in arbitrary shared memory structs, an interrupt is
always directed at a particular process, addressed by its ProcNumber.
Each process has a bitmask of pending interrupts in PGPROC.

This commit introduces two interrupt bits. INTERRUPT_GENERAL_WAKEUP
replaces the general purpose per-process latch. All code that
previously set a process's process latch now sets its
INTERRUPT_GENERAL_WAKEUP interrupt bit instead.

The other interrupt bit, INTERRUPT_RECOVERY_CONTINUE, replaces
recoveryWakeupLatch. With this new interface, the code can easily wait
for the regular interrupts (INTERRUPT_GENERAL_WAKEUP) at the same time
as INTERRUPT_RECOVERY_CONTINUE. Previously, the code that waited on
recoveryWakeupLatch had to resort to timeouts to react to other
signals, because it was not possible to wait for two latches at the
same time. Previous attempt at unifying those wakeups was in commit
ac22929a26, but that was reverted in commit 00f690a239 because it
caused a lot of spurious wakeups when startup process was waiting for
recovery conflicts. The new machinery avoids that problem, by making
it easy to wait for two interrupts at the same time.

More interrupt bits are planned for followup patches, to replace the
various ProcSignal bits, as well as ConfigReloadPending and
ShutdownRequestPending.

This also moves the WaitEventSet functions to a different source file,
waiteventset.c. This separates the platform-dependent code waiting and
signalling code from the platform-independent parts.

Reviewed-by: Thomas Munro
Discussion: https://www.postgresql.org/message-id/476672e7-62f1-4cab-a822-f3a8e949dd3f@iki.fi
---
 contrib/pg_prewarm/autoprewarm.c              |  20 +-
 contrib/postgres_fdw/connection.c             |  22 +-
 contrib/postgres_fdw/postgres_fdw.c           |   4 +-
 doc/src/sgml/bgworker.sgml                    |   2 +-
 doc/src/sgml/sources.sgml                     |   6 +-
 src/backend/access/heap/vacuumlazy.c          |  11 +-
 src/backend/access/transam/README             |   2 +-
 src/backend/access/transam/parallel.c         |  31 +-
 src/backend/access/transam/xlog.c             |  14 +-
 src/backend/access/transam/xlogfuncs.c        |  12 +-
 src/backend/access/transam/xlogrecovery.c     |  91 ++-
 src/backend/access/transam/xlogutils.c        |   6 +-
 src/backend/backup/basebackup_throttle.c      |  18 +-
 src/backend/commands/async.c                  |  13 +-
 src/backend/commands/vacuum.c                 |   4 +-
 src/backend/executor/nodeAppend.c             |   5 +-
 src/backend/executor/nodeGather.c             |   8 +-
 src/backend/libpq/auth.c                      |   9 +-
 src/backend/libpq/be-secure-gssapi.c          |  15 +-
 src/backend/libpq/be-secure-openssl.c         |   7 +-
 src/backend/libpq/be-secure.c                 |  29 +-
 src/backend/libpq/pqcomm.c                    |  29 +-
 src/backend/libpq/pqmq.c                      |   8 +-
 src/backend/libpq/pqsignal.c                  |   2 +-
 src/backend/postmaster/autovacuum.c           |  22 +-
 src/backend/postmaster/auxprocess.c           |   1 +
 src/backend/postmaster/bgworker.c             |  18 +-
 src/backend/postmaster/bgwriter.c             |  37 +-
 src/backend/postmaster/checkpointer.c         |  24 +-
 src/backend/postmaster/interrupt.c            |   6 +-
 src/backend/postmaster/pgarch.c               |  27 +-
 src/backend/postmaster/postmaster.c           |  33 +-
 src/backend/postmaster/startup.c              |  11 +-
 src/backend/postmaster/syslogger.c            |  18 +-
 src/backend/postmaster/walsummarizer.c        |  24 +-
 src/backend/postmaster/walwriter.c            |  23 +-
 .../libpqwalreceiver/libpqwalreceiver.c       |  32 +-
 .../replication/logical/applyparallelworker.c |  39 +-
 src/backend/replication/logical/launcher.c    |  54 +-
 src/backend/replication/logical/slotsync.c    |  23 +-
 src/backend/replication/logical/tablesync.c   |  35 +-
 src/backend/replication/logical/worker.c      |  25 +-
 src/backend/replication/syncrep.c             |  29 +-
 src/backend/replication/walreceiver.c         |  47 +-
 src/backend/replication/walreceiverfuncs.c    |   3 +-
 src/backend/replication/walsender.c           |  27 +-
 src/backend/storage/buffer/bufmgr.c           |  10 +-
 src/backend/storage/buffer/freelist.c         |  27 +-
 src/backend/storage/ipc/Makefile              |   5 +-
 src/backend/storage/ipc/interrupt.c           | 114 ++++
 src/backend/storage/ipc/meson.build           |   3 +-
 src/backend/storage/ipc/procsignal.c          |   6 +-
 src/backend/storage/ipc/shm_mq.c              | 102 +--
 src/backend/storage/ipc/signalfuncs.c         |  11 +-
 src/backend/storage/ipc/sinval.c              |  12 +-
 src/backend/storage/ipc/standby.c             |  22 +-
 .../storage/ipc/{latch.c => waiteventset.c}   | 616 +++++++-----------
 src/backend/storage/lmgr/condition_variable.c |  34 +-
 src/backend/storage/lmgr/predicate.c          |   8 +-
 src/backend/storage/lmgr/proc.c               | 127 ++--
 src/backend/storage/sync/sync.c               |   6 +-
 src/backend/tcop/postgres.c                   |  25 +-
 src/backend/utils/adt/misc.c                  |  26 +-
 src/backend/utils/adt/timestamp.c             |   4 +-
 src/backend/utils/init/globals.c              |   9 -
 src/backend/utils/init/miscinit.c             |  60 +-
 src/backend/utils/init/postinit.c             |  11 +-
 src/backend/utils/misc/timeout.c              |   8 +-
 src/backend/utils/mmgr/mcxt.c                 |   2 +-
 src/include/access/parallel.h                 |   2 +
 src/include/libpq/libpq-be-fe-helpers.h       |  45 +-
 src/include/libpq/libpq.h                     |   4 +-
 src/include/miscadmin.h                       |   4 -
 src/include/storage/interrupt.h               | 159 +++++
 src/include/storage/latch.h                   | 196 ------
 src/include/storage/proc.h                    |  17 +-
 src/include/storage/waiteventset.h            | 119 ++++
 src/port/pgsleep.c                            |   8 +-
 src/test/isolation/isolationtester.c          |   2 +-
 src/test/modules/test_shm_mq/setup.c          |   9 +-
 src/test/modules/test_shm_mq/test.c           |  13 +-
 src/test/modules/test_shm_mq/worker.c         |   3 +-
 src/test/modules/worker_spi/worker_spi.c      |  20 +-
 src/tools/pgindent/typedefs.list              |   2 +-
 84 files changed, 1394 insertions(+), 1383 deletions(-)
 create mode 100644 src/backend/storage/ipc/interrupt.c
 rename src/backend/storage/ipc/{latch.c => waiteventset.c} (77%)
 create mode 100644 src/include/storage/interrupt.h
 delete mode 100644 src/include/storage/latch.h
 create mode 100644 src/include/storage/waiteventset.h

diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index fac4051e1aa..65a423c7ddd 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -37,8 +37,8 @@
 #include "storage/dsm.h"
 #include "storage/dsm_registry.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/procsignal.h"
 #include "storage/smgr.h"
@@ -216,10 +216,10 @@ autoprewarm_main(Datum main_arg)
 		if (autoprewarm_interval <= 0)
 		{
 			/* We're only dumping at shutdown, so just wait forever. */
-			(void) WaitLatch(MyLatch,
-							 WL_LATCH_SET | WL_EXIT_ON_PM_DEATH,
-							 -1L,
-							 PG_WAIT_EXTENSION);
+			(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+								 WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+								 -1L,
+								 PG_WAIT_EXTENSION);
 		}
 		else
 		{
@@ -243,14 +243,14 @@ autoprewarm_main(Datum main_arg)
 			}
 
 			/* Sleep until the next dump time. */
-			(void) WaitLatch(MyLatch,
-							 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-							 delay_in_ms,
-							 PG_WAIT_EXTENSION);
+			(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+								 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+								 delay_in_ms,
+								 PG_WAIT_EXTENSION);
 		}
 
 		/* Reset the latch, loop. */
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 
 	/*
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 2326f391d34..4b46a7cc8cc 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -26,7 +26,7 @@
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "postgres_fdw.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
 #include "utils/builtins.h"
 #include "utils/hsearch.h"
 #include "utils/inval.h"
@@ -731,8 +731,8 @@ do_sql_command_end(PGconn *conn, const char *sql, bool consume_input)
 	/*
 	 * If requested, consume whatever data is available from the socket. (Note
 	 * that if all data is available, this allows pgfdw_get_result to call
-	 * PQgetResult without forcing the overhead of WaitLatchOrSocket, which
-	 * would be large compared to the overhead of PQconsumeInput.)
+	 * PQgetResult without forcing the overhead of WaitInterruptOrSocket,
+	 * which would be large compared to the overhead of PQconsumeInput.)
 	 */
 	if (consume_input && !PQconsumeInput(conn))
 		pgfdw_report_error(ERROR, NULL, conn, false, sql);
@@ -1367,7 +1367,7 @@ pgfdw_cancel_query_end(PGconn *conn, TimestampTz endtime, bool consume_input)
 	/*
 	 * If requested, consume whatever data is available from the socket. (Note
 	 * that if all data is available, this allows pgfdw_get_cleanup_result to
-	 * call PQgetResult without forcing the overhead of WaitLatchOrSocket,
+	 * call PQgetResult without forcing the overhead of WaitInterruptOrSocket,
 	 * which would be large compared to the overhead of PQconsumeInput.)
 	 */
 	if (consume_input && !PQconsumeInput(conn))
@@ -1461,7 +1461,7 @@ pgfdw_exec_cleanup_query_end(PGconn *conn, const char *query,
 	/*
 	 * If requested, consume whatever data is available from the socket. (Note
 	 * that if all data is available, this allows pgfdw_get_cleanup_result to
-	 * call PQgetResult without forcing the overhead of WaitLatchOrSocket,
+	 * call PQgetResult without forcing the overhead of WaitInterruptOrSocket,
 	 * which would be large compared to the overhead of PQconsumeInput.)
 	 */
 	if (consume_input && !PQconsumeInput(conn))
@@ -1541,12 +1541,12 @@ pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime, PGresult **result,
 					pgfdw_we_cleanup_result = WaitEventExtensionNew("PostgresFdwCleanupResult");
 
 				/* Sleep until there's something to do */
-				wc = WaitLatchOrSocket(MyLatch,
-									   WL_LATCH_SET | WL_SOCKET_READABLE |
-									   WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-									   PQsocket(conn),
-									   cur_timeout, pgfdw_we_cleanup_result);
-				ResetLatch(MyLatch);
+				wc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL_WAKEUP,
+										   WL_INTERRUPT | WL_SOCKET_READABLE |
+										   WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+										   PQsocket(conn),
+										   cur_timeout, pgfdw_we_cleanup_result);
+				ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 				CHECK_FOR_INTERRUPTS();
 
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 53733d642d0..2ddfe1302f2 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -38,7 +38,7 @@
 #include "optimizer/tlist.h"
 #include "parser/parsetree.h"
 #include "postgres_fdw.h"
-#include "storage/latch.h"
+#include "storage/waiteventset.h"
 #include "utils/builtins.h"
 #include "utils/float.h"
 #include "utils/guc.h"
@@ -7354,7 +7354,7 @@ postgresForeignAsyncConfigureWait(AsyncRequest *areq)
 		Assert(pendingAreq == areq);
 
 	AddWaitEventToSet(set, WL_SOCKET_READABLE, PQsocket(fsstate->conn),
-					  NULL, areq);
+					  0, areq);
 }
 
 /*
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..3171054e55d 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -227,7 +227,7 @@ typedef struct BackgroundWorker
    reinitializes the cluster due to a backend failure.  Backends which need
    to suspend execution only temporarily should use an interruptible sleep
    rather than exiting; this can be achieved by calling
-   <function>WaitLatch()</function>. Make sure the
+   <function>WaitInterrupt()</function>. Make sure the
    <literal>WL_POSTMASTER_DEATH</literal> flag is set when calling that function, and
    verify the return code for a prompt exit in the emergency case that
    <command>postgres</command> itself has terminated.
diff --git a/doc/src/sgml/sources.sgml b/doc/src/sgml/sources.sgml
index fa68d4d024a..9b1fe147d6c 100644
--- a/doc/src/sgml/sources.sgml
+++ b/doc/src/sgml/sources.sgml
@@ -989,19 +989,19 @@ MemoryContextSwitchTo(MemoryContext context)
      call async-signal safe functions (as defined in POSIX) and access
      variables of type <literal>volatile sig_atomic_t</literal>. A few
      functions in <command>postgres</command> are also deemed signal safe, importantly
-     <function>SetLatch()</function>.
+     <function>RaiseInterrupt()</function>.
     </para>
     <para>
      In most cases signal handlers should do nothing more than note
      that a signal has arrived, and wake up code running outside of
-     the handler using a latch. An example of such a handler is the
+     the handler using RaiseInterrupt(). An example of such a handler is the
      following:
 <programlisting>
 static void
 handle_sighup(SIGNAL_ARGS)
 {
     got_SIGHUP = true;
-    SetLatch(MyLatch);
+    RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 </programlisting>
     </para>
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 793bd33cb4d..9f54d7a12e9 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -53,6 +53,7 @@
 #include "postmaster/autovacuum.h"
 #include "storage/bufmgr.h"
 #include "storage/freespace.h"
+#include "storage/interrupt.h"
 #include "storage/lmgr.h"
 #include "utils/lsyscache.h"
 #include "utils/pg_rusage.h"
@@ -2607,11 +2608,11 @@ lazy_truncate_heap(LVRelState *vacrel)
 				return;
 			}
 
-			(void) WaitLatch(MyLatch,
-							 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-							 VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL,
-							 WAIT_EVENT_VACUUM_TRUNCATE);
-			ResetLatch(MyLatch);
+			(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+								 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+								 VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL,
+								 WAIT_EVENT_VACUUM_TRUNCATE);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 		}
 
 		/*
diff --git a/src/backend/access/transam/README b/src/backend/access/transam/README
index 6e4711dace7..b12d8be647c 100644
--- a/src/backend/access/transam/README
+++ b/src/backend/access/transam/README
@@ -794,7 +794,7 @@ commit to minimize the window in which the filesystem change has been made
 but the transaction isn't guaranteed committed.
 
 The walwriter regularly wakes up (via wal_writer_delay) or is woken up
-(via its latch, which is set by backends committing asynchronously) and
+(via an interrupt, which is set by backends committing asynchronously) and
 performs an XLogBackgroundFlush().  This checks the location of the last
 completely filled WAL page.  If that has moved forwards, then we write all
 the changed buffers up to that point, so that under full load we write
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index 0a1e089ec1d..e1970ff9714 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -33,6 +33,7 @@
 #include "miscadmin.h"
 #include "optimizer/optimizer.h"
 #include "pgstat.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/predicate.h"
 #include "storage/spin.h"
@@ -755,16 +756,16 @@ WaitForParallelWorkersToAttach(ParallelContext *pcxt)
 			{
 				/*
 				 * Worker not yet started, so we must wait.  The postmaster
-				 * will notify us if the worker's state changes.  Our latch
-				 * might also get set for some other reason, but if so we'll
-				 * just end up waiting for the same worker again.
+				 * will notify us if the worker's state changes.  The
+				 * interrupt might also get set for some other reason, but if
+				 * so we'll just end up waiting for the same worker again.
 				 */
-				rc = WaitLatch(MyLatch,
-							   WL_LATCH_SET | WL_EXIT_ON_PM_DEATH,
-							   -1, WAIT_EVENT_BGWORKER_STARTUP);
+				rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+								   WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+								   -1, WAIT_EVENT_BGWORKER_STARTUP);
 
-				if (rc & WL_LATCH_SET)
-					ResetLatch(MyLatch);
+				if (rc & WL_INTERRUPT)
+					ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			}
 		}
 
@@ -873,15 +874,17 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 				 * the worker writes messages and terminates after the
 				 * CHECK_FOR_INTERRUPTS() near the top of this function and
 				 * before the call to GetBackgroundWorkerPid().  In that case,
-				 * or latch should have been set as well and the right things
-				 * will happen on the next pass through the loop.
+				 * INTERRUPT_GENERAL_WAKEUP should have been set as well and
+				 * the right things will happen on the next pass through the
+				 * loop.
 				 */
 			}
 		}
 
-		(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, -1,
-						 WAIT_EVENT_PARALLEL_FINISH);
-		ResetLatch(MyLatch);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, -1,
+							 WAIT_EVENT_PARALLEL_FINISH);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 
 	if (pcxt->toc != NULL)
@@ -1034,7 +1037,7 @@ HandleParallelMessageInterrupt(void)
 {
 	InterruptPending = true;
 	ParallelMessagePending = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 6f58412bcab..0a9920a61f5 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -84,9 +84,9 @@
 #include "replication/walsender.h"
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/large_object.h"
-#include "storage/latch.h"
 #include "storage/predicate.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
@@ -2676,7 +2676,7 @@ XLogSetAsyncXactLSN(XLogRecPtr asyncXactLSN)
 		ProcNumber	walwriterProc = procglobal->walwriterProc;
 
 		if (walwriterProc != INVALID_PROC_NUMBER)
-			SetLatch(&GetPGProcByNumber(walwriterProc)->procLatch);
+			SendInterrupt(INTERRUPT_GENERAL_WAKEUP, ProcGlobal->walwriterProc);
 	}
 }
 
@@ -9347,11 +9347,11 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive)
 				reported_waiting = true;
 			}
 
-			(void) WaitLatch(MyLatch,
-							 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-							 1000L,
-							 WAIT_EVENT_BACKUP_WAIT_WAL_ARCHIVE);
-			ResetLatch(MyLatch);
+			(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+								 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+								 1000L,
+								 WAIT_EVENT_BACKUP_WAIT_WAL_ARCHIVE);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 			if (++waits >= seconds_before_warning)
 			{
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index b0c6d7c6875..77d25447e95 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -28,7 +28,7 @@
 #include "pgstat.h"
 #include "replication/walreceiver.h"
 #include "storage/fd.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
 #include "storage/standby.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
@@ -718,17 +718,17 @@ pg_promote(PG_FUNCTION_ARGS)
 	{
 		int			rc;
 
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		if (!RecoveryInProgress())
 			PG_RETURN_BOOL(true);
 
 		CHECK_FOR_INTERRUPTS();
 
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   1000L / WAITS_PER_SECOND,
-					   WAIT_EVENT_PROMOTE);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+						   1000L / WAITS_PER_SECOND,
+						   WAIT_EVENT_PROMOTE);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index 05c738d6614..21f2d57036a 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -52,9 +52,10 @@
 #include "replication/slotsync.h"
 #include "replication/walreceiver.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/pmsignal.h"
+#include "storage/proc.h"
 #include "storage/procarray.h"
 #include "storage/spin.h"
 #include "utils/datetime.h"
@@ -315,23 +316,6 @@ typedef struct XLogRecoveryCtlData
 	 */
 	bool		SharedPromoteIsTriggered;
 
-	/*
-	 * recoveryWakeupLatch is used to wake up the startup process to continue
-	 * WAL replay, if it is waiting for WAL to arrive or promotion to be
-	 * requested.
-	 *
-	 * Note that the startup process also uses another latch, its procLatch,
-	 * to wait for recovery conflict. If we get rid of recoveryWakeupLatch for
-	 * signaling the startup process in favor of using its procLatch, which
-	 * comports better with possible generic signal handlers using that latch.
-	 * But we should not do that because the startup process doesn't assume
-	 * that it's waken up by walreceiver process or SIGHUP signal handler
-	 * while it's waiting for recovery conflict. The separate latches,
-	 * recoveryWakeupLatch and procLatch, should be used for inter-process
-	 * communication for WAL replay and recovery conflict, respectively.
-	 */
-	Latch		recoveryWakeupLatch;
-
 	/*
 	 * Last record successfully replayed.
 	 */
@@ -466,7 +450,6 @@ XLogRecoveryShmemInit(void)
 	memset(XLogRecoveryCtl, 0, sizeof(XLogRecoveryCtlData));
 
 	SpinLockInit(&XLogRecoveryCtl->info_lck);
-	InitSharedLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
 	ConditionVariableInit(&XLogRecoveryCtl->recoveryNotPausedCV);
 }
 
@@ -540,13 +523,6 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 	readRecoverySignalFile();
 	validateRecoveryParameters();
 
-	/*
-	 * Take ownership of the wakeup latch if we're going to sleep during
-	 * recovery, if required.
-	 */
-	if (ArchiveRecoveryRequested)
-		OwnLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
-
 	/*
 	 * Set the WAL reading processor now, as it will be needed when reading
 	 * the checkpoint record required (backup_label or not).
@@ -1634,13 +1610,6 @@ ShutdownWalRecovery(void)
 		snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYHISTORY");
 		unlink(recoveryPath);	/* ignore any error */
 	}
-
-	/*
-	 * We don't need the latch anymore. It's not strictly necessary to disown
-	 * it, but let's do it for the sake of tidiness.
-	 */
-	if (ArchiveRecoveryRequested)
-		DisownLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
 }
 
 /*
@@ -1800,7 +1769,7 @@ PerformWalRecovery(void)
 			}
 
 			/*
-			 * If we've been asked to lag the primary, wait on latch until
+			 * If we've been asked to lag the primary, wait on interrupt until
 			 * enough time has passed.
 			 */
 			if (recoveryApplyDelay(xlogreader))
@@ -3023,8 +2992,8 @@ recoveryApplyDelay(XLogReaderState *record)
 	delayUntil = TimestampTzPlusMilliseconds(xtime, recovery_min_apply_delay);
 
 	/*
-	 * Exit without arming the latch if it's already past time to apply this
-	 * record
+	 * Exit without clearing the interrupt if it's already past time to apply
+	 * this record
 	 */
 	msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(), delayUntil);
 	if (msecs <= 0)
@@ -3032,11 +3001,19 @@ recoveryApplyDelay(XLogReaderState *record)
 
 	while (true)
 	{
-		ResetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
+		/*
+		 * INTERRUPT_GENERAL_WAKUP is used for all the usual interrupts, like
+		 * config reloads.  The wakeups when more WAL arrive use a different
+		 * interrupt number (INTERRUPT_RECOVERY_CONTINUE) so that more WAL
+		 * arriving don't wake up the startup process excessively, when we're
+		 * waiting in other places, like for recovery conflicts.
+		 */
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/* This might change recovery_min_apply_delay. */
 		HandleStartupProcInterrupts();
 
+		ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE);
 		if (CheckForStandbyTrigger())
 			break;
 
@@ -3057,10 +3034,11 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		elog(DEBUG2, "recovery apply delay %ld milliseconds", msecs);
 
-		(void) WaitLatch(&XLogRecoveryCtl->recoveryWakeupLatch,
-						 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-						 msecs,
-						 WAIT_EVENT_RECOVERY_APPLY_DELAY);
+		(void) WaitInterrupt(1 << INTERRUPT_RECOVERY_CONTINUE |
+							 1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 msecs,
+							 WAIT_EVENT_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -3703,15 +3681,17 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 						/* Do background tasks that might benefit us later. */
 						KnownAssignedTransactionIdsIdleMaintenance();
 
-						(void) WaitLatch(&XLogRecoveryCtl->recoveryWakeupLatch,
-										 WL_LATCH_SET | WL_TIMEOUT |
-										 WL_EXIT_ON_PM_DEATH,
-										 wait_time,
-										 WAIT_EVENT_RECOVERY_RETRIEVE_RETRY_INTERVAL);
-						ResetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
+						(void) WaitInterrupt(1 << INTERRUPT_RECOVERY_CONTINUE |
+											 1 << INTERRUPT_GENERAL_WAKEUP,
+											 WL_INTERRUPT | WL_TIMEOUT |
+											 WL_EXIT_ON_PM_DEATH,
+											 wait_time,
+											 WAIT_EVENT_RECOVERY_RETRIEVE_RETRY_INTERVAL);
+						ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE);
 						now = GetCurrentTimestamp();
 
 						/* Handle interrupt signals of startup process */
+						ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 						HandleStartupProcInterrupts();
 					}
 					last_fail_time = now;
@@ -3978,11 +3958,12 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 * Wait for more WAL to arrive, when we will be woken
 					 * immediately by the WAL receiver.
 					 */
-					(void) WaitLatch(&XLogRecoveryCtl->recoveryWakeupLatch,
-									 WL_LATCH_SET | WL_EXIT_ON_PM_DEATH,
-									 -1L,
-									 WAIT_EVENT_RECOVERY_WAL_STREAM);
-					ResetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
+					(void) WaitInterrupt(1 << INTERRUPT_RECOVERY_CONTINUE |
+										 1 << INTERRUPT_GENERAL_WAKEUP,
+										 WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+										 -1L,
+										 WAIT_EVENT_RECOVERY_WAL_STREAM);
+					ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE);
 					break;
 				}
 
@@ -4002,6 +3983,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 		 * This possibly-long loop needs to handle interrupts of startup
 		 * process.
 		 */
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 		HandleStartupProcInterrupts();
 	}
 
@@ -4476,7 +4458,10 @@ CheckPromoteSignal(void)
 void
 WakeupRecovery(void)
 {
-	SetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
+	ProcNumber	procno = ((volatile PROC_HDR *) ProcGlobal)->startupProc;
+
+	if (procno != INVALID_PROC_NUMBER)
+		SendInterrupt(INTERRUPT_RECOVERY_CONTINUE, procno);
 }
 
 /*
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 5295b85fe07..41f0cde6272 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -853,9 +853,9 @@ wal_segment_close(XLogReaderState *state)
  * output method outside walsender, e.g. in a bgworker.
  *
  * TODO: The walsender has its own version of this, but it relies on the
- * walsender's latch being set whenever WAL is flushed. No such infrastructure
- * exists for normal backends, so we have to do a check/sleep/repeat style of
- * loop for now.
+ * walsender's interrupt being set whenever WAL is flushed. No such
+ * infrastructure exists for normal backends, so we have to do a
+ * check/sleep/repeat style of loop for now.
  */
 int
 read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
diff --git a/src/backend/backup/basebackup_throttle.c b/src/backend/backup/basebackup_throttle.c
index 4477945e613..b1c02f7473a 100644
--- a/src/backend/backup/basebackup_throttle.c
+++ b/src/backend/backup/basebackup_throttle.c
@@ -17,7 +17,7 @@
 #include "backup/basebackup_sink.h"
 #include "miscadmin.h"
 #include "pgstat.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
 #include "utils/timestamp.h"
 
 typedef struct bbsink_throttle
@@ -146,7 +146,7 @@ throttle(bbsink_throttle *sink, size_t increment)
 		(sink->throttling_counter / sink->throttling_sample);
 
 	/*
-	 * Since the latch could be set repeatedly because of concurrently WAL
+	 * Since the interrupt could be set repeatedly because of concurrently WAL
 	 * activity, sleep in a loop to ensure enough time has passed.
 	 */
 	for (;;)
@@ -163,21 +163,21 @@ throttle(bbsink_throttle *sink, size_t increment)
 		if (sleep <= 0)
 			break;
 
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
-		/* We're eating a potentially set latch, so check for interrupts */
+		/* We're eating a wakeup, so check for interrupts */
 		CHECK_FOR_INTERRUPTS();
 
 		/*
 		 * (TAR_SEND_SIZE / throttling_sample * elapsed_min_unit) should be
 		 * the maximum time to sleep. Thus the cast to long is safe.
 		 */
-		wait_result = WaitLatch(MyLatch,
-								WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-								(long) (sleep / 1000),
-								WAIT_EVENT_BASE_BACKUP_THROTTLE);
+		wait_result = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+									WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+									(long) (sleep / 1000),
+									WAIT_EVENT_BASE_BACKUP_THROTTLE);
 
-		if (wait_result & WL_LATCH_SET)
+		if (wait_result & WL_INTERRUPT)
 			CHECK_FOR_INTERRUPTS();
 
 		/* Done waiting? */
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 8ed503e1c1b..e0b14f49744 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -91,7 +91,7 @@
  *	  should go out immediately after each commit.
  *
  * 5. Upon receipt of a PROCSIG_NOTIFY_INTERRUPT signal, the signal handler
- *	  sets the process's latch, which triggers the event to be processed
+ *	  raises INTERRUPT_GENERAL_WAKEUP, which triggers the event to be processed
  *	  immediately if this backend is idle (i.e., it is waiting for a frontend
  *	  command and is not within a transaction block. C.f.
  *	  ProcessClientReadInterrupt()).  Otherwise the handler may only set a
@@ -140,6 +140,7 @@
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
 #include "storage/procsignal.h"
@@ -406,9 +407,9 @@ 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.
+ * notifyInterruptPending flag and raises the INTERRUPT_GENERAL_WAKEUP
+ * interrupt.  ProcessNotifyInterrupt() will then be called whenever it's safe
+ * to actually deal with the interrupt.
  */
 volatile sig_atomic_t notifyInterruptPending = false;
 
@@ -1812,7 +1813,7 @@ HandleNotifyInterrupt(void)
 	notifyInterruptPending = true;
 
 	/* make sure the event is processed in due course */
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
@@ -1822,7 +1823,7 @@ HandleNotifyInterrupt(void)
  *		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.
+ *		with INTERRUPT_GENERAL_WAKEUP, and this routine will get called.
  *		If we are truly idle (ie, *not* inside a transaction block),
  *		process the incoming notifies.
  *
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 86f36b36954..4805388687d 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2411,8 +2411,8 @@ vacuum_delay_point(void)
 		/*
 		 * We don't want to ignore postmaster death during very long vacuums
 		 * with vacuum_cost_delay configured.  We can't use the usual
-		 * WaitLatch() approach here because we want microsecond-based sleep
-		 * durations above.
+		 * WaitInterrupt() approach here because we want microsecond-based
+		 * sleep durations above.
 		 */
 		if (IsUnderPostmaster && !PostmasterIsAlive())
 			exit(1);
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index ca0f54d676f..314e3d8fbb2 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -61,9 +61,8 @@
 #include "executor/execPartition.h"
 #include "executor/executor.h"
 #include "executor/nodeAppend.h"
-#include "miscadmin.h"
 #include "pgstat.h"
-#include "storage/latch.h"
+#include "storage/waiteventset.h"
 
 /* Shared state for parallel-aware Append. */
 struct ParallelAppendState
@@ -1028,7 +1027,7 @@ ExecAppendAsyncEventWait(AppendState *node)
 	Assert(node->as_eventset == NULL);
 	node->as_eventset = CreateWaitEventSet(CurrentResourceOwner, nevents);
 	AddWaitEventToSet(node->as_eventset, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET,
-					  NULL, NULL);
+					  0, NULL);
 
 	/* Give each waiting subplan a chance to add an event. */
 	i = -1;
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 7f7edc7f9fc..fbbc396cb40 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -36,6 +36,7 @@
 #include "executor/tqueue.h"
 #include "miscadmin.h"
 #include "optimizer/optimizer.h"
+#include "storage/interrupt.h"
 #include "utils/wait_event.h"
 
 
@@ -382,9 +383,10 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
-							 WAIT_EVENT_EXECUTE_GATHER);
-			ResetLatch(MyLatch);
+			(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+								 WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+								 WAIT_EVENT_EXECUTE_GATHER);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			nvisited = 0;
 		}
 	}
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 47e8c916060..ef7cba65e42 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -1663,8 +1663,9 @@ interpret_ident_response(const char *ident_response,
  *	owns the tcp connection to "local_addr"
  *	If the username is successfully retrieved, check the usermap.
  *
- *	XXX: Using WaitLatchOrSocket() and doing a CHECK_FOR_INTERRUPTS() if the
- *	latch was set would improve the responsiveness to timeouts/cancellations.
+ *	XXX: Using WaitInterruptOrSocket() and doing a CHECK_FOR_INTERRUPTS()
+ *	if the interrupt was pending would improve the responsiveness to
+ *	timeouts/cancellations.
  */
 static int
 ident_inet(hbaPort *port)
@@ -3098,8 +3099,8 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 	 * packets to our port thus causing us to retry in a loop and never time
 	 * out.
 	 *
-	 * XXX: Using WaitLatchOrSocket() and doing a CHECK_FOR_INTERRUPTS() if
-	 * the latch was set would improve the responsiveness to
+	 * XXX: Using WaitInterruptOrSocket() and doing a CHECK_FOR_INTERRUPTS()
+	 * if the interrupt was pending would improve the responsiveness to
 	 * timeouts/cancellations.
 	 */
 	gettimeofday(&endtime, NULL);
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 5a009776d12..2382ea08a8f 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -21,6 +21,7 @@
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/pg_bswap.h"
+#include "storage/interrupt.h"
 #include "utils/injection_point.h"
 #include "utils/memutils.h"
 
@@ -414,7 +415,7 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
 
 /*
  * Read the specified number of bytes off the wire, waiting using
- * WaitLatchOrSocket if we would block.
+ * WaitInterruptOrSocket if we would block.
  *
  * Results are read into PqGSSRecvBuffer.
  *
@@ -450,9 +451,8 @@ read_or_wait(Port *port, ssize_t len)
 		 */
 		if (ret <= 0)
 		{
-			WaitLatchOrSocket(NULL,
-							  WL_SOCKET_READABLE | WL_EXIT_ON_PM_DEATH,
-							  port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
+			WaitInterruptOrSocket(0, WL_SOCKET_READABLE | WL_EXIT_ON_PM_DEATH,
+								  port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
 
 			/*
 			 * If we got back zero bytes, and then waited on the socket to be
@@ -489,7 +489,7 @@ read_or_wait(Port *port, ssize_t len)
  *
  * Note that unlike the be_gssapi_read/be_gssapi_write functions, this
  * function WILL block on the socket to be ready for read/write (using
- * WaitLatchOrSocket) as appropriate while establishing the GSSAPI
+ * WaitInterruptOrSocket) as appropriate while establishing the GSSAPI
  * session.
  */
 ssize_t
@@ -668,9 +668,8 @@ secure_open_gssapi(Port *port)
 				/* Wait and retry if we couldn't write yet */
 				if (ret <= 0)
 				{
-					WaitLatchOrSocket(NULL,
-									  WL_SOCKET_WRITEABLE | WL_EXIT_ON_PM_DEATH,
-									  port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
+					WaitInterruptOrSocket(0, WL_SOCKET_WRITEABLE | WL_EXIT_ON_PM_DEATH,
+										  port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
 					continue;
 				}
 
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 91a86d62a35..cdab6b0f595 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -32,7 +32,8 @@
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "storage/fd.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
+#include "utils/builtins.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 
@@ -522,8 +523,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE | WL_EXIT_ON_PM_DEATH;
 
-				(void) WaitLatchOrSocket(NULL, waitfor, port->sock, 0,
-										 WAIT_EVENT_SSL_OPEN_SERVER);
+				(void) WaitInterruptOrSocket(0, waitfor, port->sock, 0,
+											 WAIT_EVENT_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0 && errno != 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 2139f81f241..6ecca6a0dac 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -28,7 +28,7 @@
 #include <arpa/inet.h>
 
 #include "libpq/libpq.h"
-#include "miscadmin.h"
+#include "storage/interrupt.h"
 #include "tcop/tcopprot.h"
 #include "utils/injection_point.h"
 #include "utils/wait_event.h"
@@ -213,7 +213,7 @@ retry:
 
 		Assert(waitfor);
 
-		ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, waitfor, NULL);
+		ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, waitfor, 0);
 
 		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
 						 WAIT_EVENT_CLIENT_READ);
@@ -228,12 +228,12 @@ retry:
 		 * new connections can be accepted.  Exiting clears the deck for a
 		 * postmaster restart.
 		 *
-		 * (Note that we only make this check when we would otherwise sleep on
-		 * our latch.  We might still continue running for a while if the
-		 * postmaster is killed in mid-query, or even through multiple queries
-		 * if we never have to wait for read.  We don't want to burn too many
-		 * cycles checking for this very rare condition, and this should cause
-		 * us to exit quickly in most cases.)
+		 * (Note that we only make this check when we would otherwise sleep
+		 * waiting for interrupt.  We might still continue running for a while
+		 * if the postmaster is killed in mid-query, or even through multiple
+		 * queries if we never have to wait for read.  We don't want to burn
+		 * too many cycles checking for this very rare condition, and this
+		 * should cause us to exit quickly in most cases.)
 		 */
 		if (event.events & WL_POSTMASTER_DEATH)
 			ereport(FATAL,
@@ -241,9 +241,9 @@ retry:
 					 errmsg("terminating connection due to unexpected postmaster exit")));
 
 		/* Handle interrupt. */
-		if (event.events & WL_LATCH_SET)
+		if (event.events & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			ProcessClientReadInterrupt(true);
 
 			/*
@@ -284,7 +284,8 @@ secure_raw_read(Port *port, void *ptr, size_t len)
 
 	/*
 	 * Try to read from the socket without blocking. If it succeeds we're
-	 * done, otherwise we'll wait for the socket using the latch mechanism.
+	 * done, otherwise we'll wait for the socket using the interrupt
+	 * mechanism.
 	 */
 #ifdef WIN32
 	pgwin32_noblock = true;
@@ -338,7 +339,7 @@ retry:
 
 		Assert(waitfor);
 
-		ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, waitfor, NULL);
+		ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, waitfor, 0);
 
 		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
 						 WAIT_EVENT_CLIENT_WRITE);
@@ -350,9 +351,9 @@ retry:
 					 errmsg("terminating connection due to unexpected postmaster exit")));
 
 		/* Handle interrupt. */
-		if (event.events & WL_LATCH_SET)
+		if (event.events & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			ProcessClientWriteInterrupt(true);
 
 			/*
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 896e1476b50..3b7bc99ca72 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -77,6 +77,7 @@
 #include "miscadmin.h"
 #include "port/pg_bswap.h"
 #include "postmaster/postmaster.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "utils/guc_hooks.h"
 #include "utils/memutils.h"
@@ -175,7 +176,7 @@ pq_init(ClientSocket *client_sock)
 {
 	Port	   *port;
 	int			socket_pos PG_USED_FOR_ASSERTS_ONLY;
-	int			latch_pos PG_USED_FOR_ASSERTS_ONLY;
+	int			interrupt_pos PG_USED_FOR_ASSERTS_ONLY;
 
 	/* allocate the Port struct and copy the ClientSocket contents to it */
 	port = palloc0(sizeof(Port));
@@ -287,8 +288,8 @@ pq_init(ClientSocket *client_sock)
 
 	/*
 	 * In backends (as soon as forked) we operate the underlying socket in
-	 * nonblocking mode and use latches to implement blocking semantics if
-	 * needed. That allows us to provide safely interruptible reads and
+	 * nonblocking mode and use WaitEventSet to implement blocking semantics
+	 * if needed. That allows us to provide safely interruptible reads and
 	 * writes.
 	 */
 #ifndef WIN32
@@ -306,18 +307,18 @@ pq_init(ClientSocket *client_sock)
 
 	FeBeWaitSet = CreateWaitEventSet(NULL, FeBeWaitSetNEvents);
 	socket_pos = AddWaitEventToSet(FeBeWaitSet, WL_SOCKET_WRITEABLE,
-								   port->sock, NULL, NULL);
-	latch_pos = AddWaitEventToSet(FeBeWaitSet, WL_LATCH_SET, PGINVALID_SOCKET,
-								  MyLatch, NULL);
+								   port->sock, 0, NULL);
+	interrupt_pos = AddWaitEventToSet(FeBeWaitSet, WL_INTERRUPT, PGINVALID_SOCKET,
+									  1 << INTERRUPT_GENERAL_WAKEUP, NULL);
 	AddWaitEventToSet(FeBeWaitSet, WL_POSTMASTER_DEATH, PGINVALID_SOCKET,
-					  NULL, NULL);
+					  0, NULL);
 
 	/*
 	 * The event positions match the order we added them, but let's sanity
 	 * check them to be sure.
 	 */
 	Assert(socket_pos == FeBeWaitSetSocketPos);
-	Assert(latch_pos == FeBeWaitSetLatchPos);
+	Assert(interrupt_pos == FeBeWaitSetInterruptPos);
 
 	return port;
 }
@@ -2060,7 +2061,7 @@ pq_check_connection(void)
 	 * It's OK to modify the socket event filter without restoring, because
 	 * all FeBeWaitSet socket wait sites do the same.
 	 */
-	ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, WL_SOCKET_CLOSED, NULL);
+	ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, WL_SOCKET_CLOSED, 0);
 
 retry:
 	rc = WaitEventSetWait(FeBeWaitSet, 0, events, lengthof(events), 0);
@@ -2068,15 +2069,15 @@ retry:
 	{
 		if (events[i].events & WL_SOCKET_CLOSED)
 			return false;
-		if (events[i].events & WL_LATCH_SET)
+		if (events[i].events & WL_INTERRUPT)
 		{
 			/*
-			 * A latch event might be preventing other events from being
+			 * An interrupt event might be preventing other events from being
 			 * reported.  Reset it and poll again.  No need to restore it
-			 * because no code should expect latches to survive across
-			 * CHECK_FOR_INTERRUPTS().
+			 * because no code should expect INTERRUPT_GENERAL_WAKEUP to
+			 * survive across CHECK_FOR_INTERRUPTS().
 			 */
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			goto retry;
 		}
 	}
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index fd735e2fea9..9b299e21400 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -20,6 +20,7 @@
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "replication/logicalworker.h"
+#include "storage/interrupt.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 
@@ -181,9 +182,10 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
-						 WAIT_EVENT_MESSAGE_QUEUE_PUT_MESSAGE);
-		ResetLatch(MyLatch);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+							 WAIT_EVENT_MESSAGE_QUEUE_PUT_MESSAGE);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 		CHECK_FOR_INTERRUPTS();
 	}
 
diff --git a/src/backend/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c
index 22a16c50b21..2f5eebdfec4 100644
--- a/src/backend/libpq/pqsignal.c
+++ b/src/backend/libpq/pqsignal.c
@@ -42,7 +42,7 @@ pqinitmask(void)
 {
 	sigemptyset(&UnBlockSig);
 
-	/* Note: InitializeLatchSupport() modifies UnBlockSig. */
+	/* Note: InitializeWaitEventSupport() modifies UnBlockSig. */
 
 	/* First set all signals, then clear some. */
 	sigfillset(&BlockSig);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index dc3cf87abab..9ef4a4ccd11 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -89,8 +89,8 @@
 #include "postmaster/interrupt.h"
 #include "postmaster/postmaster.h"
 #include "storage/bufmgr.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/lmgr.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
@@ -571,10 +571,10 @@ AutoVacLauncherMain(char *startup_data, size_t startup_data_len)
 		bool		can_launch;
 
 		/*
-		 * This loop is a bit different from the normal use of WaitLatch,
+		 * This loop is a bit different from the normal use of WaitInterrupt,
 		 * because we'd like to sleep before the first launch of a child
-		 * process.  So it's WaitLatch, then ResetLatch, then check for
-		 * wakening conditions.
+		 * process.  So it's WaitInterrupt, then ClearInterrupt, then check
+		 * for wakening conditions.
 		 */
 
 		launcher_determine_sleep(!dlist_is_empty(&AutoVacuumShmem->av_freeWorkers),
@@ -582,14 +582,14 @@ AutoVacLauncherMain(char *startup_data, size_t startup_data_len)
 
 		/*
 		 * Wait until naptime expires or we get some type of signal (all the
-		 * signal handlers will wake us by calling SetLatch).
+		 * signal handlers will wake us by calling RaiseInterrupt).
 		 */
-		(void) WaitLatch(MyLatch,
-						 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-						 (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
-						 WAIT_EVENT_AUTOVACUUM_MAIN);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+							 WAIT_EVENT_AUTOVACUUM_MAIN);
 
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		HandleAutoVacLauncherInterrupts();
 
@@ -1346,7 +1346,7 @@ static void
 avl_sigusr2_handler(SIGNAL_ARGS)
 {
 	got_SIGUSR2 = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 
diff --git a/src/backend/postmaster/auxprocess.c b/src/backend/postmaster/auxprocess.c
index d19174bda39..5cc056cd5aa 100644
--- a/src/backend/postmaster/auxprocess.c
+++ b/src/backend/postmaster/auxprocess.c
@@ -23,6 +23,7 @@
 #include "storage/proc.h"
 #include "storage/procsignal.h"
 #include "utils/memutils.h"
+#include "utils/resowner.h"
 #include "utils/ps_status.h"
 
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 07bc5517fc2..9ed18f8407a 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -21,8 +21,8 @@
 #include "postmaster/postmaster.h"
 #include "replication/logicallauncher.h"
 #include "replication/logicalworker.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
@@ -1226,9 +1226,9 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 		if (status != BGWH_NOT_YET_STARTED)
 			break;
 
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
-					   WAIT_EVENT_BGWORKER_STARTUP);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_POSTMASTER_DEATH, 0,
+						   WAIT_EVENT_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1236,7 +1236,7 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 		}
 
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 
 	return status;
@@ -1269,9 +1269,9 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 		if (status == BGWH_STOPPED)
 			break;
 
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
-					   WAIT_EVENT_BGWORKER_SHUTDOWN);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_POSTMASTER_DEATH, 0,
+						   WAIT_EVENT_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1279,7 +1279,7 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 		}
 
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 
 	return status;
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 0f75548759a..c59480565ac 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -42,6 +42,7 @@
 #include "storage/bufmgr.h"
 #include "storage/condition_variable.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/lwlock.h"
 #include "storage/proc.h"
 #include "storage/procsignal.h"
@@ -224,7 +225,7 @@ BackgroundWriterMain(char *startup_data, size_t startup_data_len)
 		int			rc;
 
 		/* Clear any already-pending wakeups */
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		HandleMainLoopInterrupts();
 
@@ -299,22 +300,22 @@ BackgroundWriterMain(char *startup_data, size_t startup_data_len)
 		 * will call it every BgWriterDelay msec.  While it's not critical for
 		 * correctness that that be exact, the feedback loop might misbehave
 		 * if we stray too far from that.  Hence, avoid loading this process
-		 * down with latch events that are likely to happen frequently during
-		 * normal operation.
+		 * down with interrupt events that are likely to happen frequently
+		 * during normal operation.
 		 */
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-					   BgWriterDelay /* ms */ , WAIT_EVENT_BGWRITER_MAIN);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						   BgWriterDelay /* ms */ , WAIT_EVENT_BGWRITER_MAIN);
 
 		/*
-		 * If no latch event and BgBufferSync says nothing's happening, extend
-		 * the sleep in "hibernation" mode, where we sleep for much longer
-		 * than bgwriter_delay says.  Fewer wakeups save electricity.  When a
-		 * backend starts using buffers again, it will wake us up by setting
-		 * our latch.  Because the extra sleep will persist only as long as no
-		 * buffer allocations happen, this should not distort the behavior of
-		 * BgBufferSync's control loop too badly; essentially, it will think
-		 * that the system-wide idle interval didn't exist.
+		 * If no interrupt event and BgBufferSync says nothing's happening,
+		 * extend the sleep in "hibernation" mode, where we sleep for much
+		 * longer than bgwriter_delay says.  Fewer wakeups save electricity.
+		 * When a backend starts using buffers again, it will wake us up by
+		 * sending us an interrupt.  Because the extra sleep will persist only
+		 * as long as no buffer allocations happen, this should not distort
+		 * the behavior of BgBufferSync's control loop too badly; essentially,
+		 * it will think that the system-wide idle interval didn't exist.
 		 *
 		 * There is a race condition here, in that a backend might allocate a
 		 * buffer between the time BgBufferSync saw the alloc count as zero
@@ -329,10 +330,10 @@ BackgroundWriterMain(char *startup_data, size_t startup_data_len)
 			/* Ask for notification at next buffer allocation */
 			StrategyNotifyBgWriter(MyProcNumber);
 			/* Sleep ... */
-			(void) WaitLatch(MyLatch,
-							 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-							 BgWriterDelay * HIBERNATE_FACTOR,
-							 WAIT_EVENT_BGWRITER_HIBERNATE);
+			(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+								 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+								 BgWriterDelay * HIBERNATE_FACTOR,
+								 WAIT_EVENT_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 982572a75db..1ae4841ad2d 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -49,6 +49,7 @@
 #include "storage/bufmgr.h"
 #include "storage/condition_variable.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/lwlock.h"
 #include "storage/proc.h"
@@ -343,7 +344,7 @@ CheckpointerMain(char *startup_data, size_t startup_data_len)
 		bool		chkpt_or_rstpt_timed = false;
 
 		/* Clear any already-pending wakeups */
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/*
 		 * Process any requests or signals received recently.
@@ -555,10 +556,10 @@ CheckpointerMain(char *startup_data, size_t startup_data_len)
 			cur_timeout = Min(cur_timeout, XLogArchiveTimeout - elapsed_secs);
 		}
 
-		(void) WaitLatch(MyLatch,
-						 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-						 cur_timeout * 1000L /* convert to ms */ ,
-						 WAIT_EVENT_CHECKPOINTER_MAIN);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 cur_timeout * 1000L /* convert to ms */ ,
+							 WAIT_EVENT_CHECKPOINTER_MAIN);
 	}
 }
 
@@ -758,10 +759,11 @@ CheckpointWriteDelay(int flags, double progress)
 		 * Checkpointer and bgwriter are no longer related so take the Big
 		 * Sleep.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH | WL_TIMEOUT,
-				  100,
-				  WAIT_EVENT_CHECKPOINT_WRITE_DELAY);
-		ResetLatch(MyLatch);
+		WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+					  WL_INTERRUPT | WL_EXIT_ON_PM_DEATH | WL_TIMEOUT,
+					  100,
+					  WAIT_EVENT_CHECKPOINT_WRITE_DELAY);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 	else if (--absorb_counter <= 0)
 	{
@@ -873,7 +875,7 @@ ReqCheckpointHandler(SIGNAL_ARGS)
 	 * The signaling process should have set ckpt_flags nonzero, so all we
 	 * need do is ensure that our main loop gets kicked out of any wait.
 	 */
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 
@@ -1145,7 +1147,7 @@ ForwardSyncRequest(const FileTag *ftag, SyncRequestType type)
 		ProcNumber	checkpointerProc = procglobal->checkpointerProc;
 
 		if (checkpointerProc != INVALID_PROC_NUMBER)
-			SetLatch(&GetPGProcByNumber(checkpointerProc)->procLatch);
+			SendInterrupt(INTERRUPT_GENERAL_WAKEUP, ProcGlobal->checkpointerProc);
 	}
 
 	return true;
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index eedc0980cf1..8cbde0698c1 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -18,8 +18,8 @@
 
 #include "miscadmin.h"
 #include "postmaster/interrupt.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/procsignal.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
@@ -61,7 +61,7 @@ void
 SignalHandlerForConfigReload(SIGNAL_ARGS)
 {
 	ConfigReloadPending = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
@@ -105,5 +105,5 @@ void
 SignalHandlerForShutdownRequest(SIGNAL_ARGS)
 {
 	ShutdownRequestPending = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 02f91431f5f..01405bf2a07 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -41,8 +41,8 @@
 #include "postmaster/pgarch.h"
 #include "storage/condition_variable.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procsignal.h"
@@ -247,8 +247,8 @@ PgArchiverMain(char *startup_data, size_t startup_data_len)
 	on_shmem_exit(pgarch_die, 0);
 
 	/*
-	 * Advertise our proc number so that backends can use our latch to wake us
-	 * up while we're sleeping.
+	 * Advertise our proc number so that backends can wake us up while we're
+	 * sleeping.
 	 */
 	PgArch->pgprocno = MyProcNumber;
 
@@ -282,13 +282,12 @@ PgArchWakeup(void)
 	int			arch_pgprocno = PgArch->pgprocno;
 
 	/*
-	 * We don't acquire ProcArrayLock here.  It's actually fine because
-	 * procLatch isn't ever freed, so we just can potentially set the wrong
-	 * process' (or no process') latch.  Even in that case the archiver will
-	 * be relaunched shortly and will start archiving.
+	 * We don't acquire ProcArrayLock here, so we may send the interrupt to
+	 * wrong process, but that's harmless.  Even in that case the archiver
+	 * will be relaunched shortly and will start archiving.
 	 */
 	if (arch_pgprocno != INVALID_PROC_NUMBER)
-		SetLatch(&ProcGlobal->allProcs[arch_pgprocno].procLatch);
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, arch_pgprocno);
 }
 
 
@@ -298,7 +297,7 @@ pgarch_waken_stop(SIGNAL_ARGS)
 {
 	/* set flag to do a final cycle and shut down afterwards */
 	ready_to_stop = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
@@ -318,7 +317,7 @@ pgarch_MainLoop(void)
 	 */
 	do
 	{
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/* When we get SIGUSR2, we do one more archive cycle, then exit */
 		time_to_stop = ready_to_stop;
@@ -355,10 +354,10 @@ pgarch_MainLoop(void)
 		{
 			int			rc;
 
-			rc = WaitLatch(MyLatch,
-						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   PGARCH_AUTOWAKE_INTERVAL * 1000L,
-						   WAIT_EVENT_ARCHIVER_MAIN);
+			rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							   WL_INTERRUPT | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+							   PGARCH_AUTOWAKE_INTERVAL * 1000L,
+							   WAIT_EVENT_ARCHIVER_MAIN);
 			if (rc & WL_POSTMASTER_DEATH)
 				time_to_stop = true;
 		}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 78e66a06ac5..687c8e7672c 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -109,6 +109,7 @@
 #include "replication/slotsync.h"
 #include "replication/walsender.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/pmsignal.h"
 #include "tcop/backend_startup.h"
@@ -535,8 +536,7 @@ PostmasterMain(int argc, char *argv[])
 	pqsignal(SIGCHLD, handle_pm_child_exit_signal);
 
 	/* This may configure SIGURG, depending on platform. */
-	InitializeLatchSupport();
-	InitProcessLocalLatch();
+	InitializeWaitEventSupport();
 
 	/*
 	 * No other place in Postgres should touch SIGTTIN/SIGTTOU handling.  We
@@ -1595,14 +1595,14 @@ ConfigurePostmasterWaitSet(bool accept_connections)
 
 	pm_wait_set = CreateWaitEventSet(NULL,
 									 accept_connections ? (1 + NumListenSockets) : 1);
-	AddWaitEventToSet(pm_wait_set, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch,
-					  NULL);
+	AddWaitEventToSet(pm_wait_set, WL_INTERRUPT, PGINVALID_SOCKET,
+					  1 << INTERRUPT_GENERAL_WAKEUP, NULL);
 
 	if (accept_connections)
 	{
 		for (int i = 0; i < NumListenSockets; i++)
 			AddWaitEventToSet(pm_wait_set, WL_SOCKET_ACCEPT, ListenSockets[i],
-							  NULL, NULL);
+							  0, NULL);
 	}
 }
 
@@ -1631,19 +1631,20 @@ ServerLoop(void)
 								   0 /* postmaster posts no wait_events */ );
 
 		/*
-		 * Latch set by signal handler, or new connection pending on any of
-		 * our sockets? If the latter, fork a child process to deal with it.
+		 * Interrupt raised by signal handler, or new connection pending on
+		 * any of our sockets?  If the latter, fork a child process to deal
+		 * with it.
 		 */
 		for (int i = 0; i < nevents; i++)
 		{
-			if (events[i].events & WL_LATCH_SET)
-				ResetLatch(MyLatch);
+			if (events[i].events & WL_INTERRUPT)
+				ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 			/*
 			 * The following requests are handled unconditionally, even if we
-			 * didn't see WL_LATCH_SET.  This gives high priority to shutdown
-			 * and reload requests where the latch happens to appear later in
-			 * events[] or will be reported by a later call to
+			 * didn't see WL_INTERRUPT.  This gives high priority to shutdown
+			 * and reload requests where the interrupt event happens to appear
+			 * later in events[] or will be reported by a later call to
 			 * WaitEventSetWait().
 			 */
 			if (pending_pm_shutdown_request)
@@ -1937,7 +1938,7 @@ static void
 handle_pm_pmsignal_signal(SIGNAL_ARGS)
 {
 	pending_pm_pmsignal = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
@@ -1947,7 +1948,7 @@ static void
 handle_pm_reload_request_signal(SIGNAL_ARGS)
 {
 	pending_pm_reload_request = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
@@ -2024,7 +2025,7 @@ handle_pm_shutdown_request_signal(SIGNAL_ARGS)
 			pending_pm_shutdown_request = true;
 			break;
 	}
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
@@ -2185,7 +2186,7 @@ static void
 handle_pm_child_exit_signal(SIGNAL_ARGS)
 {
 	pending_pm_child_exit = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index ef6f98ebcd7..2020f170d00 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -28,6 +28,7 @@
 #include "postmaster/startup.h"
 #include "storage/ipc.h"
 #include "storage/pmsignal.h"
+#include "storage/proc.h"
 #include "storage/procsignal.h"
 #include "storage/standby.h"
 #include "utils/guc.h"
@@ -40,7 +41,7 @@
  * On systems that need to make a system call to find out if the postmaster has
  * gone away, we'll do so only every Nth call to HandleStartupProcInterrupts().
  * This only affects how long it takes us to detect the condition while we're
- * busy replaying WAL.  Latch waits and similar which should react immediately
+ * busy replaying WAL.  Interrupt waits and similar should react immediately
  * through the usual techniques.
  */
 #define POSTMASTER_POLL_RATE_LIMIT 1024
@@ -205,6 +206,8 @@ StartupProcExit(int code, Datum arg)
 	/* Shutdown the recovery environment */
 	if (standbyState != STANDBY_DISABLED)
 		ShutdownRecoveryTransactionEnvironment();
+
+	ProcGlobal->startupProc = INVALID_PROC_NUMBER;
 }
 
 
@@ -220,6 +223,12 @@ StartupProcessMain(char *startup_data, size_t startup_data_len)
 	MyBackendType = B_STARTUP;
 	AuxiliaryProcessMainCommon();
 
+	/*
+	 * Advertise our proc number so that backends can wake us up, when the
+	 * server is promoted or recovery is paused/resumed.
+	 */
+	ProcGlobal->startupProc = MyProcNumber;
+
 	/* Arrange to clean up at startup process exit */
 	on_shmem_exit(StartupProcExit, 0);
 
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index f12639056f2..2ea271344bd 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -44,8 +44,8 @@
 #include "postmaster/syslogger.h"
 #include "storage/dsm.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/pg_shmem.h"
 #include "tcop/tcopprot.h"
 #include "utils/guc.h"
@@ -328,7 +328,7 @@ SysLoggerMain(char *startup_data, size_t startup_data_len)
 	whereToSendOutput = DestNone;
 
 	/*
-	 * Set up a reusable WaitEventSet object we'll use to wait for our latch,
+	 * Set up a reusable WaitEventSet object we'll use to wait for interrupts,
 	 * and (except on Windows) our socket.
 	 *
 	 * Unlike all other postmaster child processes, we'll ignore postmaster
@@ -338,9 +338,9 @@ SysLoggerMain(char *startup_data, size_t startup_data_len)
 	 * (including the postmaster).
 	 */
 	wes = CreateWaitEventSet(NULL, 2);
-	AddWaitEventToSet(wes, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch, NULL);
+	AddWaitEventToSet(wes, WL_INTERRUPT, PGINVALID_SOCKET, 1 << INTERRUPT_GENERAL_WAKEUP, NULL);
 #ifndef WIN32
-	AddWaitEventToSet(wes, WL_SOCKET_READABLE, syslogPipe[0], NULL, NULL);
+	AddWaitEventToSet(wes, WL_SOCKET_READABLE, syslogPipe[0], 0, NULL);
 #endif
 
 	/* main worker loop */
@@ -356,7 +356,7 @@ SysLoggerMain(char *startup_data, size_t startup_data_len)
 #endif
 
 		/* Clear any already-pending wakeups */
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/*
 		 * Process any requests or signals received recently.
@@ -1186,7 +1186,7 @@ pipeThread(void *arg)
 			if (ftell(syslogFile) >= Log_RotationSize * 1024L ||
 				(csvlogFile != NULL && ftell(csvlogFile) >= Log_RotationSize * 1024L) ||
 				(jsonlogFile != NULL && ftell(jsonlogFile) >= Log_RotationSize * 1024L))
-				SetLatch(MyLatch);
+				RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 		}
 		LeaveCriticalSection(&sysloggerSection);
 	}
@@ -1197,8 +1197,8 @@ pipeThread(void *arg)
 	/* if there's any data left then force it out now */
 	flush_pipe_input(logbuffer, &bytes_in_logbuffer);
 
-	/* set the latch to waken the main thread, which will quit */
-	SetLatch(MyLatch);
+	/* raise the interrupt to waken the main thread, which will quit */
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 	LeaveCriticalSection(&sysloggerSection);
 	_endthread();
@@ -1593,5 +1593,5 @@ static void
 sigUsr1Handler(SIGNAL_ARGS)
 {
 	rotation_requested = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c
index 48350bec524..f78f011c2b1 100644
--- a/src/backend/postmaster/walsummarizer.c
+++ b/src/backend/postmaster/walsummarizer.c
@@ -38,8 +38,8 @@
 #include "postmaster/walsummarizer.h"
 #include "replication/walreceiver.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/proc.h"
 #include "storage/procsignal.h"
@@ -315,10 +315,8 @@ WalSummarizerMain(char *startup_data, size_t startup_data_len)
 		 * So a really fast retry time doesn't seem to be especially
 		 * beneficial, and it will clutter the logs.
 		 */
-		(void) WaitLatch(NULL,
-						 WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-						 10000,
-						 WAIT_EVENT_WAL_SUMMARIZER_ERROR);
+		(void) WaitInterrupt(0, WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10000,
+							 WAIT_EVENT_WAL_SUMMARIZER_ERROR);
 	}
 
 	/* We can now handle ereport(ERROR) */
@@ -630,8 +628,8 @@ GetOldestUnsummarizedLSN(TimeLineID *tli, bool *lsn_is_exact)
  *
  * This might not work, because there's no guarantee that the WAL summarizer
  * process was successfully started, and it also might have started but
- * subsequently terminated. So, under normal circumstances, this will get the
- * latch set, but there's no guarantee.
+ * subsequently terminated. So, under normal circumstances, this will send
+ * the interrupt, but there's no guarantee.
  */
 void
 WakeupWalSummarizer(void)
@@ -646,7 +644,7 @@ WakeupWalSummarizer(void)
 	LWLockRelease(WALSummarizerLock);
 
 	if (pgprocno != INVALID_PROC_NUMBER)
-		SetLatch(&ProcGlobal->allProcs[pgprocno].procLatch);
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, pgprocno);
 }
 
 /*
@@ -1637,11 +1635,11 @@ summarizer_wait_for_wal(void)
 	}
 
 	/* OK, now sleep. */
-	(void) WaitLatch(MyLatch,
-					 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-					 sleep_quanta * MS_PER_SLEEP_QUANTUM,
-					 WAIT_EVENT_WAL_SUMMARIZER_WAL);
-	ResetLatch(MyLatch);
+	(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						 sleep_quanta * MS_PER_SLEEP_QUANTUM,
+						 WAIT_EVENT_WAL_SUMMARIZER_WAL);
+	ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 	/* Reset count of pages read. */
 	pages_read_since_last_sleep = 0;
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 5a3cb894652..fabcf3bb7f8 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -54,6 +54,7 @@
 #include "storage/bufmgr.h"
 #include "storage/condition_variable.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/lwlock.h"
 #include "storage/proc.h"
 #include "storage/procsignal.h"
@@ -222,12 +223,12 @@ WalWriterMain(char *startup_data, size_t startup_data_len)
 
 		/*
 		 * Advertise whether we might hibernate in this cycle.  We do this
-		 * before resetting the latch to ensure that any async commits will
-		 * see the flag set if they might possibly need to wake us up, and
-		 * that we won't miss any signal they send us.  (If we discover work
-		 * to do in the last cycle before we would hibernate, the global flag
-		 * will be set unnecessarily, but little harm is done.)  But avoid
-		 * touching the global flag if it doesn't need to change.
+		 * before clearing the interrupt flag to ensure that any async commits
+		 * will see the flag set if they might possibly need to wake us up,
+		 * and that we won't miss any signal they send us.  (If we discover
+		 * work to do in the last cycle before we would hibernate, the global
+		 * flag will be set unnecessarily, but little harm is done.)  But
+		 * avoid touching the global flag if it doesn't need to change.
 		 */
 		if (hibernating != (left_till_hibernate <= 1))
 		{
@@ -236,7 +237,7 @@ WalWriterMain(char *startup_data, size_t startup_data_len)
 		}
 
 		/* Clear any already-pending wakeups */
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/* Process any signals received recently */
 		HandleMainLoopInterrupts();
@@ -263,9 +264,9 @@ WalWriterMain(char *startup_data, size_t startup_data_len)
 		else
 			cur_timeout = WalWriterDelay * HIBERNATE_FACTOR;
 
-		(void) WaitLatch(MyLatch,
-						 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-						 cur_timeout,
-						 WAIT_EVENT_WAL_WRITER_MAIN);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 cur_timeout,
+							 WAIT_EVENT_WAL_WRITER_MAIN);
 	}
 }
diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
index c74369953f8..2294d5d4371 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -30,7 +30,7 @@
 #include "pgstat.h"
 #include "pqexpbuffer.h"
 #include "replication/walreceiver.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
@@ -237,16 +237,16 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
 		else
 			io_flag = WL_SOCKET_WRITEABLE;
 
-		rc = WaitLatchOrSocket(MyLatch,
-							   WL_EXIT_ON_PM_DEATH | WL_LATCH_SET | io_flag,
-							   PQsocket(conn->streamConn),
-							   0,
-							   WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
+		rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL_WAKEUP,
+								   WL_EXIT_ON_PM_DEATH | WL_INTERRUPT | io_flag,
+								   PQsocket(conn->streamConn),
+								   0,
+								   WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
 
 		/* Interrupted? */
-		if (rc & WL_LATCH_SET)
+		if (rc & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			ProcessWalRcvInterrupts();
 		}
 
@@ -848,17 +848,17 @@ libpqrcv_PQgetResult(PGconn *streamConn)
 		 * since we'll get interrupted by signals and can handle any
 		 * interrupts here.
 		 */
-		rc = WaitLatchOrSocket(MyLatch,
-							   WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE |
-							   WL_LATCH_SET,
-							   PQsocket(streamConn),
-							   0,
-							   WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
+		rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL_WAKEUP,
+								   WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE |
+								   WL_INTERRUPT,
+								   PQsocket(streamConn),
+								   0,
+								   WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
 
 		/* Interrupted? */
-		if (rc & WL_LATCH_SET)
+		if (rc & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			ProcessWalRcvInterrupts();
 		}
 
diff --git a/src/backend/replication/logical/applyparallelworker.c b/src/backend/replication/logical/applyparallelworker.c
index e7f7d4c5e4b..ad9979c9b8c 100644
--- a/src/backend/replication/logical/applyparallelworker.c
+++ b/src/backend/replication/logical/applyparallelworker.c
@@ -165,6 +165,7 @@
 #include "replication/logicalworker.h"
 #include "replication/origin.h"
 #include "replication/worker_internal.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
 #include "tcop/tcopprot.h"
@@ -804,13 +805,13 @@ LogicalParallelApplyLoop(shm_mq_handle *mqh)
 				int			rc;
 
 				/* Wait for more work. */
-				rc = WaitLatch(MyLatch,
-							   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-							   1000L,
-							   WAIT_EVENT_LOGICAL_PARALLEL_APPLY_MAIN);
+				rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+								   WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+								   1000L,
+								   WAIT_EVENT_LOGICAL_PARALLEL_APPLY_MAIN);
 
-				if (rc & WL_LATCH_SET)
-					ResetLatch(MyLatch);
+				if (rc & WL_INTERRUPT)
+					ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			}
 		}
 		else
@@ -990,7 +991,7 @@ HandleParallelApplyMessageInterrupt(void)
 {
 	InterruptPending = true;
 	ParallelApplyMessagePending = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
@@ -1182,14 +1183,14 @@ pa_send_data(ParallelApplyWorkerInfo *winfo, Size nbytes, const void *data)
 		Assert(result == SHM_MQ_WOULD_BLOCK);
 
 		/* Wait before retrying. */
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-					   SHM_SEND_RETRY_INTERVAL_MS,
-					   WAIT_EVENT_LOGICAL_APPLY_SEND_DATA);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						   SHM_SEND_RETRY_INTERVAL_MS,
+						   WAIT_EVENT_LOGICAL_APPLY_SEND_DATA);
 
-		if (rc & WL_LATCH_SET)
+		if (rc & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			CHECK_FOR_INTERRUPTS();
 		}
 
@@ -1254,13 +1255,13 @@ pa_wait_for_xact_state(ParallelApplyWorkerInfo *winfo,
 			break;
 
 		/* Wait to be signalled. */
-		(void) WaitLatch(MyLatch,
-						 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-						 10L,
-						 WAIT_EVENT_LOGICAL_PARALLEL_APPLY_STATE_CHANGE);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 10L,
+							 WAIT_EVENT_LOGICAL_PARALLEL_APPLY_STATE_CHANGE);
 
-		/* Reset the latch so we don't spin. */
-		ResetLatch(MyLatch);
+		/* Clear the interrupt flag so we don't spin. */
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/* An interrupt may have occurred while we were waiting. */
 		CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index e5fdca8bbf6..1fb8790f213 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -34,6 +34,7 @@
 #include "replication/slot.h"
 #include "replication/walreceiver.h"
 #include "replication/worker_internal.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
@@ -218,16 +219,17 @@ WaitForReplicationWorkerAttach(LogicalRepWorker *worker,
 		}
 
 		/*
-		 * We need timeout because we generally don't get notified via latch
-		 * about the worker attach.  But we don't expect to have to wait long.
+		 * We need timeout because we generally don't get notified via an
+		 * interrupt about the worker attach.  But we don't expect to have to
+		 * wait long.
 		 */
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-					   10L, WAIT_EVENT_BGWORKER_STARTUP);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						   10L, WAIT_EVENT_BGWORKER_STARTUP);
 
-		if (rc & WL_LATCH_SET)
+		if (rc & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			CHECK_FOR_INTERRUPTS();
 		}
 	}
@@ -553,13 +555,13 @@ logicalrep_worker_stop_internal(LogicalRepWorker *worker, int signo)
 		LWLockRelease(LogicalRepWorkerLock);
 
 		/* Wait a bit --- we don't expect to have to wait long. */
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-					   10L, WAIT_EVENT_BGWORKER_STARTUP);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						   10L, WAIT_EVENT_BGWORKER_STARTUP);
 
-		if (rc & WL_LATCH_SET)
+		if (rc & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			CHECK_FOR_INTERRUPTS();
 		}
 
@@ -594,13 +596,13 @@ logicalrep_worker_stop_internal(LogicalRepWorker *worker, int signo)
 		LWLockRelease(LogicalRepWorkerLock);
 
 		/* Wait a bit --- we don't expect to have to wait long. */
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-					   10L, WAIT_EVENT_BGWORKER_SHUTDOWN);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						   10L, WAIT_EVENT_BGWORKER_SHUTDOWN);
 
-		if (rc & WL_LATCH_SET)
+		if (rc & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			CHECK_FOR_INTERRUPTS();
 		}
 
@@ -676,7 +678,7 @@ logicalrep_pa_worker_stop(ParallelApplyWorkerInfo *winfo)
 }
 
 /*
- * Wake up (using latch) any logical replication worker for specified sub/rel.
+ * Wake up (using interrupt) any logical replication worker for specified sub/rel.
  */
 void
 logicalrep_worker_wakeup(Oid subid, Oid relid)
@@ -694,7 +696,7 @@ logicalrep_worker_wakeup(Oid subid, Oid relid)
 }
 
 /*
- * Wake up (using latch) the specified logical replication worker.
+ * Wake up (using interrupt) the specified logical replication worker.
  *
  * Caller must hold lock, else worker->proc could change under us.
  */
@@ -703,7 +705,7 @@ logicalrep_worker_wakeup_ptr(LogicalRepWorker *worker)
 {
 	Assert(LWLockHeldByMe(LogicalRepWorkerLock));
 
-	SetLatch(&worker->proc->procLatch);
+	SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(worker->proc));
 }
 
 /*
@@ -1221,14 +1223,14 @@ ApplyLauncherMain(Datum main_arg)
 		MemoryContextDelete(subctx);
 
 		/* Wait for more work. */
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-					   wait_time,
-					   WAIT_EVENT_LOGICAL_LAUNCHER_MAIN);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						   wait_time,
+						   WAIT_EVENT_LOGICAL_LAUNCHER_MAIN);
 
-		if (rc & WL_LATCH_SET)
+		if (rc & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			CHECK_FOR_INTERRUPTS();
 		}
 
diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index d62186a5107..918cc667750 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -59,6 +59,7 @@
 #include "replication/logical.h"
 #include "replication/slotsync.h"
 #include "replication/snapbuild.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
 #include "storage/proc.h"
@@ -1252,13 +1253,13 @@ wait_for_slot_activity(bool some_slot_updated)
 		sleep_ms = MIN_SLOTSYNC_WORKER_NAPTIME_MS;
 	}
 
-	rc = WaitLatch(MyLatch,
-				   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-				   sleep_ms,
-				   WAIT_EVENT_REPLICATION_SLOTSYNC_MAIN);
+	rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+					   WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+					   sleep_ms,
+					   WAIT_EVENT_REPLICATION_SLOTSYNC_MAIN);
 
-	if (rc & WL_LATCH_SET)
-		ResetLatch(MyLatch);
+	if (rc & WL_INTERRUPT)
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
@@ -1590,13 +1591,13 @@ ShutDownSlotSync(void)
 		int			rc;
 
 		/* Wait a bit, we don't expect to have to wait long */
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-					   10L, WAIT_EVENT_REPLICATION_SLOTSYNC_SHUTDOWN);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						   10L, WAIT_EVENT_REPLICATION_SLOTSYNC_SHUTDOWN);
 
-		if (rc & WL_LATCH_SET)
+		if (rc & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			CHECK_FOR_INTERRUPTS();
 		}
 
diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c
index 118503fcb76..65c7f347599 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -111,6 +111,7 @@
 #include "replication/slot.h"
 #include "replication/walreceiver.h"
 #include "replication/worker_internal.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
 #include "utils/acl.h"
@@ -210,11 +211,11 @@ wait_for_relation_state_change(Oid relid, char expected_state)
 		if (!worker)
 			break;
 
-		(void) WaitLatch(MyLatch,
-						 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-						 1000L, WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 1000L, WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE);
 
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 
 	return false;
@@ -260,15 +261,15 @@ wait_for_worker_state_change(char expected_state)
 			break;
 
 		/*
-		 * Wait.  We expect to get a latch signal back from the apply worker,
+		 * Wait.  We expect to get an interrupt wakeup from the apply worker,
 		 * but use a timeout in case it dies without sending one.
 		 */
-		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-					   1000L, WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						   1000L, WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE);
 
-		if (rc & WL_LATCH_SET)
-			ResetLatch(MyLatch);
+		if (rc & WL_INTERRUPT)
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 
 	return false;
@@ -555,7 +556,7 @@ process_syncing_tables_for_apply(XLogRecPtr current_lsn)
 						 * the existing locks before entering a busy loop.
 						 * This is required to avoid any undetected deadlocks
 						 * due to any existing lock as deadlock detector won't
-						 * be able to detect the waits on the latch.
+						 * be able to detect the waits on the interrupt
 						 */
 						CommitTransactionCommand();
 						pgstat_report_stat(false);
@@ -771,14 +772,14 @@ copy_read_data(void *outbuf, int minread, int maxread)
 		}
 
 		/*
-		 * Wait for more data or latch.
+		 * Wait for more data or interrupt.
 		 */
-		(void) WaitLatchOrSocket(MyLatch,
-								 WL_SOCKET_READABLE | WL_LATCH_SET |
-								 WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-								 fd, 1000L, WAIT_EVENT_LOGICAL_SYNC_DATA);
+		(void) WaitInterruptOrSocket(1 << INTERRUPT_GENERAL_WAKEUP,
+									 WL_SOCKET_READABLE | WL_INTERRUPT |
+									 WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+									 fd, 1000L, WAIT_EVENT_LOGICAL_SYNC_DATA);
 
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 
 	return bytesread;
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 925dff9cc44..3deb436bee3 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -177,6 +177,7 @@
 #include "replication/worker_internal.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/buffile.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
 #include "tcop/tcopprot.h"
@@ -3729,26 +3730,26 @@ LogicalRepApplyLoop(XLogRecPtr last_received)
 			break;
 
 		/*
-		 * Wait for more data or latch.  If we have unflushed transactions,
-		 * wake up after WalWriterDelay to see if they've been flushed yet (in
-		 * which case we should send a feedback message).  Otherwise, there's
-		 * no particular urgency about waking up unless we get data or a
-		 * signal.
+		 * Wait for more data or interrupt.  If we have unflushed
+		 * transactions, wake up after WalWriterDelay to see if they've been
+		 * flushed yet (in which case we should send a feedback message).
+		 * Otherwise, there's no particular urgency about waking up unless we
+		 * get data or a signal.
 		 */
 		if (!dlist_is_empty(&lsn_mapping))
 			wait_time = WalWriterDelay;
 		else
 			wait_time = NAPTIME_PER_CYCLE;
 
-		rc = WaitLatchOrSocket(MyLatch,
-							   WL_SOCKET_READABLE | WL_LATCH_SET |
-							   WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-							   fd, wait_time,
-							   WAIT_EVENT_LOGICAL_APPLY_MAIN);
+		rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL_WAKEUP,
+								   WL_SOCKET_READABLE | WL_INTERRUPT |
+								   WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+								   fd, wait_time,
+								   WAIT_EVENT_LOGICAL_APPLY_MAIN);
 
-		if (rc & WL_LATCH_SET)
+		if (rc & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			CHECK_FOR_INTERRUPTS();
 		}
 
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index fa5988c824e..b1efda2e5f4 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -81,6 +81,7 @@
 #include "replication/syncrep.h"
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
+#include "storage/interrupt.h"
 #include "storage/proc.h"
 #include "tcop/tcopprot.h"
 #include "utils/guc_hooks.h"
@@ -222,22 +223,22 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 	/*
 	 * Wait for specified LSN to be confirmed.
 	 *
-	 * Each proc has its own wait latch, so we perform a normal latch
+	 * Each proc has its own wait interrupt vector, so we perform a normal
 	 * check/wait loop here.
 	 */
 	for (;;)
 	{
 		int			rc;
 
-		/* Must reset the latch before testing state. */
-		ResetLatch(MyLatch);
+		/* Must clear the interrupt flag before testing state. */
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/*
-		 * Acquiring the lock is not needed, the latch ensures proper
-		 * barriers. If it looks like we're done, we must really be done,
-		 * because once walsender changes the state to SYNC_REP_WAIT_COMPLETE,
-		 * it will never update it again, so we can't be seeing a stale value
-		 * in that case.
+		 * Acquiring the lock is not needed, the interrupt ensures proper
+		 * barriers (FIXME: is that so?). If it looks like we're done, we must
+		 * really be done, because once walsender changes the state to
+		 * SYNC_REP_WAIT_COMPLETE, it will never update it again, so we can't
+		 * be seeing a stale value in that case.
 		 */
 		if (MyProc->syncRepState == SYNC_REP_WAIT_COMPLETE)
 			break;
@@ -282,11 +283,13 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		}
 
 		/*
-		 * Wait on latch.  Any condition that should wake us up will set the
-		 * latch, so no need for timeout.
+		 * Wait on interrupt.  Any condition that should wake us up will set
+		 * the interrupt, so no need for timeout.
 		 */
-		rc = WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
-					   WAIT_EVENT_SYNC_REP);
+		rc = WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+						   WL_INTERRUPT | WL_POSTMASTER_DEATH,
+						   -1,
+						   WAIT_EVENT_SYNC_REP);
 
 		/*
 		 * If the postmaster dies, we'll probably never get an acknowledgment,
@@ -902,7 +905,7 @@ SyncRepWakeQueue(bool all, int mode)
 		/*
 		 * Wake only when we have set state and removed from queue.
 		 */
-		SetLatch(&(proc->procLatch));
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(proc));
 
 		numprocs++;
 	}
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 5f641d27905..39fa9fa3e2e 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -67,6 +67,7 @@
 #include "postmaster/interrupt.h"
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
@@ -147,15 +148,17 @@ static void WalRcvComputeNextWakeup(WalRcvWakeupReason reason, TimestampTz now);
 
 /*
  * Process any interrupts the walreceiver process may have received.
- * This should be called any time the process's latch has become set.
+ * This should be called any time the INTERRUPT_GENERAL_WAKEUP interrupt
+ * has become set.
  *
  * Currently, only SIGTERM is of interest.  We can't just exit(1) within the
  * SIGTERM signal handler, because the signal might arrive in the middle of
  * some critical operation, like while we're holding a spinlock.  Instead, the
- * signal handler sets a flag variable as well as setting the process's latch.
- * We must check the flag (by calling ProcessWalRcvInterrupts) anytime the
- * latch has become set.  Operations that could block for a long time, such as
- * reading from a remote server, must pay attention to the latch too; see
+ * signal handler sets a flag variable as well as raising
+ * INTERRUPT_GENERAL_WAKEUP.  We must check the flag (by calling
+ * ProcessWalRcvInterrupts) anytime the INTERRUPT_GENERAL_WAKEUP has become
+ * set.  Operations that could block for a long time, such as reading from a
+ * remote server, must pay attention to the interrupt too; see
  * libpqrcv_PQgetResult for example.
  */
 void
@@ -536,25 +539,25 @@ WalReceiverMain(char *startup_data, size_t startup_data_len)
 
 				/*
 				 * Ideally we would reuse a WaitEventSet object repeatedly
-				 * here to avoid the overheads of WaitLatchOrSocket on epoll
-				 * systems, but we can't be sure that libpq (or any other
-				 * walreceiver implementation) has the same socket (even if
-				 * the fd is the same number, it may have been closed and
+				 * here to avoid the overheads of WaitInterruptOrSocket on
+				 * epoll systems, but we can't be sure that libpq (or any
+				 * other walreceiver implementation) has the same socket (even
+				 * if the fd is the same number, it may have been closed and
 				 * reopened since the last time).  In future, if there is a
 				 * function for removing sockets from WaitEventSet, then we
 				 * could add and remove just the socket each time, potentially
 				 * avoiding some system calls.
 				 */
 				Assert(wait_fd != PGINVALID_SOCKET);
-				rc = WaitLatchOrSocket(MyLatch,
-									   WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE |
-									   WL_TIMEOUT | WL_LATCH_SET,
-									   wait_fd,
-									   nap,
-									   WAIT_EVENT_WAL_RECEIVER_MAIN);
-				if (rc & WL_LATCH_SET)
+				rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL_WAKEUP,
+										   WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE |
+										   WL_TIMEOUT | WL_INTERRUPT,
+										   wait_fd,
+										   nap,
+										   WAIT_EVENT_WAL_RECEIVER_MAIN);
+				if (rc & WL_INTERRUPT)
 				{
-					ResetLatch(MyLatch);
+					ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 					ProcessWalRcvInterrupts();
 
 					if (walrcv->force_reply)
@@ -692,7 +695,7 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 	WakeupRecovery();
 	for (;;)
 	{
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		ProcessWalRcvInterrupts();
 
@@ -724,8 +727,10 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
-						 WAIT_EVENT_WAL_RECEIVER_WAIT_START);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+							 0,
+							 WAIT_EVENT_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
@@ -1366,7 +1371,7 @@ WalRcvForceReply(void)
 	procno = WalRcv->procno;
 	SpinLockRelease(&WalRcv->mutex);
 	if (procno != INVALID_PROC_NUMBER)
-		SetLatch(&GetPGProcByNumber(procno)->procLatch);
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, procno);
 }
 
 /*
diff --git a/src/backend/replication/walreceiverfuncs.c b/src/backend/replication/walreceiverfuncs.c
index 8557d10cf9d..6212e825770 100644
--- a/src/backend/replication/walreceiverfuncs.c
+++ b/src/backend/replication/walreceiverfuncs.c
@@ -26,6 +26,7 @@
 #include "access/xlogrecovery.h"
 #include "pgstat.h"
 #include "replication/walreceiver.h"
+#include "storage/interrupt.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/shmem.h"
@@ -317,7 +318,7 @@ RequestXLogStreaming(TimeLineID tli, XLogRecPtr recptr, const char *conninfo,
 	if (launch)
 		SendPostmasterSignal(PMSIGNAL_START_WALRECEIVER);
 	else if (walrcv_proc != INVALID_PROC_NUMBER)
-		SetLatch(&GetPGProcByNumber(walrcv_proc)->procLatch);
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, walrcv_proc);
 }
 
 /*
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 371eef3dddc..25c5bd8e305 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -80,6 +80,7 @@
 #include "replication/walsender_private.h"
 #include "storage/condition_variable.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
@@ -1013,8 +1014,8 @@ StartReplication(StartReplicationCmd *cmd)
  * XLogReaderRoutine->page_read callback for logical decoding contexts, as a
  * walsender process.
  *
- * Inside the walsender we can do better than read_local_xlog_page,
- * which has to do a plain sleep/busy loop, because the walsender's latch gets
+ * Inside the walsender we can do better than read_local_xlog_page, which
+ * has to do a plain sleep/busy loop, because the walsender's interrupt gets
  * set every time WAL is flushed.
  */
 static int
@@ -1611,7 +1612,7 @@ ProcessPendingWrites(void)
 				   WAIT_EVENT_WAL_SENDER_WRITE_DATA);
 
 		/* Clear any already-pending wakeups */
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		CHECK_FOR_INTERRUPTS();
 
@@ -1628,8 +1629,8 @@ ProcessPendingWrites(void)
 			WalSndShutdown();
 	}
 
-	/* reactivate latch so WalSndLoop knows to continue */
-	SetLatch(MyLatch);
+	/* reactivate interrupt so WalSndLoop knows to continue */
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
@@ -1817,7 +1818,7 @@ WalSndWaitForWal(XLogRecPtr loc)
 		long		sleeptime;
 
 		/* Clear any already-pending wakeups */
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		CHECK_FOR_INTERRUPTS();
 
@@ -1937,8 +1938,8 @@ WalSndWaitForWal(XLogRecPtr loc)
 		WalSndWait(wakeEvents, sleeptime, wait_event);
 	}
 
-	/* reactivate latch so WalSndLoop knows to continue */
-	SetLatch(MyLatch);
+	/* reactivate interrupt flag so WalSndLoop knows to continue */
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	return RecentFlushPtr;
 }
 
@@ -2755,7 +2756,7 @@ WalSndLoop(WalSndSendDataCallback send_data)
 	for (;;)
 	{
 		/* Clear any already-pending wakeups */
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		CHECK_FOR_INTERRUPTS();
 
@@ -3554,7 +3555,7 @@ static void
 WalSndLastCycleHandler(SIGNAL_ARGS)
 {
 	got_SIGUSR2 = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /* Set up signal handlers */
@@ -3660,7 +3661,7 @@ WalSndWait(uint32 socket_events, long timeout, uint32 wait_event)
 {
 	WaitEvent	event;
 
-	ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, socket_events, NULL);
+	ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, socket_events, 0);
 
 	/*
 	 * We use a condition variable to efficiently wake up walsenders in
@@ -3672,8 +3673,8 @@ WalSndWait(uint32 socket_events, long timeout, uint32 wait_event)
 	 * ConditionVariableSleep()). It still uses WaitEventSetWait() for
 	 * waiting, because we also need to wait for socket events. The processes
 	 * (startup process, walreceiver etc.) wanting to wake up walsenders use
-	 * ConditionVariableBroadcast(), which in turn calls SetLatch(), helping
-	 * walsenders come out of WaitEventSetWait().
+	 * ConditionVariableBroadcast(), which in turn calls SendInterrupt(),
+	 * helping walsenders come out of WaitEventSetWait().
 	 *
 	 * This approach is simple and efficient because, one doesn't have to loop
 	 * through all the walsenders slots, with a spinlock acquisition and
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 0f02bf62fa3..9992b837c5d 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -51,6 +51,7 @@
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
 #include "storage/proc.h"
@@ -2882,7 +2883,7 @@ UnpinBufferNoOwner(BufferDesc *buf)
 
 				buf_state &= ~BM_PIN_COUNT_WAITER;
 				UnlockBufHdr(buf, buf_state);
-				ProcSendSignal(wait_backend_pgprocno);
+				SendInterrupt(INTERRUPT_GENERAL_WAKEUP, wait_backend_pgprocno);
 			}
 			else
 				UnlockBufHdr(buf, buf_state);
@@ -5344,7 +5345,12 @@ LockBufferForCleanup(Buffer buffer)
 			SetStartupBufferPinWaitBufId(-1);
 		}
 		else
-			ProcWaitForSignal(WAIT_EVENT_BUFFER_PIN);
+		{
+			WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+						  WAIT_EVENT_BUFFER_PIN);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
+			CHECK_FOR_INTERRUPTS();
+		}
 
 		/*
 		 * Remove flag marking us as waiter. Normally this will not be set
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c
index dffdd57e9b5..57733b03d20 100644
--- a/src/backend/storage/buffer/freelist.c
+++ b/src/backend/storage/buffer/freelist.c
@@ -19,6 +19,7 @@
 #include "port/atomics.h"
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
+#include "storage/interrupt.h"
 #include "storage/proc.h"
 
 #define INT_ACCESS_ONCE(var)	((int)(*((volatile int *)&(var))))
@@ -219,27 +220,25 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint32 *buf_state, bool *from_r
 	/*
 	 * If asked, we need to waken the bgwriter. Since we don't want to rely on
 	 * a spinlock for this we force a read from shared memory once, and then
-	 * set the latch based on that value. We need to go through that length
-	 * because otherwise bgwprocno might be reset while/after we check because
-	 * the compiler might just reread from memory.
+	 * send the interrupt based on that value. We need to go through that
+	 * length because otherwise bgwprocno might be reset while/after we check
+	 * because the compiler might just reread from memory.
 	 *
-	 * This can possibly set the latch of the wrong process if the bgwriter
-	 * dies in the wrong moment. But since PGPROC->procLatch is never
-	 * deallocated the worst consequence of that is that we set the latch of
-	 * some arbitrary process.
+	 * This can possibly send the interrupt to the wrong process if the
+	 * bgwriter dies in the wrong moment, but that's harmless.
 	 */
 	bgwprocno = INT_ACCESS_ONCE(StrategyControl->bgwprocno);
 	if (bgwprocno != -1)
 	{
-		/* reset bgwprocno first, before setting the latch */
+		/* reset bgwprocno first, before sending the interrupt */
 		StrategyControl->bgwprocno = -1;
 
 		/*
-		 * Not acquiring ProcArrayLock here which is slightly icky. It's
-		 * actually fine because procLatch isn't ever freed, so we just can
-		 * potentially set the wrong process' (or no process') latch.
+		 * Not acquiring ProcArrayLock here which is slightly icky, because we
+		 * can potentially send the interrupt to the wrong process (or no
+		 * process), but it's harmless.
 		 */
-		SetLatch(&ProcGlobal->allProcs[bgwprocno].procLatch);
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, bgwprocno);
 	}
 
 	/*
@@ -420,10 +419,10 @@ StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc)
 }
 
 /*
- * StrategyNotifyBgWriter -- set or clear allocation notification latch
+ * StrategyNotifyBgWriter -- set or clear allocation notification process
  *
  * If bgwprocno isn't -1, the next invocation of StrategyGetBuffer will
- * set that latch.  Pass -1 to clear the pending notification before it
+ * interrupt that process.  Pass -1 to clear the pending notification before it
  * happens.  This feature is used by the bgwriter process to wake itself up
  * from hibernation, and is not meant for anybody else to use.
  */
diff --git a/src/backend/storage/ipc/Makefile b/src/backend/storage/ipc/Makefile
index d8a1653eb6a..5c7c72f902a 100644
--- a/src/backend/storage/ipc/Makefile
+++ b/src/backend/storage/ipc/Makefile
@@ -13,9 +13,9 @@ OBJS = \
 	dsm.o \
 	dsm_impl.o \
 	dsm_registry.o \
+	interrupt.o \
 	ipc.o \
 	ipci.o \
-	latch.o \
 	pmsignal.o \
 	procarray.o \
 	procsignal.o \
@@ -25,6 +25,7 @@ OBJS = \
 	signalfuncs.o \
 	sinval.o \
 	sinvaladt.o \
-	standby.o
+	standby.o \
+	waiteventset.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/ipc/interrupt.c b/src/backend/storage/ipc/interrupt.c
new file mode 100644
index 00000000000..b94ed343dc5
--- /dev/null
+++ b/src/backend/storage/ipc/interrupt.c
@@ -0,0 +1,114 @@
+/*-------------------------------------------------------------------------
+ *
+ * interrupt.c
+ *	  Interrupt handling routines.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/storage/ipc/interrupt.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <unistd.h>
+
+#include "miscadmin.h"
+#include "port/atomics.h"
+#include "storage/interrupt.h"
+#include "storage/ipc.h"
+#include "storage/proc.h"
+#include "storage/procsignal.h"
+#include "storage/waiteventset.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+
+static pg_atomic_uint32 LocalPendingInterrupts;
+static pg_atomic_uint32 LocalMaybeSleepingOnInterrupts;
+
+pg_atomic_uint32 *MyPendingInterrupts = &LocalPendingInterrupts;
+pg_atomic_uint32 *MyMaybeSleepingOnInterrupts = &LocalMaybeSleepingOnInterrupts;
+
+/*
+ * Switch to local interrupts.  Other backends can't send interrupts to this
+ * one.  Only RaiseInterrupt() can set them, from inside this process.
+ */
+void
+SwitchToLocalInterrupts(void)
+{
+	if (MyPendingInterrupts == &LocalPendingInterrupts)
+		return;
+
+	MyPendingInterrupts = &LocalPendingInterrupts;
+	MyMaybeSleepingOnInterrupts = &LocalMaybeSleepingOnInterrupts;
+
+	/*
+	 * Make sure that SIGALRM handlers that call RaiseInterrupt() are now
+	 * seeing the new MyPendingInterrupts destination.
+	 */
+	pg_memory_barrier();
+
+	/*
+	 * Mix in the interrupts that we have received already in our shared
+	 * interrupt vector, while atomically clearing it.  Other backends may
+	 * continue to set bits in it after this point, but we've atomically
+	 * transferred the existing bits to our local vector so we won't get
+	 * duplicated interrupts later if we switch backx.
+	 */
+	pg_atomic_fetch_or_u32(MyPendingInterrupts,
+						   pg_atomic_exchange_u32(&MyProc->pendingInterrupts, 0));
+}
+
+/*
+ * Switch to shared memory interrupts.  Other backends can send interrupts to
+ * this one if they know its ProcNumber, and we'll now see any that we missed.
+ */
+void
+SwitchToSharedInterrupts(void)
+{
+	if (MyPendingInterrupts == &MyProc->pendingInterrupts)
+		return;
+
+	MyPendingInterrupts = &MyProc->pendingInterrupts;
+	MyMaybeSleepingOnInterrupts = &MyProc->maybeSleepingOnInterrupts;
+
+	/*
+	 * Make sure that SIGALRM handlers that call RaiseInterrupt() are now
+	 * seeing the new MyPendingInterrupts destination.
+	 */
+	pg_memory_barrier();
+
+	/* Mix in any unhandled bits from LocalPendingInterrupts. */
+	pg_atomic_fetch_or_u32(MyPendingInterrupts,
+						   pg_atomic_exchange_u32(&LocalPendingInterrupts, 0));
+}
+
+/*
+ * Set an interrupt flag in this backend.
+ */
+void
+RaiseInterrupt(InterruptType reason)
+{
+	pg_atomic_fetch_or_u32(MyPendingInterrupts, 1 << reason);
+	WakeupMyProc();
+}
+
+/*
+ * Set an interrupt flag in another backend.
+ */
+void
+SendInterrupt(InterruptType reason, ProcNumber pgprocno)
+{
+	PGPROC	   *proc;
+
+	Assert(pgprocno != INVALID_PROC_NUMBER);
+	Assert(pgprocno >= 0);
+	Assert(pgprocno < ProcGlobal->allProcCount);
+
+	proc = &ProcGlobal->allProcs[pgprocno];
+	pg_atomic_fetch_or_u32(&proc->pendingInterrupts, 1 << reason);
+	WakeupOtherProc(proc);
+}
diff --git a/src/backend/storage/ipc/meson.build b/src/backend/storage/ipc/meson.build
index 5a936171f73..4eba41b78af 100644
--- a/src/backend/storage/ipc/meson.build
+++ b/src/backend/storage/ipc/meson.build
@@ -5,9 +5,9 @@ backend_sources += files(
   'dsm.c',
   'dsm_impl.c',
   'dsm_registry.c',
+  'interrupt.c',
   'ipc.c',
   'ipci.c',
-  'latch.c',
   'pmsignal.c',
   'procarray.c',
   'procsignal.c',
@@ -18,5 +18,6 @@ backend_sources += files(
   'sinval.c',
   'sinvaladt.c',
   'standby.c',
+  'waiteventset.c',
 
 )
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 87027f27eb7..f97f4ca1cec 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -25,8 +25,8 @@
 #include "replication/logicalworker.h"
 #include "replication/walsender.h"
 #include "storage/condition_variable.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/shmem.h"
 #include "storage/sinval.h"
 #include "storage/smgr.h"
@@ -481,7 +481,7 @@ HandleProcSignalBarrierInterrupt(void)
 {
 	InterruptPending = true;
 	ProcSignalBarrierPending = true;
-	/* latch will be set by procsignal_sigusr1_handler */
+	/* interrupt will be raised by procsignal_sigusr1_handler */
 }
 
 /*
@@ -712,7 +712,7 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 9235fcd08ec..7605acfd0c6 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -4,7 +4,7 @@
  *	  single-reader, single-writer shared memory message queue
  *
  * Both the sender and the receiver must have a PGPROC; their respective
- * process latches are used for synchronization.  Only the sender may send,
+ * process interrupts are used for synchronization.  Only the sender may send,
  * and only the receiver may receive.  This is intended to allow a user
  * backend to communicate with worker backends that it has registered.
  *
@@ -22,6 +22,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/bgworker.h"
+#include "storage/interrupt.h"
 #include "storage/shm_mq.h"
 #include "storage/spin.h"
 #include "utils/memutils.h"
@@ -44,10 +45,11 @@
  *
  * mq_detached needs no locking.  It can be set by either the sender or the
  * receiver, but only ever from false to true, so redundant writes don't
- * matter.  It is important that if we set mq_detached and then set the
- * counterparty's latch, the counterparty must be certain to see the change
- * after waking up.  Since SetLatch begins with a memory barrier and ResetLatch
- * ends with one, this should be OK.
+ * matter.  It is important that if we set mq_detached and then send the
+ * interrupt to the counterparty, the counterparty must be certain to see the
+ * change after waking up.  Since SendInterrupt begins with a memory barrier
+ * and ClearInterrupt ends with one, this should be OK.
+ * XXX: No explicit memory barriers in SendIntterupt/ClearInterrupt, do we need to add some?
  *
  * mq_ring_size and mq_ring_offset never change after initialization, and
  * can therefore be read without the lock.
@@ -112,7 +114,7 @@ struct shm_mq
  * yet updated in the shared memory.  We will not update it until the written
  * data is 1/4th of the ring size or the tuple queue is full.  This will
  * prevent frequent CPU cache misses, and it will also avoid frequent
- * SetLatch() calls, which are quite expensive.
+ * SendInterrupt() calls, which are quite expensive.
  *
  * mqh_partial_bytes, mqh_expected_bytes, and mqh_length_word_complete
  * are used to track the state of non-blocking operations.  When the caller
@@ -214,7 +216,7 @@ shm_mq_set_receiver(shm_mq *mq, PGPROC *proc)
 	SpinLockRelease(&mq->mq_mutex);
 
 	if (sender != NULL)
-		SetLatch(&sender->procLatch);
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(sender));
 }
 
 /*
@@ -232,7 +234,7 @@ shm_mq_set_sender(shm_mq *mq, PGPROC *proc)
 	SpinLockRelease(&mq->mq_mutex);
 
 	if (receiver != NULL)
-		SetLatch(&receiver->procLatch);
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(receiver));
 }
 
 /*
@@ -341,14 +343,14 @@ shm_mq_send(shm_mq_handle *mqh, Size nbytes, const void *data, bool nowait,
  * Write a message into a shared message queue, gathered from multiple
  * addresses.
  *
- * When nowait = false, we'll wait on our process latch when the ring buffer
- * fills up, and then continue writing once the receiver has drained some data.
- * The process latch is reset after each wait.
+ * When nowait = false, we'll wait on our process interrupt when the ring
+ * buffer fills up, and then continue writing once the receiver has drained
+ * some data.  The process interrupt is cleared after each wait.
  *
- * When nowait = true, we do not manipulate the state of the process latch;
+ * When nowait = true, we do not manipulate the state of the process interrupt;
  * instead, if the buffer becomes full, we return SHM_MQ_WOULD_BLOCK.  In
  * this case, the caller should call this function again, with the same
- * arguments, each time the process latch is set.  (Once begun, the sending
+ * arguments, each time the process interrupt is set.  (Once begun, the sending
  * of a message cannot be aborted except by detaching from the queue; changing
  * the length or payload will corrupt the queue.)
  *
@@ -539,7 +541,7 @@ shm_mq_sendv(shm_mq_handle *mqh, shm_mq_iovec *iov, int iovcnt, bool nowait,
 	{
 		shm_mq_inc_bytes_written(mq, mqh->mqh_send_pending);
 		if (receiver != NULL)
-			SetLatch(&receiver->procLatch);
+			SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(receiver));
 		mqh->mqh_send_pending = 0;
 	}
 
@@ -557,16 +559,16 @@ shm_mq_sendv(shm_mq_handle *mqh, shm_mq_iovec *iov, int iovcnt, bool nowait,
  * while still allowing longer messages.  In either case, the return value
  * remains valid until the next receive operation is performed on the queue.
  *
- * When nowait = false, we'll wait on our process latch when the ring buffer
- * is empty and we have not yet received a full message.  The sender will
- * set our process latch after more data has been written, and we'll resume
- * processing.  Each call will therefore return a complete message
+ * When nowait = false, we'll wait on our process interrupt when the ring
+ * buffer is empty and we have not yet received a full message.  The sender
+ * will set our process interrupt after more data has been written, and we'll
+ * resume processing.  Each call will therefore return a complete message
  * (unless the sender detaches the queue).
  *
- * When nowait = true, we do not manipulate the state of the process latch;
- * instead, whenever the buffer is empty and we need to read from it, we
- * return SHM_MQ_WOULD_BLOCK.  In this case, the caller should call this
- * function again after the process latch has been set.
+ * When nowait = true, we do not manipulate the state of the process
+ * interrupt; instead, whenever the buffer is empty and we need to read from
+ * it, we return SHM_MQ_WOULD_BLOCK.  In this case, the caller should call
+ * this function again after the process interrupt has been set.
  */
 shm_mq_result
 shm_mq_receive(shm_mq_handle *mqh, Size *nbytesp, void **datap, bool nowait)
@@ -619,8 +621,8 @@ shm_mq_receive(shm_mq_handle *mqh, Size *nbytesp, void **datap, bool nowait)
 	 * If we've consumed an amount of data greater than 1/4th of the ring
 	 * size, mark it consumed in shared memory.  We try to avoid doing this
 	 * unnecessarily when only a small amount of data has been consumed,
-	 * because SetLatch() is fairly expensive and we don't want to do it too
-	 * often.
+	 * because SendInterrupt() is fairly expensive and we don't want to do it
+	 * too often.
 	 */
 	if (mqh->mqh_consume_pending > mq->mq_ring_size / 4)
 	{
@@ -895,7 +897,7 @@ shm_mq_detach_internal(shm_mq *mq)
 	SpinLockRelease(&mq->mq_mutex);
 
 	if (victim != NULL)
-		SetLatch(&victim->procLatch);
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(victim));
 }
 
 /*
@@ -993,7 +995,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * Therefore, we can read it without acquiring the spinlock.
 			 */
 			Assert(mqh->mqh_counterparty_attached);
-			SetLatch(&mq->mq_receiver->procLatch);
+			SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(mq->mq_receiver));
 
 			/*
 			 * We have just updated the mqh_send_pending bytes in the shared
@@ -1001,7 +1003,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 */
 			mqh->mqh_send_pending = 0;
 
-			/* Skip manipulation of our latch if nowait = true. */
+			/* Skip waiting if nowait = true. */
 			if (nowait)
 			{
 				*bytes_written = sent;
@@ -1009,17 +1011,17 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			}
 
 			/*
-			 * Wait for our latch to be set.  It might already be set for some
+			 * Wait for the interrupt.  It might already be set for some
 			 * unrelated reason, but that'll just result in one extra trip
-			 * through the loop.  It's worth it to avoid resetting the latch
-			 * at top of loop, because setting an already-set latch is much
-			 * cheaper than setting one that has been reset.
+			 * through the loop.  It's worth it to avoid clearing the
+			 * interrupt at top of loop, because setting an already-set
+			 * interrupt is much cheaper than setting one that has been reset.
 			 */
-			(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
-							 WAIT_EVENT_MESSAGE_QUEUE_SEND);
+			(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+								 WAIT_EVENT_MESSAGE_QUEUE_SEND);
 
-			/* Reset the latch so we don't spin. */
-			ResetLatch(MyLatch);
+			/* Clear the interrupt so we don't spin. */
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 			/* An interrupt may have occurred while we were waiting. */
 			CHECK_FOR_INTERRUPTS();
@@ -1054,7 +1056,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 
 			/*
 			 * For efficiency, we don't update the bytes written in the shared
-			 * memory and also don't set the reader's latch here.  Refer to
+			 * memory and also don't send the reader interrupt here.  Refer to
 			 * the comments atop the shm_mq_handle structure for more
 			 * information.
 			 */
@@ -1150,22 +1152,22 @@ shm_mq_receive_bytes(shm_mq_handle *mqh, Size bytes_needed, bool nowait,
 			mqh->mqh_consume_pending = 0;
 		}
 
-		/* Skip manipulation of our latch if nowait = true. */
+		/* Skip waiting if nowait = true. */
 		if (nowait)
 			return SHM_MQ_WOULD_BLOCK;
 
 		/*
-		 * Wait for our latch to be set.  It might already be set for some
+		 * Wait for the interrupt to be set.  It might already be set for some
 		 * unrelated reason, but that'll just result in one extra trip through
-		 * the loop.  It's worth it to avoid resetting the latch at top of
-		 * loop, because setting an already-set latch is much cheaper than
-		 * setting one that has been reset.
+		 * the loop.  It's worth it to avoid clearing the interrupt at top of
+		 * loop, because setting an already-set interrupt is much cheaper than
+		 * setting one that has been cleared.
 		 */
-		(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
-						 WAIT_EVENT_MESSAGE_QUEUE_RECEIVE);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+							 WAIT_EVENT_MESSAGE_QUEUE_RECEIVE);
 
-		/* Reset the latch so we don't spin. */
-		ResetLatch(MyLatch);
+		/* Clear the interrupt so we don't spin. */
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/* An interrupt may have occurred while we were waiting. */
 		CHECK_FOR_INTERRUPTS();
@@ -1250,11 +1252,11 @@ shm_mq_wait_internal(shm_mq *mq, PGPROC **ptr, BackgroundWorkerHandle *handle)
 		}
 
 		/* Wait to be signaled. */
-		(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
-						 WAIT_EVENT_MESSAGE_QUEUE_INTERNAL);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+							 WAIT_EVENT_MESSAGE_QUEUE_INTERNAL);
 
-		/* Reset the latch so we don't spin. */
-		ResetLatch(MyLatch);
+		/* Clear the interrupt so we don't spin. */
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/* An interrupt may have occurred while we were waiting. */
 		CHECK_FOR_INTERRUPTS();
@@ -1293,7 +1295,7 @@ shm_mq_inc_bytes_read(shm_mq *mq, Size n)
 	 */
 	sender = mq->mq_sender;
 	Assert(sender != NULL);
-	SetLatch(&sender->procLatch);
+	SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(sender));
 }
 
 /*
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index aa729a36e39..a354a7d0656 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -20,6 +20,7 @@
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "postmaster/syslogger.h"
+#include "storage/interrupt.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
@@ -204,12 +205,12 @@ pg_wait_until_termination(int pid, int64 timeout)
 		/* Process interrupts, if any, before waiting */
 		CHECK_FOR_INTERRUPTS();
 
-		(void) WaitLatch(MyLatch,
-						 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-						 waittime,
-						 WAIT_EVENT_BACKEND_TERMINATION);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 waittime,
+							 WAIT_EVENT_BACKEND_TERMINATION);
 
-		ResetLatch(MyLatch);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		remainingtime -= waittime;
 	} while (remainingtime > 0);
diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c
index d9b16f84d19..b157b1a9958 100644
--- a/src/backend/storage/ipc/sinval.c
+++ b/src/backend/storage/ipc/sinval.c
@@ -16,7 +16,7 @@
 
 #include "access/xact.h"
 #include "miscadmin.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
 #include "storage/sinvaladt.h"
 #include "utils/inval.h"
 
@@ -31,9 +31,9 @@ uint64		SharedInvalidMessageCounter;
  * 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
+ * The signal handler will set an interrupt pending flag and raise the
+ * INTERRUPT_GENERAL_WAKEUP. Whenever starting to read from the client, or
+ * when interrupted while doing so, ProcessClientReadInterrupt() will call
  * ProcessCatchupEvent().
  */
 volatile sig_atomic_t catchupInterruptPending = false;
@@ -148,7 +148,7 @@ ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *m
  *
  * 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.
+ * raising INTERRUPT_GENERAL_WAKEUP.
  */
 void
 HandleCatchupInterrupt(void)
@@ -161,7 +161,7 @@ HandleCatchupInterrupt(void)
 	catchupInterruptPending = true;
 
 	/* make sure the event is processed in due course */
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 25267f0f85d..e8e4d731422 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -26,6 +26,7 @@
 #include "pgstat.h"
 #include "replication/slot.h"
 #include "storage/bufmgr.h"
+#include "storage/interrupt.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
 #include "storage/sinvaladt.h"
@@ -594,7 +595,7 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
  * ResolveRecoveryConflictWithLock is called from ProcSleep()
  * to resolve conflicts with other backends holding relation locks.
  *
- * The WaitLatch sleep normally done in ProcSleep()
+ * The WaitInterrupt sleep normally done in ProcSleep()
  * (when not InHotStandby) is performed here, for code clarity.
  *
  * We either resolve conflicts immediately or set a timeout to wake us at
@@ -696,7 +697,10 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
 	}
 
 	/* Wait to be signaled by the release of the Relation Lock */
-	ProcWaitForSignal(PG_WAIT_LOCK | locktag.locktag_type);
+	WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+				  PG_WAIT_LOCK | locktag.locktag_type);
+	ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
+	CHECK_FOR_INTERRUPTS();
 
 	/*
 	 * Exit if ltime is reached. Then all the backends holding conflicting
@@ -745,7 +749,10 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
 		 * until the relation locks are released or ltime is reached.
 		 */
 		got_standby_deadlock_timeout = false;
-		ProcWaitForSignal(PG_WAIT_LOCK | locktag.locktag_type);
+		WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+					  PG_WAIT_LOCK | locktag.locktag_type);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
+		CHECK_FOR_INTERRUPTS();
 	}
 
 cleanup:
@@ -837,9 +844,14 @@ ResolveRecoveryConflictWithBufferPin(void)
 	 * We assume that only UnpinBuffer() and the timeout requests established
 	 * above can wake us up here. WakeupRecovery() called by walreceiver or
 	 * SIGHUP signal handler, etc cannot do that because it uses the different
-	 * latch from that ProcWaitForSignal() waits on.
+	 * interrupt flag. FIXME: seems like a shaky assumption. WakeupRecovery()
+	 * indeed uses a different interrupt flag (different latch earlier), but
+	 * the signal handler??
 	 */
-	ProcWaitForSignal(WAIT_EVENT_BUFFER_PIN);
+	WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+				  WAIT_EVENT_BUFFER_PIN);
+	ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
+	CHECK_FOR_INTERRUPTS();
 
 	if (got_standby_delay_timeout)
 		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/waiteventset.c
similarity index 77%
rename from src/backend/storage/ipc/latch.c
rename to src/backend/storage/ipc/waiteventset.c
index 608eb66abed..1bbba04e343 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/waiteventset.c
@@ -1,17 +1,32 @@
 /*-------------------------------------------------------------------------
  *
- * latch.c
- *	  Routines for inter-process latches
+ * waiteventset.c
+ *	  ppoll()/pselect() like abstraction
  *
- * The poll() implementation uses the so-called self-pipe trick to overcome the
- * race condition involved with poll() and setting a global flag in the signal
- * handler. When a latch is set and the current process is waiting for it, the
- * signal handler wakes up the poll() in WaitLatch by writing a byte to a pipe.
- * A signal by itself doesn't interrupt poll() on all platforms, and even on
- * platforms where it does, a signal that arrives just before the poll() call
- * does not prevent poll() from entering sleep. An incoming byte on a pipe
- * however reliably interrupts the sleep, and causes poll() to return
- * immediately even if the signal arrives before poll() begins.
+ * WaitEvents are an abstraction for waiting for one or more events at a time.
+ * The waiting can be done in a race free fashion, similar ppoll() or
+ * pselect() (as opposed to plain poll()/select()).
+ *
+ * You can wait for:
+ * - an interrupt from another process or from signal handler in the same
+ *   process (WL_INTERRUPT)
+ * - data to become readable or writeable on a socket (WL_SOCKET_*)
+ * - postmaster death (WL_POSTMASTER_DEATH or WL_EXIT_ON_PM_DEATH)
+ * - timeout (WL_TIMEOUT)
+ *
+ * Implementation
+ * --------------
+ *
+ * The poll() implementation uses the so-called self-pipe trick to overcome
+ * the race condition involved with poll() and setting a global flag in the
+ * signal handler. When an interrupt is received and the current process is
+ * waiting for it, the signal handler wakes up the poll() in WaitInterrupt by
+ * writing a byte to a pipe.  A signal by itself doesn't interrupt poll() on
+ * all platforms, and even on platforms where it does, a signal that arrives
+ * just before the poll() call does not prevent poll() from entering sleep. An
+ * incoming byte on a pipe however reliably interrupts the sleep, and causes
+ * poll() to return immediately even if the signal arrives before poll()
+ * begins.
  *
  * The epoll() implementation overcomes the race with a different technique: it
  * keeps SIGURG blocked and consumes from a signalfd() descriptor instead.  We
@@ -27,7 +42,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  src/backend/storage/ipc/latch.c
+ *	  src/backend/storage/ipc/waiteventset.c
  *
  *-------------------------------------------------------------------------
  */
@@ -57,9 +72,11 @@
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/pmsignal.h"
+#include "storage/proc.h"
+#include "storage/waiteventset.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
 
@@ -98,7 +115,7 @@
 #endif
 #endif
 
-/* typedef in latch.h */
+/* typedef in waiteventset.h */
 struct WaitEventSet
 {
 	ResourceOwner owner;
@@ -113,13 +130,13 @@ struct WaitEventSet
 	WaitEvent  *events;
 
 	/*
-	 * If WL_LATCH_SET is specified in any wait event, latch is a pointer to
-	 * said latch, and latch_pos the offset in the ->events array. This is
-	 * useful because we check the state of the latch before performing doing
-	 * syscalls related to waiting.
+	 * If WL_INTERRUPT is specified in any wait event, interruptMask is the
+	 * interrupts to wait for, and interrupt_pos the offset in the ->events
+	 * array. This is useful because we check the state of pending interrupts
+	 * before performing doing syscalls related to waiting.
 	 */
-	Latch	   *latch;
-	int			latch_pos;
+	uint32		interrupt_mask;
+	int			interrupt_pos;
 
 	/*
 	 * WL_EXIT_ON_PM_DEATH is converted to WL_POSTMASTER_DEATH, but this flag
@@ -151,14 +168,14 @@ struct WaitEventSet
 #endif
 };
 
-/* A common WaitEventSet used to implement WaitLatch() */
-static WaitEventSet *LatchWaitSet;
+/* A common WaitEventSet used to implement WaitInterrupt() */
+static WaitEventSet *InterruptWaitSet;
 
-/* The position of the latch in LatchWaitSet. */
-#define LatchWaitSetLatchPos 0
+/* The position of the interrupt in InterruptWaitSet. */
+#define InterruptWaitSetInterruptPos 0
 
 #ifndef WIN32
-/* Are we currently in WaitLatch? The signal handler would like to know. */
+/* Are we currently in WaitInterrupt? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
 #endif
 
@@ -174,9 +191,16 @@ static int	selfpipe_writefd = -1;
 
 /* Process owning the self-pipe --- needed for checking purposes */
 static int	selfpipe_owner_pid = 0;
+#endif
+
+#ifdef WAIT_USE_WIN32
+static HANDLE LocalInterruptWakeupEvent;
+#endif
 
 /* Private function prototypes */
-static void latch_sigurg_handler(SIGNAL_ARGS);
+
+#ifdef WAIT_USE_SELF_PIPE
+static void interrupt_sigurg_handler(SIGNAL_ARGS);
 static void sendSelfPipeByte(void);
 #endif
 
@@ -223,13 +247,13 @@ ResourceOwnerForgetWaitEventSet(ResourceOwner owner, WaitEventSet *set)
 
 
 /*
- * Initialize the process-local latch infrastructure.
+ * Initialize the process-local wait event infrastructure.
  *
  * This must be called once during startup of any process that can wait on
- * latches, before it issues any InitLatch() or OwnLatch() calls.
+ * interrupts.
  */
 void
-InitializeLatchSupport(void)
+InitializeWaitEventSupport(void)
 {
 #if defined(WAIT_USE_SELF_PIPE)
 	int			pipefd[2];
@@ -276,12 +300,12 @@ InitializeLatchSupport(void)
 
 	/*
 	 * Set up the self-pipe that allows a signal handler to wake up the
-	 * poll()/epoll_wait() in WaitLatch. Make the write-end non-blocking, so
-	 * that SetLatch won't block if the event has already been set many times
-	 * filling the kernel buffer. Make the read-end non-blocking too, so that
-	 * we can easily clear the pipe by reading until EAGAIN or EWOULDBLOCK.
-	 * Also, make both FDs close-on-exec, since we surely do not want any
-	 * child processes messing with them.
+	 * poll()/epoll_wait() in WaitInterrupt. Make the write-end non-blocking,
+	 * so that SendInterrupt won't block if the event has already been set
+	 * many times filling the kernel buffer. Make the read-end non-blocking
+	 * too, so that we can easily clear the pipe by reading until EAGAIN or
+	 * EWOULDBLOCK. Also, make both FDs close-on-exec, since we surely do not
+	 * want any child processes messing with them.
 	 */
 	if (pipe(pipefd) < 0)
 		elog(FATAL, "pipe() failed: %m");
@@ -302,7 +326,7 @@ InitializeLatchSupport(void)
 	ReserveExternalFD();
 	ReserveExternalFD();
 
-	pqsignal(SIGURG, latch_sigurg_handler);
+	pqsignal(SIGURG, interrupt_sigurg_handler);
 #endif
 
 #ifdef WAIT_USE_SIGNALFD
@@ -340,37 +364,43 @@ InitializeLatchSupport(void)
 	/* Ignore SIGURG, because we'll receive it via kqueue. */
 	pqsignal(SIGURG, SIG_IGN);
 #endif
+
+#ifdef WAIT_USE_WIN32
+	LocalInterruptWakeupEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (LocalInterruptWakeupEvent == NULL)
+		elog(ERROR, "CreateEvent failed: error code %lu", GetLastError());
+#endif
 }
 
 void
-InitializeLatchWaitSet(void)
+InitializeInterruptWaitSet(void)
 {
-	int			latch_pos PG_USED_FOR_ASSERTS_ONLY;
+	int			interrupt_pos PG_USED_FOR_ASSERTS_ONLY;
 
-	Assert(LatchWaitSet == NULL);
+	Assert(InterruptWaitSet == NULL);
 
-	/* Set up the WaitEventSet used by WaitLatch(). */
-	LatchWaitSet = CreateWaitEventSet(NULL, 2);
-	latch_pos = AddWaitEventToSet(LatchWaitSet, WL_LATCH_SET, PGINVALID_SOCKET,
-								  MyLatch, NULL);
+	/* Set up the WaitEventSet used by WaitInterrupt(). */
+	InterruptWaitSet = CreateWaitEventSet(NULL, 2);
+	interrupt_pos = AddWaitEventToSet(InterruptWaitSet, WL_INTERRUPT, PGINVALID_SOCKET,
+									  0, NULL);
 	if (IsUnderPostmaster)
-		AddWaitEventToSet(LatchWaitSet, WL_EXIT_ON_PM_DEATH,
-						  PGINVALID_SOCKET, NULL, NULL);
+		AddWaitEventToSet(InterruptWaitSet, WL_EXIT_ON_PM_DEATH,
+						  PGINVALID_SOCKET, 0, NULL);
 
-	Assert(latch_pos == LatchWaitSetLatchPos);
+	Assert(interrupt_pos == InterruptWaitSetInterruptPos);
 }
 
 void
-ShutdownLatchSupport(void)
+ShutdownWaitEventSupport(void)
 {
 #if defined(WAIT_USE_POLL)
 	pqsignal(SIGURG, SIG_IGN);
 #endif
 
-	if (LatchWaitSet)
+	if (InterruptWaitSet)
 	{
-		FreeWaitEventSet(LatchWaitSet);
-		LatchWaitSet = NULL;
+		FreeWaitEventSet(InterruptWaitSet);
+		InterruptWaitSet = NULL;
 	}
 
 #if defined(WAIT_USE_SELF_PIPE)
@@ -388,134 +418,24 @@ ShutdownLatchSupport(void)
 }
 
 /*
- * Initialize a process-local latch.
- */
-void
-InitLatch(Latch *latch)
-{
-	latch->is_set = false;
-	latch->maybe_sleeping = false;
-	latch->owner_pid = MyProcPid;
-	latch->is_shared = false;
-
-#if defined(WAIT_USE_SELF_PIPE)
-	/* Assert InitializeLatchSupport has been called in this process */
-	Assert(selfpipe_readfd >= 0 && selfpipe_owner_pid == MyProcPid);
-#elif defined(WAIT_USE_SIGNALFD)
-	/* Assert InitializeLatchSupport has been called in this process */
-	Assert(signal_fd >= 0);
-#elif defined(WAIT_USE_WIN32)
-	latch->event = CreateEvent(NULL, TRUE, FALSE, NULL);
-	if (latch->event == NULL)
-		elog(ERROR, "CreateEvent failed: error code %lu", GetLastError());
-#endif							/* WIN32 */
-}
-
-/*
- * Initialize a shared latch that can be set from other processes. The latch
- * is initially owned by no-one; use OwnLatch to associate it with the
- * current process.
- *
- * InitSharedLatch needs to be called in postmaster before forking child
- * processes, usually right after allocating the shared memory block
- * containing the latch with ShmemInitStruct. (The Unix implementation
- * doesn't actually require that, but the Windows one does.) Because of
- * this restriction, we have no concurrency issues to worry about here.
- *
- * Note that other handles created in this module are never marked as
- * inheritable.  Thus we do not need to worry about cleaning up child
- * process references to postmaster-private latches or WaitEventSets.
- */
-void
-InitSharedLatch(Latch *latch)
-{
-#ifdef WIN32
-	SECURITY_ATTRIBUTES sa;
-
-	/*
-	 * Set up security attributes to specify that the events are inherited.
-	 */
-	ZeroMemory(&sa, sizeof(sa));
-	sa.nLength = sizeof(sa);
-	sa.bInheritHandle = TRUE;
-
-	latch->event = CreateEvent(&sa, TRUE, FALSE, NULL);
-	if (latch->event == NULL)
-		elog(ERROR, "CreateEvent failed: error code %lu", GetLastError());
-#endif
-
-	latch->is_set = false;
-	latch->maybe_sleeping = false;
-	latch->owner_pid = 0;
-	latch->is_shared = true;
-}
-
-/*
- * Associate a shared latch with the current process, allowing it to
- * wait on the latch.
- *
- * Although there is a sanity check for latch-already-owned, we don't do
- * any sort of locking here, meaning that we could fail to detect the error
- * if two processes try to own the same latch at about the same time.  If
- * there is any risk of that, caller must provide an interlock to prevent it.
- */
-void
-OwnLatch(Latch *latch)
-{
-	int			owner_pid;
-
-	/* Sanity checks */
-	Assert(latch->is_shared);
-
-#if defined(WAIT_USE_SELF_PIPE)
-	/* Assert InitializeLatchSupport has been called in this process */
-	Assert(selfpipe_readfd >= 0 && selfpipe_owner_pid == MyProcPid);
-#elif defined(WAIT_USE_SIGNALFD)
-	/* Assert InitializeLatchSupport has been called in this process */
-	Assert(signal_fd >= 0);
-#endif
-
-	owner_pid = latch->owner_pid;
-	if (owner_pid != 0)
-		elog(PANIC, "latch already owned by PID %d", owner_pid);
-
-	latch->owner_pid = MyProcPid;
-}
-
-/*
- * Disown a shared latch currently owned by the current process.
- */
-void
-DisownLatch(Latch *latch)
-{
-	Assert(latch->is_shared);
-	Assert(latch->owner_pid == MyProcPid);
-
-	latch->owner_pid = 0;
-}
-
-/*
- * Wait for a given latch to be set, or for postmaster death, or until timeout
- * is exceeded. 'wakeEvents' is a bitmask that specifies which of those events
- * to wait for. If the latch is already set (and WL_LATCH_SET is given), the
- * function returns immediately.
+ * Wait for any of the interrupts in interruptMask to be set, or for
+ * postmaster death, or until timeout is exceeded. 'wakeEvents' is a bitmask
+ * that specifies which of those events to wait for. If the interrupt is
+ * already pending (and WL_INTERRUPT is given), the function returns
+ * immediately.
  *
  * The "timeout" is given in milliseconds. It must be >= 0 if WL_TIMEOUT flag
  * is given.  Although it is declared as "long", we don't actually support
  * timeouts longer than INT_MAX milliseconds.  Note that some extra overhead
  * is incurred when WL_TIMEOUT is given, so avoid using a timeout if possible.
  *
- * The latch must be owned by the current process, ie. it must be a
- * process-local latch initialized with InitLatch, or a shared latch
- * associated with the current process by calling OwnLatch.
- *
  * Returns bit mask indicating which condition(s) caused the wake-up. Note
  * that if multiple wake-up conditions are true, there is no guarantee that
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(Latch *latch, int wakeEvents, long timeout,
-		  uint32 wait_event_info)
+WaitInterrupt(uint32 interruptMask, int wakeEvents, long timeout,
+			  uint32 wait_event_info)
 {
 	WaitEvent	event;
 
@@ -525,17 +445,17 @@ WaitLatch(Latch *latch, int wakeEvents, long timeout,
 		   (wakeEvents & WL_POSTMASTER_DEATH));
 
 	/*
-	 * Some callers may have a latch other than MyLatch, or no latch at all,
-	 * or want to handle postmaster death differently.  It's cheap to assign
-	 * those, so just do it every time.
+	 * Some callers may have an interrupt mask different from last time, or no
+	 * interrupt mask at all, or want to handle postmaster death differently.
+	 * It's cheap to assign those, so just do it every time.
 	 */
-	if (!(wakeEvents & WL_LATCH_SET))
-		latch = NULL;
-	ModifyWaitEvent(LatchWaitSet, LatchWaitSetLatchPos, WL_LATCH_SET, latch);
-	LatchWaitSet->exit_on_postmaster_death =
+	if (!(wakeEvents & WL_INTERRUPT))
+		interruptMask = 0;
+	ModifyWaitEvent(InterruptWaitSet, InterruptWaitSetInterruptPos, WL_INTERRUPT, interruptMask);
+	InterruptWaitSet->exit_on_postmaster_death =
 		((wakeEvents & WL_EXIT_ON_PM_DEATH) != 0);
 
-	if (WaitEventSetWait(LatchWaitSet,
+	if (WaitEventSetWait(InterruptWaitSet,
 						 (wakeEvents & WL_TIMEOUT) ? timeout : -1,
 						 &event, 1,
 						 wait_event_info) == 0)
@@ -545,7 +465,7 @@ WaitLatch(Latch *latch, int wakeEvents, long timeout,
 }
 
 /*
- * Like WaitLatch, but with an extra socket argument for WL_SOCKET_*
+ * Like WaitInterrupt, but with an extra socket argument for WL_SOCKET_*
  * conditions.
  *
  * When waiting on a socket, EOF and error conditions always cause the socket
@@ -558,12 +478,12 @@ WaitLatch(Latch *latch, int wakeEvents, long timeout,
  * where some behavior other than immediate exit is needed.
  *
  * NB: These days this is just a wrapper around the WaitEventSet API. When
- * using a latch very frequently, consider creating a longer living
+ * using an interrupt very frequently, consider creating a longer living
  * WaitEventSet instead; that's more efficient.
  */
 int
-WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout, uint32 wait_event_info)
+WaitInterruptOrSocket(uint32 interruptMask, int wakeEvents, pgsocket sock,
+					  long timeout, uint32 wait_event_info)
 {
 	int			ret = 0;
 	int			rc;
@@ -575,9 +495,9 @@ WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
 	else
 		timeout = -1;
 
-	if (wakeEvents & WL_LATCH_SET)
-		AddWaitEventToSet(set, WL_LATCH_SET, PGINVALID_SOCKET,
-						  latch, NULL);
+	if (wakeEvents & WL_INTERRUPT)
+		AddWaitEventToSet(set, WL_INTERRUPT, PGINVALID_SOCKET,
+						  interruptMask, NULL);
 
 	/* Postmaster-managed callers must handle postmaster death somehow. */
 	Assert(!IsUnderPostmaster ||
@@ -586,18 +506,18 @@ WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
 
 	if ((wakeEvents & WL_POSTMASTER_DEATH) && IsUnderPostmaster)
 		AddWaitEventToSet(set, WL_POSTMASTER_DEATH, PGINVALID_SOCKET,
-						  NULL, NULL);
+						  0, NULL);
 
 	if ((wakeEvents & WL_EXIT_ON_PM_DEATH) && IsUnderPostmaster)
 		AddWaitEventToSet(set, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET,
-						  NULL, NULL);
+						  0, NULL);
 
 	if (wakeEvents & WL_SOCKET_MASK)
 	{
 		int			ev;
 
 		ev = wakeEvents & WL_SOCKET_MASK;
-		AddWaitEventToSet(set, ev, sock, NULL, NULL);
+		AddWaitEventToSet(set, ev, sock, 0, NULL);
 	}
 
 	rc = WaitEventSetWait(set, timeout, &event, 1, wait_event_info);
@@ -606,7 +526,7 @@ WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
 		ret |= WL_TIMEOUT;
 	else
 	{
-		ret |= event.events & (WL_LATCH_SET |
+		ret |= event.events & (WL_INTERRUPT |
 							   WL_POSTMASTER_DEATH |
 							   WL_SOCKET_MASK);
 	}
@@ -617,125 +537,47 @@ WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
 }
 
 /*
- * Sets a latch and wakes up anyone waiting on it.
- *
- * This is cheap if the latch is already set, otherwise not so much.
+ * Wake up my process if it's currently waiting.
  *
- * NB: when calling this in a signal handler, be sure to save and restore
- * errno around it.  (That's standard practice in most signal handlers, of
- * course, but we used to omit it in handlers that only set a flag.)
+ * NB: be sure to save and restore errno around it.  (That's standard practice
+ * in most signal handlers, of course, but we used to omit it in handlers that
+ * only set a flag.) XXX
  *
  * NB: this function is called from critical sections and signal handlers so
  * throwing an error is not a good idea.
  */
 void
-SetLatch(Latch *latch)
+WakeupMyProc(void)
 {
 #ifndef WIN32
-	pid_t		owner_pid;
-#else
-	HANDLE		handle;
-#endif
-
-	/*
-	 * The memory barrier has to be placed here to ensure that any flag
-	 * variables possibly changed by this process have been flushed to main
-	 * memory, before we check/set is_set.
-	 */
-	pg_memory_barrier();
-
-	/* Quick exit if already set */
-	if (latch->is_set)
-		return;
-
-	latch->is_set = true;
-
-	pg_memory_barrier();
-	if (!latch->maybe_sleeping)
-		return;
-
-#ifndef WIN32
-
-	/*
-	 * See if anyone's waiting for the latch. It can be the current process if
-	 * we're in a signal handler. We use the self-pipe or SIGURG to ourselves
-	 * to wake up WaitEventSetWaitBlock() without races in that case. If it's
-	 * another process, send a signal.
-	 *
-	 * Fetch owner_pid only once, in case the latch is concurrently getting
-	 * owned or disowned. XXX: This assumes that pid_t is atomic, which isn't
-	 * guaranteed to be true! In practice, the effective range of pid_t fits
-	 * in a 32 bit integer, and so should be atomic. In the worst case, we
-	 * might end up signaling the wrong process. Even then, you're very
-	 * unlucky if a process with that bogus pid exists and belongs to
-	 * Postgres; and PG database processes should handle excess SIGUSR1
-	 * interrupts without a problem anyhow.
-	 *
-	 * Another sort of race condition that's possible here is for a new
-	 * process to own the latch immediately after we look, so we don't signal
-	 * it. This is okay so long as all callers of ResetLatch/WaitLatch follow
-	 * the standard coding convention of waiting at the bottom of their loops,
-	 * not the top, so that they'll correctly process latch-setting events
-	 * that happen before they enter the loop.
-	 */
-	owner_pid = latch->owner_pid;
-	if (owner_pid == 0)
-		return;
-	else if (owner_pid == MyProcPid)
-	{
 #if defined(WAIT_USE_SELF_PIPE)
-		if (waiting)
-			sendSelfPipeByte();
+	if (waiting)
+		sendSelfPipeByte();
 #else
-		if (waiting)
-			kill(MyProcPid, SIGURG);
+	if (waiting)
+		kill(MyProcPid, SIGURG);
 #endif
-	}
-	else
-		kill(owner_pid, SIGURG);
-
 #else
-
-	/*
-	 * See if anyone's waiting for the latch. It can be the current process if
-	 * we're in a signal handler.
-	 *
-	 * Use a local variable here just in case somebody changes the event field
-	 * concurrently (which really should not happen).
-	 */
-	handle = latch->event;
-	if (handle)
-	{
-		SetEvent(handle);
-
-		/*
-		 * Note that we silently ignore any errors. We might be in a signal
-		 * handler or other critical path where it's not safe to call elog().
-		 */
-	}
+	SetEvent(MyProc ? MyProc->interruptWakeupEvent : LocalInterruptWakeupEvent);
 #endif
 }
 
 /*
- * Clear the latch. Calling WaitLatch after this will sleep, unless
- * the latch is set again before the WaitLatch call.
+ * Wake up another process if it's currently waiting.
  */
 void
-ResetLatch(Latch *latch)
+WakeupOtherProc(PGPROC *proc)
 {
-	/* Only the owner should reset the latch */
-	Assert(latch->owner_pid == MyProcPid);
-	Assert(latch->maybe_sleeping == false);
-
-	latch->is_set = false;
+#ifndef WIN32
+	kill(proc->pid, SIGURG);
+#else
+	SetEvent(proc->interruptWakeupEvent);
 
 	/*
-	 * Ensure that the write to is_set gets flushed to main memory before we
-	 * examine any flag variables.  Otherwise a concurrent SetLatch might
-	 * falsely conclude that it needn't signal us, even though we have missed
-	 * seeing some flag updates that SetLatch was supposed to inform us of.
+	 * Note that we silently ignore any errors. We might be in a signal
+	 * handler or other critical path where it's not safe to call elog().
 	 */
-	pg_memory_barrier();
+#endif
 }
 
 /*
@@ -799,7 +641,8 @@ CreateWaitEventSet(ResourceOwner resowner, int nevents)
 	data += MAXALIGN(sizeof(HANDLE) * nevents);
 #endif
 
-	set->latch = NULL;
+	set->interrupt_mask = 0;
+	set->interrupt_pos = -1;
 	set->nevents_space = nevents;
 	set->exit_on_postmaster_death = false;
 
@@ -890,9 +733,9 @@ FreeWaitEventSet(WaitEventSet *set)
 		 cur_event < (set->events + set->nevents);
 		 cur_event++)
 	{
-		if (cur_event->events & WL_LATCH_SET)
+		if (cur_event->events & WL_INTERRUPT)
 		{
-			/* uses the latch's HANDLE */
+			/* uses the process's wakeup HANDLE */
 		}
 		else if (cur_event->events & WL_POSTMASTER_DEATH)
 		{
@@ -929,7 +772,7 @@ FreeWaitEventSetAfterFork(WaitEventSet *set)
 
 /* ---
  * Add an event to the set. Possible events are:
- * - WL_LATCH_SET: Wait for the latch to be set
+ * - WL_INTERRUPT: Wait for the interrupt to be set
  * - WL_POSTMASTER_DEATH: Wait for postmaster to die
  * - WL_SOCKET_READABLE: Wait for socket to become readable,
  *	 can be combined in one event with other WL_SOCKET_* events
@@ -947,10 +790,6 @@ FreeWaitEventSetAfterFork(WaitEventSet *set)
  * Returns the offset in WaitEventSet->events (starting from 0), which can be
  * used to modify previously added wait events using ModifyWaitEvent().
  *
- * In the WL_LATCH_SET case the latch must be owned by the current process,
- * i.e. it must be a process-local latch initialized with InitLatch, or a
- * shared latch associated with the current process by calling OwnLatch.
- *
  * In the WL_SOCKET_READABLE/WRITEABLE/CONNECTED/ACCEPT cases, EOF and error
  * conditions cause the socket to be reported as readable/writable/connected,
  * so that the caller can deal with the condition.
@@ -960,7 +799,7 @@ FreeWaitEventSetAfterFork(WaitEventSet *set)
  * events.
  */
 int
-AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch,
+AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, uint32 interruptMask,
 				  void *user_data)
 {
 	WaitEvent  *event;
@@ -974,19 +813,16 @@ AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch,
 		set->exit_on_postmaster_death = true;
 	}
 
-	if (latch)
-	{
-		if (latch->owner_pid != MyProcPid)
-			elog(ERROR, "cannot wait on a latch owned by another process");
-		if (set->latch)
-			elog(ERROR, "cannot wait on more than one latch");
-		if ((events & WL_LATCH_SET) != WL_LATCH_SET)
-			elog(ERROR, "latch events only support being set");
-	}
-	else
+	/*
+	 * It doesn't make much sense to wait for WL_INTERRUPT with empty
+	 * interruptMask, but we allow it so that you can use ModifyEvent to set
+	 * the interruptMask later. Non-zero interruptMask doesn't make sense
+	 * without WL_INTERRUPT, however.
+	 */
+	if (interruptMask != 0)
 	{
-		if (events & WL_LATCH_SET)
-			elog(ERROR, "cannot wait on latch without a specified latch");
+		if ((events & WL_INTERRUPT) != WL_INTERRUPT)
+			elog(ERROR, "interrupted events only support being set");
 	}
 
 	/* waiting for socket readiness without a socket indicates a bug */
@@ -1002,10 +838,10 @@ AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch,
 	event->reset = false;
 #endif
 
-	if (events == WL_LATCH_SET)
+	if (events == WL_INTERRUPT)
 	{
-		set->latch = latch;
-		set->latch_pos = event->pos;
+		set->interrupt_mask = interruptMask;
+		set->interrupt_pos = event->pos;
 #if defined(WAIT_USE_SELF_PIPE)
 		event->fd = selfpipe_readfd;
 #elif defined(WAIT_USE_SIGNALFD)
@@ -1039,14 +875,14 @@ AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch,
 }
 
 /*
- * Change the event mask and, in the WL_LATCH_SET case, the latch associated
- * with the WaitEvent.  The latch may be changed to NULL to disable the latch
- * temporarily, and then set back to a latch later.
+ * Change the event mask and, in the WL_INTERRUPT case, the interrupt mask
+ * associated with the WaitEvent.  The interrupt mask may be changed to 0 to
+ * disable the wakeups on interrupts temporarily, and then set back later.
  *
  * 'pos' is the id returned by AddWaitEventToSet.
  */
 void
-ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch)
+ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, uint32 interruptMask)
 {
 	WaitEvent  *event;
 #if defined(WAIT_USE_KQUEUE)
@@ -1061,19 +897,19 @@ ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch)
 #endif
 
 	/*
-	 * If neither the event mask nor the associated latch changes, return
-	 * early. That's an important optimization for some sockets, where
+	 * If neither the event mask nor the associated interrupt mask changes,
+	 * return early. That's an important optimization for some sockets, where
 	 * ModifyWaitEvent is frequently used to switch from waiting for reads to
 	 * waiting on writes.
 	 */
 	if (events == event->events &&
-		(!(event->events & WL_LATCH_SET) || set->latch == latch))
+		(!(event->events & WL_INTERRUPT) || set->interrupt_mask == interruptMask))
 		return;
 
-	if (event->events & WL_LATCH_SET &&
+	if (event->events & WL_INTERRUPT &&
 		events != event->events)
 	{
-		elog(ERROR, "cannot modify latch event");
+		elog(ERROR, "cannot modify interrupts event");
 	}
 
 	if (event->events & WL_POSTMASTER_DEATH)
@@ -1084,25 +920,19 @@ ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch)
 	/* FIXME: validate event mask */
 	event->events = events;
 
-	if (events == WL_LATCH_SET)
+	if (events == WL_INTERRUPT)
 	{
-		if (latch && latch->owner_pid != MyProcPid)
-			elog(ERROR, "cannot wait on a latch owned by another process");
-		set->latch = latch;
+		set->interrupt_mask = interruptMask;
 
 		/*
-		 * On Unix, we don't need to modify the kernel object because the
-		 * underlying pipe (if there is one) is the same for all latches so we
-		 * can return immediately.  On Windows, we need to update our array of
-		 * handles, but we leave the old one in place and tolerate spurious
-		 * wakeups if the latch is disabled.
+		 * We don't bother to adjust the event when the interrupt mask
+		 * changes.  It means that when interrupt mask is set to 0, we will
+		 * listen on the kernel object unnecessarily, and might get some
+		 * spurious wakeups. The interrupt sending code should not wakes us up
+		 * if the maybeSleepingOnInterrupts is zero, though, so it should be
+		 * rare.
 		 */
-#if defined(WAIT_USE_WIN32)
-		if (!latch)
-			return;
-#else
 		return;
-#endif
 	}
 
 #if defined(WAIT_USE_EPOLL)
@@ -1132,9 +962,8 @@ WaitEventAdjustEpoll(WaitEventSet *set, WaitEvent *event, int action)
 	epoll_ev.events = EPOLLERR | EPOLLHUP;
 
 	/* prepare pollfd entry once */
-	if (event->events == WL_LATCH_SET)
+	if (event->events == WL_INTERRUPT)
 	{
-		Assert(set->latch != NULL);
 		epoll_ev.events |= EPOLLIN;
 	}
 	else if (event->events == WL_POSTMASTER_DEATH)
@@ -1181,9 +1010,8 @@ WaitEventAdjustPoll(WaitEventSet *set, WaitEvent *event)
 	pollfd->fd = event->fd;
 
 	/* prepare pollfd entry once */
-	if (event->events == WL_LATCH_SET)
+	if (event->events == WL_INTERRUPT)
 	{
-		Assert(set->latch != NULL);
 		pollfd->events = POLLIN;
 	}
 	else if (event->events == WL_POSTMASTER_DEATH)
@@ -1245,9 +1073,9 @@ WaitEventAdjustKqueueAddPostmaster(struct kevent *k_ev, WaitEvent *event)
 }
 
 static inline void
-WaitEventAdjustKqueueAddLatch(struct kevent *k_ev, WaitEvent *event)
+WaitEventAdjustKqueueAddInterruptWakeup(struct kevent *k_ev, WaitEvent *event)
 {
-	/* For now latch can only be added, not removed. */
+	/* For now interrupt wakeup can only be added, not removed. */
 	k_ev->ident = SIGURG;
 	k_ev->filter = EVFILT_SIGNAL;
 	k_ev->flags = EV_ADD;
@@ -1273,8 +1101,7 @@ WaitEventAdjustKqueue(WaitEventSet *set, WaitEvent *event, int old_events)
 	if (old_events == event->events)
 		return;
 
-	Assert(event->events != WL_LATCH_SET || set->latch != NULL);
-	Assert(event->events == WL_LATCH_SET ||
+	Assert(event->events == WL_INTERRUPT ||
 		   event->events == WL_POSTMASTER_DEATH ||
 		   (event->events & (WL_SOCKET_READABLE |
 							 WL_SOCKET_WRITEABLE |
@@ -1289,10 +1116,10 @@ WaitEventAdjustKqueue(WaitEventSet *set, WaitEvent *event, int old_events)
 		 */
 		WaitEventAdjustKqueueAddPostmaster(&k_ev[count++], event);
 	}
-	else if (event->events == WL_LATCH_SET)
+	else if (event->events == WL_INTERRUPT)
 	{
-		/* We detect latch wakeup using a signal event. */
-		WaitEventAdjustKqueueAddLatch(&k_ev[count++], event);
+		/* We detect interrupt wakeup using a signal event. */
+		WaitEventAdjustKqueueAddInterruptWakeup(&k_ev[count++], event);
 	}
 	else
 	{
@@ -1370,10 +1197,13 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
 {
 	HANDLE	   *handle = &set->handles[event->pos + 1];
 
-	if (event->events == WL_LATCH_SET)
+	if (event->events == WL_INTERRUPT)
 	{
-		Assert(set->latch != NULL);
-		*handle = set->latch->event;
+		/*
+		 * We register the event even if interrupt_mask is zero. We might get
+		 * some spurious wakeups, but that's OK
+		 */
+		*handle = MyProc ? MyProc->interruptWakeupEvent : LocalInterruptWakeupEvent;
 	}
 	else if (event->events == WL_POSTMASTER_DEATH)
 	{
@@ -1450,7 +1280,7 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 #ifndef WIN32
 	waiting = true;
 #else
-	/* Ensure that signals are serviced even if latch is already set */
+	/* Ensure that signals are serviced even if interrupt is already pending */
 	pgwin32_dispatch_queued_signals();
 #endif
 	while (returned_events == 0)
@@ -1458,52 +1288,52 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 		int			rc;
 
 		/*
-		 * Check if the latch is set already. If so, leave the loop
+		 * Check if the interrupt is already pending. If so, leave the loop
 		 * immediately, avoid blocking again. We don't attempt to report any
 		 * other events that might also be satisfied.
 		 *
-		 * If someone sets the latch between this and the
+		 * If someone sets the interrupt flag between this and the
 		 * WaitEventSetWaitBlock() below, the setter will write a byte to the
 		 * pipe (or signal us and the signal handler will do that), and the
 		 * readiness routine will return immediately.
 		 *
 		 * On unix, If there's a pending byte in the self pipe, we'll notice
 		 * whenever blocking. Only clearing the pipe in that case avoids
-		 * having to drain it every time WaitLatchOrSocket() is used. Should
-		 * the pipe-buffer fill up we're still ok, because the pipe is in
-		 * nonblocking mode. It's unlikely for that to happen, because the
+		 * having to drain it every time WaitInterruptOrSocket() is used.
+		 * Should the pipe-buffer fill up we're still ok, because the pipe is
+		 * in nonblocking mode. It's unlikely for that to happen, because the
 		 * self pipe isn't filled unless we're blocking (waiting = true), or
-		 * from inside a signal handler in latch_sigurg_handler().
+		 * from inside a signal handler in interrupt_sigurg_handler().
 		 *
 		 * On windows, we'll also notice if there's a pending event for the
-		 * latch when blocking, but there's no danger of anything filling up,
-		 * as "Setting an event that is already set has no effect.".
+		 * interrupt when blocking, but there's no danger of anything filling
+		 * up, as "Setting an event that is already set has no effect.".
 		 *
-		 * Note: we assume that the kernel calls involved in latch management
-		 * will provide adequate synchronization on machines with weak memory
-		 * ordering, so that we cannot miss seeing is_set if a notification
-		 * has already been queued.
+		 * Note: we assume that the kernel calls involved in interrupt
+		 * management will provide adequate synchronization on machines with
+		 * weak memory ordering, so that we cannot miss seeing is_set if a
+		 * notification has already been queued.
 		 */
-		if (set->latch && !set->latch->is_set)
+		if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) == 0)
 		{
-			/* about to sleep on a latch */
-			set->latch->maybe_sleeping = true;
+			/* about to sleep waiting for interrupts */
+			pg_atomic_write_u32(MyMaybeSleepingOnInterrupts, set->interrupt_mask);
 			pg_memory_barrier();
 			/* and recheck */
 		}
 
-		if (set->latch && set->latch->is_set)
+		if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
 		{
 			occurred_events->fd = PGINVALID_SOCKET;
-			occurred_events->pos = set->latch_pos;
+			occurred_events->pos = set->interrupt_pos;
 			occurred_events->user_data =
-				set->events[set->latch_pos].user_data;
-			occurred_events->events = WL_LATCH_SET;
+				set->events[set->interrupt_pos].user_data;
+			occurred_events->events = WL_INTERRUPT;
 			occurred_events++;
 			returned_events++;
 
 			/* could have been set above */
-			set->latch->maybe_sleeping = false;
+			pg_atomic_write_u32(MyMaybeSleepingOnInterrupts, 0);
 
 			break;
 		}
@@ -1516,10 +1346,10 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 		rc = WaitEventSetWaitBlock(set, cur_timeout,
 								   occurred_events, nevents);
 
-		if (set->latch)
+		if (set->interrupt_mask != 0)
 		{
-			Assert(set->latch->maybe_sleeping);
-			set->latch->maybe_sleeping = false;
+			Assert(pg_atomic_read_u32(MyMaybeSleepingOnInterrupts) == set->interrupt_mask);
+			pg_atomic_write_u32(MyMaybeSleepingOnInterrupts, 0);
 		}
 
 		if (rc == -1)
@@ -1607,16 +1437,16 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 		occurred_events->user_data = cur_event->user_data;
 		occurred_events->events = 0;
 
-		if (cur_event->events == WL_LATCH_SET &&
+		if (cur_event->events == WL_INTERRUPT &&
 			cur_epoll_event->events & (EPOLLIN | EPOLLERR | EPOLLHUP))
 		{
 			/* Drain the signalfd. */
 			drain();
 
-			if (set->latch && set->latch->is_set)
+			if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
 			{
 				occurred_events->fd = PGINVALID_SOCKET;
-				occurred_events->events = WL_LATCH_SET;
+				occurred_events->events = WL_INTERRUPT;
 				occurred_events++;
 				returned_events++;
 			}
@@ -1769,13 +1599,13 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 		occurred_events->user_data = cur_event->user_data;
 		occurred_events->events = 0;
 
-		if (cur_event->events == WL_LATCH_SET &&
+		if (cur_event->events == WL_INTERRUPT &&
 			cur_kqueue_event->filter == EVFILT_SIGNAL)
 		{
-			if (set->latch && set->latch->is_set)
+			if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
 			{
 				occurred_events->fd = PGINVALID_SOCKET;
-				occurred_events->events = WL_LATCH_SET;
+				occurred_events->events = WL_INTERRUPT;
 				occurred_events++;
 				returned_events++;
 			}
@@ -1891,16 +1721,16 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 		occurred_events->user_data = cur_event->user_data;
 		occurred_events->events = 0;
 
-		if (cur_event->events == WL_LATCH_SET &&
+		if (cur_event->events == WL_INTERRUPT &&
 			(cur_pollfd->revents & (POLLIN | POLLHUP | POLLERR | POLLNVAL)))
 		{
 			/* There's data in the self-pipe, clear it. */
 			drain();
 
-			if (set->latch && set->latch->is_set)
+			if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
 			{
 				occurred_events->fd = PGINVALID_SOCKET;
-				occurred_events->events = WL_LATCH_SET;
+				occurred_events->events = WL_INTERRUPT;
 				occurred_events++;
 				returned_events++;
 			}
@@ -1999,6 +1829,15 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 			cur_event->reset = false;
 		}
 
+		/*
+		 * We need to use different event object depending on whether "local"
+		 * or "shared memory" interrupts are in use. There's no easy way to
+		 * adjust all existing WaitEventSet when you switch from local to
+		 * shared or back, so we refresh it on every call.
+		 */
+		if (cur_event->events & WL_INTERRUPT)
+			WaitEventAdjustWin32(set, cur_event);
+
 		/*
 		 * We associate the socket with a new event handle for each
 		 * WaitEventSet.  FD_CLOSE is only generated once if the other end
@@ -2104,19 +1943,15 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 		occurred_events->user_data = cur_event->user_data;
 		occurred_events->events = 0;
 
-		if (cur_event->events == WL_LATCH_SET)
+		if (cur_event->events == WL_INTERRUPT)
 		{
-			/*
-			 * We cannot use set->latch->event to reset the fired event if we
-			 * aren't waiting on this latch now.
-			 */
 			if (!ResetEvent(set->handles[cur_event->pos + 1]))
 				elog(ERROR, "ResetEvent failed: error code %lu", GetLastError());
 
-			if (set->latch && set->latch->is_set)
+			if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
 			{
 				occurred_events->fd = PGINVALID_SOCKET;
-				occurred_events->events = WL_LATCH_SET;
+				occurred_events->events = WL_INTERRUPT;
 				occurred_events++;
 				returned_events++;
 			}
@@ -2161,7 +1996,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 
 				/*------
 				 * WaitForMultipleObjects doesn't guarantee that a read event
-				 * will be returned if the latch is set at the same time.  Even
+				 * will be returned if the interrupt is set at the same time.  Even
 				 * if it did, the caller might drop that event expecting it to
 				 * reoccur on next call.  So, we must force the event to be
 				 * reset if this WaitEventSet is used again in order to avoid
@@ -2267,18 +2102,17 @@ GetNumRegisteredWaitEvents(WaitEventSet *set)
 #if defined(WAIT_USE_SELF_PIPE)
 
 /*
- * SetLatch uses SIGURG to wake up the process waiting on the latch.
- *
- * Wake up WaitLatch, if we're waiting.
+ * WakeupOtherProc and WakupMyProc use SIGURG to wake up the process waiting
+ * for an interrupt
  */
 static void
-latch_sigurg_handler(SIGNAL_ARGS)
+interrupt_sigurg_handler(SIGNAL_ARGS)
 {
 	if (waiting)
 		sendSelfPipeByte();
 }
 
-/* Send one byte to the self-pipe, to wake up WaitLatch */
+/* Send one byte to the self-pipe, to wake up WaitInterrupt */
 static void
 sendSelfPipeByte(void)
 {
@@ -2295,7 +2129,7 @@ retry:
 
 		/*
 		 * If the pipe is full, we don't need to retry, the data that's there
-		 * already is enough to wake up WaitLatch.
+		 * already is enough to wake up WaitInterrupt.
 		 */
 		if (errno == EAGAIN || errno == EWOULDBLOCK)
 			return;
diff --git a/src/backend/storage/lmgr/condition_variable.c b/src/backend/storage/lmgr/condition_variable.c
index 112a518bae0..9af29706606 100644
--- a/src/backend/storage/lmgr/condition_variable.c
+++ b/src/backend/storage/lmgr/condition_variable.c
@@ -21,6 +21,7 @@
 #include "miscadmin.h"
 #include "portability/instr_time.h"
 #include "storage/condition_variable.h"
+#include "storage/interrupt.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -147,23 +148,23 @@ ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
 		INSTR_TIME_SET_CURRENT(start_time);
 		Assert(timeout >= 0 && timeout <= INT_MAX);
 		cur_timeout = timeout;
-		wait_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
+		wait_events = WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
 	}
 	else
-		wait_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
+		wait_events = WL_INTERRUPT | WL_EXIT_ON_PM_DEATH;
 
 	while (true)
 	{
 		bool		done = false;
 
 		/*
-		 * Wait for latch to be set.  (If we're awakened for some other
-		 * reason, the code below will cope anyway.)
+		 * Wait for interrupt.  (If we're awakened for some other reason, the
+		 * code below will cope anyway.)
 		 */
-		(void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, wait_events, cur_timeout, wait_event_info);
 
-		/* Reset latch before examining the state of the wait list. */
-		ResetLatch(MyLatch);
+		/* Clear the flag before examining the state of the wait list. */
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/*
 		 * If this process has been taken out of the wait list, then we know
@@ -176,9 +177,10 @@ ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
 		 * the wait list only when the caller calls
 		 * ConditionVariableCancelSleep.
 		 *
-		 * If we're still in the wait list, then the latch must have been set
-		 * by something other than ConditionVariableSignal; though we don't
-		 * guarantee not to return spuriously, we'll avoid this obvious case.
+		 * If we're still in the wait list, then the interrupt must have been
+		 * sent by something other than ConditionVariableSignal; though we
+		 * don't guarantee not to return spuriously, we'll avoid this obvious
+		 * case.
 		 */
 		SpinLockAcquire(&cv->mutex);
 		if (!proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
@@ -266,9 +268,9 @@ ConditionVariableSignal(ConditionVariable *cv)
 		proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
 	SpinLockRelease(&cv->mutex);
 
-	/* If we found someone sleeping, set their latch to wake them up. */
+	/* If we found someone sleeping, wake them up. */
 	if (proc != NULL)
-		SetLatch(&proc->procLatch);
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(proc));
 }
 
 /*
@@ -297,8 +299,8 @@ ConditionVariableBroadcast(ConditionVariable *cv)
 	 * CV and in doing so remove our sentinel entry.  But that's fine: since
 	 * CV waiters are always added and removed in order, that must mean that
 	 * every previous waiter has been wakened, so we're done.  We'll get an
-	 * extra "set" on our latch from the someone else's signal, which is
-	 * slightly inefficient but harmless.
+	 * extra interrupt from the someone else's signal, which is slightly
+	 * inefficient but harmless.
 	 *
 	 * We can't insert our cvWaitLink as a sentinel if it's already in use in
 	 * some other proclist.  While that's not expected to be true for typical
@@ -331,7 +333,7 @@ ConditionVariableBroadcast(ConditionVariable *cv)
 
 	/* Awaken first waiter, if there was one. */
 	if (proc != NULL)
-		SetLatch(&proc->procLatch);
+		SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(proc));
 
 	while (have_sentinel)
 	{
@@ -355,6 +357,6 @@ ConditionVariableBroadcast(ConditionVariable *cv)
 		SpinLockRelease(&cv->mutex);
 
 		if (proc != NULL && proc != MyProc)
-			SetLatch(&proc->procLatch);
+			SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(proc));
 	}
 }
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 2030322f957..eff314dd05d 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -207,6 +207,7 @@
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/pg_lfind.h"
+#include "storage/interrupt.h"
 #include "storage/predicate.h"
 #include "storage/predicate_internals.h"
 #include "storage/proc.h"
@@ -1576,7 +1577,10 @@ GetSafeSnapshot(Snapshot origSnapshot)
 				 SxactIsROUnsafe(MySerializableXact)))
 		{
 			LWLockRelease(SerializableXactHashLock);
-			ProcWaitForSignal(WAIT_EVENT_SAFE_SNAPSHOT);
+			WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+						  WAIT_EVENT_SAFE_SNAPSHOT);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
+			CHECK_FOR_INTERRUPTS();
 			LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
 		}
 		MySerializableXact->flags &= ~SXACT_FLAG_DEFERRABLE_WAITING;
@@ -3609,7 +3613,7 @@ ReleasePredicateLocks(bool isCommit, bool isReadOnlySafe)
 			 */
 			if (SxactIsDeferrableWaiting(roXact) &&
 				(SxactIsROUnsafe(roXact) || SxactIsROSafe(roXact)))
-				ProcSendSignal(roXact->pgprocno);
+				SendInterrupt(INTERRUPT_GENERAL_WAKEUP, roXact->pgprocno);
 		}
 	}
 
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 720ef99ee83..0393bbb3828 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -42,6 +42,7 @@
 #include "replication/slotsync.h"
 #include "replication/syncrep.h"
 #include "storage/condition_variable.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
 #include "storage/pmsignal.h"
@@ -257,14 +258,28 @@ InitProcGlobal(void)
 		Assert(fpPtr <= fpEndPtr);
 
 		/*
-		 * Set up per-PGPROC semaphore, latch, and fpInfoLock.  Prepared xact
-		 * dummy PGPROCs don't need these though - they're never associated
-		 * with a real process
+		 * Set up per-PGPROC semaphore, interrupt wakeup event (on Windows),
+		 * and fpInfoLock.  Prepared xact dummy PGPROCs don't need these
+		 * though - they're never associated with a real process
 		 */
 		if (i < MaxBackends + NUM_AUXILIARY_PROCS)
 		{
+#ifdef WIN32
+			SECURITY_ATTRIBUTES sa;
+
+			/*
+			 * Set up security attributes to specify that the events are
+			 * inherited.
+			 */
+			ZeroMemory(&sa, sizeof(sa));
+			sa.nLength = sizeof(sa);
+			sa.bInheritHandle = TRUE;
+
+			proc->interruptWakeupEvent = CreateEvent(&sa, TRUE, FALSE, NULL);
+			if (proc->interruptWakeupEvent == NULL)
+				elog(ERROR, "CreateEvent failed: error code %lu", GetLastError());
+#endif
 			proc->sem = PGSemaphoreCreate();
-			InitSharedLatch(&(proc->procLatch));
 			LWLockInitialize(&(proc->fpInfoLock), LWTRANCHE_LOCK_FASTPATH);
 		}
 
@@ -474,13 +489,8 @@ InitProcess(void)
 	MyProc->clogGroupMemberLsn = InvalidXLogRecPtr;
 	Assert(pg_atomic_read_u32(&MyProc->clogGroupNext) == INVALID_PROC_NUMBER);
 
-	/*
-	 * Acquire ownership of the PGPROC's latch, so that we can use WaitLatch
-	 * on it.  That allows us to repoint the process latch, which so far
-	 * points to process local one, to the shared one.
-	 */
-	OwnLatch(&MyProc->procLatch);
-	SwitchToSharedLatch();
+	/* Start accepting interrupts from other processes */
+	SwitchToSharedInterrupts();
 
 	/* now that we have a proc, report wait events to shared memory */
 	pgstat_set_wait_event_storage(&MyProc->wait_event_info);
@@ -644,13 +654,8 @@ InitAuxiliaryProcess(void)
 	}
 #endif
 
-	/*
-	 * Acquire ownership of the PGPROC's latch, so that we can use WaitLatch
-	 * on it.  That allows us to repoint the process latch, which so far
-	 * points to process local one, to the shared one.
-	 */
-	OwnLatch(&MyProc->procLatch);
-	SwitchToSharedLatch();
+	/* Start accepting interrupts from other processes */
+	SwitchToSharedInterrupts();
 
 	/* now that we have a proc, report wait events to shared memory */
 	pgstat_set_wait_event_storage(&MyProc->wait_event_info);
@@ -927,21 +932,20 @@ ProcKill(int code, Datum arg)
 	}
 
 	/*
-	 * Reset MyLatch to the process local one.  This is so that signal
-	 * handlers et al can continue using the latch after the shared latch
-	 * isn't ours anymore.
+	 * Reset interrupt vector to the process local one.  This is so that
+	 * signal handlers et al can continue using interrupts after the PGPROC
+	 * entry isn't ours anymore.
 	 *
 	 * Similarly, stop reporting wait events to MyProc->wait_event_info.
 	 *
-	 * After that clear MyProc and disown the shared latch.
+	 * After that clear MyProc.
 	 */
-	SwitchBackToLocalLatch();
+	SwitchToLocalInterrupts();
 	pgstat_reset_wait_event_storage();
 
 	proc = MyProc;
 	MyProc = NULL;
 	MyProcNumber = INVALID_PROC_NUMBER;
-	DisownLatch(&proc->procLatch);
 
 	/* Mark the proc no longer in use */
 	proc->pid = 0;
@@ -1004,13 +1008,12 @@ AuxiliaryProcKill(int code, Datum arg)
 	ConditionVariableCancelSleep();
 
 	/* look at the equivalent ProcKill() code for comments */
-	SwitchBackToLocalLatch();
+	SwitchToLocalInterrupts();
 	pgstat_reset_wait_event_storage();
 
 	proc = MyProc;
 	MyProc = NULL;
 	MyProcNumber = INVALID_PROC_NUMBER;
-	DisownLatch(&proc->procLatch);
 
 	SpinLockAcquire(ProcStructLock);
 
@@ -1337,18 +1340,18 @@ ProcSleep(LOCALLOCK *locallock)
 	}
 
 	/*
-	 * If somebody wakes us between LWLockRelease and WaitLatch, the latch
-	 * will not wait. But a set latch does not necessarily mean that the lock
-	 * is free now, as there are many other sources for latch sets than
-	 * somebody releasing the lock.
+	 * If somebody wakes us between LWLockRelease and WaitInterrupt,
+	 * WaitInterrupt will not wait. But an interrupt does not necessarily mean
+	 * that the lock is free now, as there are many other sources for the
+	 * interrupt than somebody releasing the lock.
 	 *
-	 * We process interrupts whenever the latch has been set, so cancel/die
-	 * interrupts are processed quickly. This means we must not mind losing
-	 * control to a cancel/die interrupt here.  We don't, because we have no
-	 * shared-state-change work to do after being granted the lock (the
-	 * grantor did it all).  We do have to worry about canceling the deadlock
-	 * timeout and updating the locallock table, but if we lose control to an
-	 * error, LockErrorCleanup will fix that up.
+	 * We process interrupts whenever the interrupt has been set, so
+	 * cancel/die interrupts are processed quickly. This means we must not
+	 * mind losing control to a cancel/die interrupt here.  We don't, because
+	 * we have no shared-state-change work to do after being granted the lock
+	 * (the grantor did it all).  We do have to worry about canceling the
+	 * deadlock timeout and updating the locallock table, but if we lose
+	 * control to an error, LockErrorCleanup will fix that up.
 	 */
 	do
 	{
@@ -1394,9 +1397,9 @@ ProcSleep(LOCALLOCK *locallock)
 		}
 		else
 		{
-			(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
-							 PG_WAIT_LOCK | locallock->tag.lock.locktag_type);
-			ResetLatch(MyLatch);
+			(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+								 PG_WAIT_LOCK | locallock->tag.lock.locktag_type);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
 			{
@@ -1688,7 +1691,7 @@ ProcSleep(LOCALLOCK *locallock)
 
 
 /*
- * ProcWakeup -- wake up a process by setting its latch.
+ * ProcWakeup -- wake up a process by sending it an interrupt.
  *
  *	 Also remove the process from the wait queue and set its links invalid.
  *
@@ -1717,7 +1720,7 @@ ProcWakeup(PGPROC *proc, ProcWaitStatus waitStatus)
 	pg_atomic_write_u64(&MyProc->waitStart, 0);
 
 	/* And awaken it */
-	SetLatch(&proc->procLatch);
+	SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(proc));
 }
 
 /*
@@ -1869,45 +1872,19 @@ CheckDeadLockAlert(void)
 	got_deadlock_timeout = true;
 
 	/*
-	 * Have to set the latch again, even if handle_sig_alarm already did. Back
-	 * then got_deadlock_timeout wasn't yet set... It's unlikely that this
-	 * ever would be a problem, but setting a set latch again is cheap.
+	 * Have to raise the interrupt again, even if handle_sig_alarm already
+	 * did. Back then got_deadlock_timeout wasn't yet set... It's unlikely
+	 * that this ever would be a problem, but raising an interrupt again is
+	 * cheap.
 	 *
 	 * Note that, when this function runs inside procsignal_sigusr1_handler(),
-	 * the handler function sets the latch again after the latch is set here.
+	 * the handler function raises the interrupt again after the interrupt is
+	 * raised here.
 	 */
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	errno = save_errno;
 }
 
-/*
- * ProcWaitForSignal - wait for a signal from another backend.
- *
- * As this uses the generic process latch the caller has to be robust against
- * unrelated wakeups: Always check that the desired state has occurred, and
- * wait again if not.
- */
-void
-ProcWaitForSignal(uint32 wait_event_info)
-{
-	(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
-					 wait_event_info);
-	ResetLatch(MyLatch);
-	CHECK_FOR_INTERRUPTS();
-}
-
-/*
- * ProcSendSignal - set the latch of a backend identified by ProcNumber
- */
-void
-ProcSendSignal(ProcNumber procNumber)
-{
-	if (procNumber < 0 || procNumber >= ProcGlobal->allProcCount)
-		elog(ERROR, "procNumber out of range");
-
-	SetLatch(&ProcGlobal->allProcs[procNumber].procLatch);
-}
-
 /*
  * BecomeLockGroupLeader - designate process as lock group leader
  *
diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c
index ab7137d0fff..bec42f40601 100644
--- a/src/backend/storage/sync/sync.c
+++ b/src/backend/storage/sync/sync.c
@@ -27,7 +27,7 @@
 #include "portability/instr_time.h"
 #include "postmaster/bgwriter.h"
 #include "storage/fd.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
 #include "storage/md.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -611,8 +611,8 @@ RegisterSyncRequest(const FileTag *ftag, SyncRequestType type,
 		if (ret || (!ret && !retryOnError))
 			break;
 
-		WaitLatch(NULL, WL_EXIT_ON_PM_DEATH | WL_TIMEOUT, 10,
-				  WAIT_EVENT_REGISTER_SYNC_REQUEST);
+		WaitInterrupt(0, WL_EXIT_ON_PM_DEATH | WL_TIMEOUT, 10,
+					  WAIT_EVENT_REGISTER_SYNC_REQUEST);
 	}
 
 	return ret;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 184b8301687..b8d8312ef9d 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -61,6 +61,7 @@
 #include "replication/walsender.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/bufmgr.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
@@ -531,15 +532,15 @@ ProcessClientReadInterrupt(bool blocked)
 		/*
 		 * We're dying.  If there is no data available to read, then it's safe
 		 * (and sane) to handle that now.  If we haven't tried to read yet,
-		 * make sure the process latch is set, so that if there is no data
+		 * make sure the interrupt flag is set, so that if there is no data
 		 * then we'll come back here and die.  If we're done reading, also
-		 * make sure the process latch is set, as we might've undesirably
+		 * make sure the interrupt flag is set, as we might've undesirably
 		 * cleared it while reading.
 		 */
 		if (blocked)
 			CHECK_FOR_INTERRUPTS();
 		else
-			SetLatch(MyLatch);
+			RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 
 	errno = save_errno;
@@ -565,9 +566,9 @@ ProcessClientWriteInterrupt(bool blocked)
 		 * We're dying.  If it's not possible to write, then we should handle
 		 * that immediately, else a stuck client could indefinitely delay our
 		 * response to the signal.  If we haven't tried to write yet, make
-		 * sure the process latch is set, so that if the write would block
+		 * sure the interrupt flag is set, so that if the write would block
 		 * then we'll come back here and die.  If we're done writing, also
-		 * make sure the process latch is set, as we might've undesirably
+		 * make sure the interrupt flag is set, as we might've undesirably
 		 * cleared it while writing.
 		 */
 		if (blocked)
@@ -591,7 +592,7 @@ ProcessClientWriteInterrupt(bool blocked)
 			}
 		}
 		else
-			SetLatch(MyLatch);
+			RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 
 	errno = save_errno;
@@ -3013,12 +3014,12 @@ die(SIGNAL_ARGS)
 	/* for the cumulative stats system */
 	pgStatSessionEndCause = DISCONNECT_KILLED;
 
-	/* If we're still here, waken anything waiting on the process latch */
-	SetLatch(MyLatch);
+	/* If we're still here, waken anything waiting on the interrupt */
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 	/*
 	 * If we're in single user mode, we want to quit immediately - we can't
-	 * rely on latches as they wouldn't work when stdin/stdout is a file.
+	 * rely on interrupts as they wouldn't work when stdin/stdout is a file.
 	 * Rather ugly, but it's unlikely to be worthwhile to invest much more
 	 * effort just for the benefit of single user mode.
 	 */
@@ -3042,8 +3043,8 @@ StatementCancelHandler(SIGNAL_ARGS)
 		QueryCancelPending = true;
 	}
 
-	/* If we're still here, waken anything waiting on the process latch */
-	SetLatch(MyLatch);
+	/* If we're still here, waken anything waiting on the interrupt */
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /* signal handler for floating point exception */
@@ -3069,7 +3070,7 @@ HandleRecoveryConflictInterrupt(ProcSignalReason reason)
 	RecoveryConflictPendingReasons[reason] = true;
 	RecoveryConflictPending = true;
 	InterruptPending = true;
-	/* latch will be set by procsignal_sigusr1_handler */
+	/* INTERRUPT_GENERAL_WAKEUP will be raised by procsignal_sigusr1_handler */
 }
 
 /*
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 1aa8bbb3063..8313a49fc6c 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -38,7 +38,7 @@
 #include "postmaster/syslogger.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/fd.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
@@ -373,16 +373,16 @@ pg_sleep(PG_FUNCTION_ARGS)
 	float8		endtime;
 
 	/*
-	 * We sleep using WaitLatch, to ensure that we'll wake up promptly if an
-	 * important signal (such as SIGALRM or SIGINT) arrives.  Because
-	 * WaitLatch's upper limit of delay is INT_MAX milliseconds, and the user
-	 * might ask for more than that, we sleep for at most 10 minutes and then
-	 * loop.
+	 * We sleep using WaitInterrupt, to ensure that we'll wake up promptly if
+	 * an important signal (such as SIGALRM or SIGINT) arrives.  Because
+	 * WaitInterrupt's upper limit of delay is INT_MAX milliseconds, and the
+	 * user might ask for more than that, we sleep for at most 10 minutes and
+	 * then loop.
 	 *
 	 * By computing the intended stop time initially, we avoid accumulation of
 	 * extra delay across multiple sleeps.  This also ensures we won't delay
-	 * less than the specified time when WaitLatch is terminated early by a
-	 * non-query-canceling signal such as SIGHUP.
+	 * less than the specified time when WaitInterrupt is terminated early by
+	 * a non-query-canceling signal such as SIGHUP.
 	 */
 #define GetNowFloat()	((float8) GetCurrentTimestamp() / 1000000.0)
 
@@ -403,11 +403,11 @@ pg_sleep(PG_FUNCTION_ARGS)
 		else
 			break;
 
-		(void) WaitLatch(MyLatch,
-						 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-						 delay_ms,
-						 WAIT_EVENT_PG_SLEEP);
-		ResetLatch(MyLatch);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 delay_ms,
+							 WAIT_EVENT_PG_SLEEP);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 	}
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index e00cd3c9690..f3e3cafc098 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -1738,10 +1738,10 @@ TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
  * TimestampDifferenceMilliseconds -- convert the difference between two
  * 		timestamps into integer milliseconds
  *
- * This is typically used to calculate a wait timeout for WaitLatch()
+ * This is typically used to calculate a wait timeout for WaitInterrupt()
  * or a related function.  The choice of "long" as the result type
  * is to harmonize with that; furthermore, we clamp the result to at most
- * INT_MAX milliseconds, because that's all that WaitLatch() allows.
+ * INT_MAX milliseconds, because that's all that WaitInterrupt() allows.
  *
  * We expect start_time <= stop_time.  If not, we return zero,
  * since then we're already past the previously determined stop_time.
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 03a54451ac2..cb5343d28ca 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -52,15 +52,6 @@ bool		MyCancelKeyValid = false;
 int32		MyCancelKey = 0;
 int			MyPMChildSlot;
 
-/*
- * MyLatch points to the latch that should be used for signal handling by the
- * current process. It will either point to a process local latch if the
- * current process does not have a PGPROC entry in that moment, or to
- * PGPROC->procLatch if it has. Thus it can always be used in signal handlers,
- * without checking for its existence.
- */
-struct Latch *MyLatch;
-
 /*
  * DataDir is the absolute path to the top level of the PGDATA directory tree.
  * Except during early startup, this is also the server's working directory;
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 9e3676f7b3f..87ad534923e 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -42,8 +42,8 @@
 #include "postmaster/postmaster.h"
 #include "replication/slotsync.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
-#include "storage/latch.h"
 #include "storage/pg_shmem.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
@@ -66,8 +66,6 @@ BackendType MyBackendType;
 /* List of lock files to be removed at proc exit */
 static List *lock_files = NIL;
 
-static Latch LocalLatchData;
-
 /* ----------------------------------------------------------------
  *		ignoring system indexes support stuff
  *
@@ -133,10 +131,9 @@ InitPostmasterChild(void)
 	pqinitmask();
 #endif
 
-	/* Initialize process-local latch support */
-	InitializeLatchSupport();
-	InitProcessLocalLatch();
-	InitializeLatchWaitSet();
+	/* Initialize process-local interrupt support */
+	InitializeWaitEventSupport();
+	InitializeInterruptWaitSet();
 
 	/*
 	 * If possible, make this process a group leader, so that the postmaster
@@ -194,10 +191,9 @@ InitStandaloneProcess(const char *argv0)
 
 	InitProcessGlobals();
 
-	/* Initialize process-local latch support */
-	InitializeLatchSupport();
-	InitProcessLocalLatch();
-	InitializeLatchWaitSet();
+	/* Initialize process-local interrupt support */
+	InitializeWaitEventSupport();
+	InitializeInterruptWaitSet();
 
 	/*
 	 * For consistency with InitPostmasterChild, initialize signal mask here.
@@ -218,48 +214,6 @@ InitStandaloneProcess(const char *argv0)
 		get_pkglib_path(my_exec_path, pkglib_path);
 }
 
-void
-SwitchToSharedLatch(void)
-{
-	Assert(MyLatch == &LocalLatchData);
-	Assert(MyProc != NULL);
-
-	MyLatch = &MyProc->procLatch;
-
-	if (FeBeWaitSet)
-		ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetLatchPos, WL_LATCH_SET,
-						MyLatch);
-
-	/*
-	 * Set the shared latch as the local one might have been set. This
-	 * shouldn't normally be necessary as code is supposed to check the
-	 * condition before waiting for the latch, but a bit care can't hurt.
-	 */
-	SetLatch(MyLatch);
-}
-
-void
-InitProcessLocalLatch(void)
-{
-	MyLatch = &LocalLatchData;
-	InitLatch(MyLatch);
-}
-
-void
-SwitchBackToLocalLatch(void)
-{
-	Assert(MyLatch != &LocalLatchData);
-	Assert(MyProc != NULL && MyLatch == &MyProc->procLatch);
-
-	MyLatch = &LocalLatchData;
-
-	if (FeBeWaitSet)
-		ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetLatchPos, WL_LATCH_SET,
-						MyLatch);
-
-	SetLatch(MyLatch);
-}
-
 /*
  * Return a human-readable string representation of a BackendType.
  *
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 5b657a3f135..a34d97e8338 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -46,6 +46,7 @@
 #include "replication/walsender.h"
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
 #include "storage/proc.h"
@@ -1388,7 +1389,7 @@ TransactionTimeoutHandler(void)
 {
 	TransactionTimeoutPending = true;
 	InterruptPending = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 static void
@@ -1396,7 +1397,7 @@ IdleInTransactionSessionTimeoutHandler(void)
 {
 	IdleInTransactionSessionTimeoutPending = true;
 	InterruptPending = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 static void
@@ -1404,7 +1405,7 @@ IdleSessionTimeoutHandler(void)
 {
 	IdleSessionTimeoutPending = true;
 	InterruptPending = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 static void
@@ -1412,7 +1413,7 @@ IdleStatsUpdateTimeoutHandler(void)
 {
 	IdleStatsUpdateTimeoutPending = true;
 	InterruptPending = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 static void
@@ -1420,7 +1421,7 @@ ClientCheckTimeoutHandler(void)
 {
 	CheckClientConnectionPending = true;
 	InterruptPending = true;
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 }
 
 /*
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index ec7e570920a..56c9f87648c 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -17,7 +17,7 @@
 #include <sys/time.h>
 
 #include "miscadmin.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 
@@ -371,10 +371,10 @@ handle_sig_alarm(SIGNAL_ARGS)
 	HOLD_INTERRUPTS();
 
 	/*
-	 * SIGALRM is always cause for waking anything waiting on the process
-	 * latch.
+	 * SIGALRM is always cause for waking anything waiting on
+	 * INTERRUPT_GENERAL_WAKEUP.
 	 */
-	SetLatch(MyLatch);
+	RaiseInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 	/*
 	 * Always reset signal_pending, even if !alarm_enabled, since indeed no
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index bde54326c66..12d9ad63d82 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -1273,7 +1273,7 @@ HandleLogMemoryContextInterrupt(void)
 {
 	InterruptPending = true;
 	LogMemoryContextPending = true;
-	/* latch will be set by procsignal_sigusr1_handler */
+	/* INTERRUPT_GENERAL_WAKEUP will be raised by procsignal_sigusr1_handler */
 }
 
 /*
diff --git a/src/include/access/parallel.h b/src/include/access/parallel.h
index 69ffe5498f9..7daec7ef830 100644
--- a/src/include/access/parallel.h
+++ b/src/include/access/parallel.h
@@ -14,6 +14,8 @@
 #ifndef PARALLEL_H
 #define PARALLEL_H
 
+#include <signal.h>
+
 #include "access/xlogdefs.h"
 #include "lib/ilist.h"
 #include "postmaster/bgworker.h"
diff --git a/src/include/libpq/libpq-be-fe-helpers.h b/src/include/libpq/libpq-be-fe-helpers.h
index 46a90dfb022..ca0af464a47 100644
--- a/src/include/libpq/libpq-be-fe-helpers.h
+++ b/src/include/libpq/libpq-be-fe-helpers.h
@@ -43,7 +43,7 @@
 #include "libpq-fe.h"
 #include "miscadmin.h"
 #include "storage/fd.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
 #include "utils/timestamp.h"
 #include "utils/wait_event.h"
 
@@ -177,8 +177,8 @@ libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
 		return;
 
 	/*
-	 * WaitLatchOrSocket() can conceivably fail, handle that case here instead
-	 * of requiring all callers to do so.
+	 * WaitInterruptOrSocket() can conceivably fail, handle that case here
+	 * instead of requiring all callers to do so.
 	 */
 	PG_TRY();
 	{
@@ -209,16 +209,16 @@ libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
 			else
 				io_flag = WL_SOCKET_WRITEABLE;
 
-			rc = WaitLatchOrSocket(MyLatch,
-								   WL_EXIT_ON_PM_DEATH | WL_LATCH_SET | io_flag,
-								   PQsocket(conn),
-								   0,
-								   wait_event_info);
+			rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL_WAKEUP,
+									   WL_EXIT_ON_PM_DEATH | WL_INTERRUPT | io_flag,
+									   PQsocket(conn),
+									   0,
+									   wait_event_info);
 
 			/* Interrupted? */
-			if (rc & WL_LATCH_SET)
+			if (rc & WL_INTERRUPT)
 			{
-				ResetLatch(MyLatch);
+				ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 				CHECK_FOR_INTERRUPTS();
 			}
 
@@ -341,17 +341,17 @@ libpqsrv_get_result(PGconn *conn, uint32 wait_event_info)
 	{
 		int			rc;
 
-		rc = WaitLatchOrSocket(MyLatch,
-							   WL_EXIT_ON_PM_DEATH | WL_LATCH_SET |
-							   WL_SOCKET_READABLE,
-							   PQsocket(conn),
-							   0,
-							   wait_event_info);
+		rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL_WAKEUP,
+								   WL_EXIT_ON_PM_DEATH | WL_INTERRUPT |
+								   WL_SOCKET_READABLE,
+								   PQsocket(conn),
+								   0,
+								   wait_event_info);
 
 		/* Interrupted? */
-		if (rc & WL_LATCH_SET)
+		if (rc & WL_INTERRUPT)
 		{
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			CHECK_FOR_INTERRUPTS();
 		}
 
@@ -407,7 +407,7 @@ libpqsrv_cancel(PGconn *conn, TimestampTz endtime)
 			PostgresPollingStatusType pollres;
 			TimestampTz now;
 			long		cur_timeout;
-			int			waitEvents = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
+			int			waitEvents = WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
 
 			pollres = PQcancelPoll(cancel_conn);
 			if (pollres == PGRES_POLLING_OK)
@@ -436,10 +436,11 @@ libpqsrv_cancel(PGconn *conn, TimestampTz endtime)
 			}
 
 			/* Sleep until there's something to do */
-			WaitLatchOrSocket(MyLatch, waitEvents, PQcancelSocket(cancel_conn),
-							  cur_timeout, PG_WAIT_CLIENT);
+			WaitInterruptOrSocket(1 << INTERRUPT_GENERAL_WAKEUP, waitEvents,
+								  PQcancelSocket(cancel_conn),
+								  cur_timeout, PG_WAIT_CLIENT);
 
-			ResetLatch(MyLatch);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index d825b4c7b6c..1c797a3de67 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -18,7 +18,7 @@
 
 #include "lib/stringinfo.h"
 #include "libpq/libpq-be.h"
-#include "storage/latch.h"
+#include "storage/waiteventset.h"
 
 
 /*
@@ -61,7 +61,7 @@ extern const PGDLLIMPORT PQcommMethods *PqCommMethods;
 extern PGDLLIMPORT WaitEventSet *FeBeWaitSet;
 
 #define FeBeWaitSetSocketPos 0
-#define FeBeWaitSetLatchPos 1
+#define FeBeWaitSetInterruptPos 1
 #define FeBeWaitSetNEvents 3
 
 extern int	ListenServerPort(int family, const char *hostName,
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 42a2b38cac9..3d442f256d9 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -190,7 +190,6 @@ extern PGDLLIMPORT int MyProcPid;
 extern PGDLLIMPORT pg_time_t MyStartTime;
 extern PGDLLIMPORT TimestampTz MyStartTimestamp;
 extern PGDLLIMPORT struct Port *MyProcPort;
-extern PGDLLIMPORT struct Latch *MyLatch;
 extern PGDLLIMPORT bool MyCancelKeyValid;
 extern PGDLLIMPORT int32 MyCancelKey;
 extern PGDLLIMPORT int MyPMChildSlot;
@@ -317,9 +316,6 @@ extern PGDLLIMPORT char *DatabasePath;
 /* now in utils/init/miscinit.c */
 extern void InitPostmasterChild(void);
 extern void InitStandaloneProcess(const char *argv0);
-extern void InitProcessLocalLatch(void);
-extern void SwitchToSharedLatch(void);
-extern void SwitchBackToLocalLatch(void);
 
 /*
  * MyBackendType indicates what kind of a backend this is.
diff --git a/src/include/storage/interrupt.h b/src/include/storage/interrupt.h
new file mode 100644
index 00000000000..5bcc9f2407e
--- /dev/null
+++ b/src/include/storage/interrupt.h
@@ -0,0 +1,159 @@
+/*-------------------------------------------------------------------------
+ *
+ * interrupt.h
+ *	  Interrupt handling routines.
+ *
+ * "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, or asynchronously by timer signal handlers, other signal
+ * handlers or "sent" by other backends setting them directly.
+ *
+ * Most code currently deals with the INTERRUPT_GENERAL_WAKEUP interrupt. It
+ * is raised by any of the events checked by CHECK_FOR_INTERRUPTS), like query
+ * cancellation or idle session timeout. Well behaved backend code performs
+ * CHECK_FOR_INTERRUPTS() periodically in long computations, and should never
+ * sleep using mechanisms other than the WaitEventSet mechanism or the more
+ * convenient WaitInterrupt/WaitSockerOrInterrupt functions (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().
+ *
+ *
+ * The correct pattern to wait for event(s) using INTERRUPT_GENERAL_WAKEUP is:
+ *
+ * for (;;)
+ * {
+ *	   ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
+ *	   if (work to do)
+ *		   Do Stuff();
+ *	   WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, ...);
+ * }
+ *
+ * It's important to clear the interrupt *before* checking if there's work to
+ * do. Otherwise, if someone sets the interrupt between the check and the
+ * ClearInterrupt() call, you will miss it and Wait will incorrectly block.
+ *
+ * Another valid coding pattern looks like:
+ *
+ * for (;;)
+ * {
+ *	   if (work to do)
+ *		   Do Stuff(); // in particular, exit loop if some condition satisfied
+ *	   WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, ...);
+ *	   ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
+ * }
+ *
+ * This is useful to reduce interrupt traffic if it's expected that the loop's
+ * termination condition will often be satisfied in the first iteration;
+ * the cost is an extra loop iteration before blocking when it is not.
+ * What must be avoided is placing any checks for asynchronous events after
+ * WaitInterrupt and before ClearInterrupt, as that creates a race condition.
+ *
+ * To wake up the waiter, you must first set a global flag or something else
+ * that the wait loop tests in the "if (work to do)" part, and call
+ * SendInterrupt(INTERRUPT_GENERAL_WAKEP) *after* that. SendInterrupt is
+ * designed to return quickly if the interrupt is already set. In more complex
+ * scenarios with nested loops that can consume different events, you can
+ * define your own INTERRUPT_* flag instead of relying on
+ * INTERRUPT_GENERAL_WAKEUP.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/include/storage/interrupt.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef STORAGE_INTERRUPT_H
+#define STORAGE_INTERRUPT_H
+
+#include "port/atomics.h"
+#include "storage/procnumber.h"
+#include "storage/waiteventset.h"
+
+#include <signal.h>
+
+extern PGDLLIMPORT pg_atomic_uint32 *MyPendingInterrupts;
+extern PGDLLIMPORT pg_atomic_uint32 *MyMaybeSleepingOnInterrupts;
+
+typedef enum
+{
+	/*
+	 * INTERRUPT_GENERAL_WAKEUP is multiplexed for many reasons, like query
+	 * cancellation termination requests, recovery conflicts, and config
+	 * reload requests.  Upon receiving INTERRUPT_GENERAL_WAKEUP, you should
+	 * call CHECK_FOR_INTERRUPTS() to process those requests.  It is also used
+	 * for various other context-dependent purposes, but note that if it's
+	 * used to wake up for other reasons, you must still call
+	 * CHECK_FOR_INTERRUPTS() once per iteration.
+	 */
+	INTERRUPT_GENERAL_WAKEUP,
+
+	/*
+	 * INTERRUPT_RECOVERY_WAKEUP is used to wake up startup process, to tell
+	 * it that it should continue WAL replay. It's sent by WAL receiver when
+	 * more WAL arrives, or when promotion is requested.
+	 */
+	INTERRUPT_RECOVERY_CONTINUE,
+} InterruptType;
+
+/*
+ * Test an interrupt flag.
+ */
+static inline bool
+InterruptIsPending(InterruptType reason)
+{
+	return (pg_atomic_read_u32(MyPendingInterrupts) & (1 << reason)) != 0;
+}
+
+/*
+ * Test an interrupt flag.
+ */
+static inline bool
+InterruptsPending(uint32 mask)
+{
+	return (pg_atomic_read_u32(MyPendingInterrupts) & (mask)) != 0;
+}
+
+/*
+ * Clear an interrupt flag.
+ */
+static inline void
+ClearInterrupt(InterruptType reason)
+{
+	pg_atomic_fetch_and_u32(MyPendingInterrupts, ~(1 << reason));
+}
+
+/*
+ * Test and clear an interrupt flag.
+ */
+static inline bool
+ConsumeInterrupt(InterruptType reason)
+{
+	if (likely(!InterruptIsPending(reason)))
+		return false;
+
+	ClearInterrupt(reason);
+
+	return true;
+}
+
+extern void RaiseInterrupt(InterruptType reason);
+extern void SendInterrupt(InterruptType reason, ProcNumber pgprocno);
+extern int	WaitInterrupt(uint32 interruptMask, int wakeEvents, long timeout,
+						  uint32 wait_event_info);
+extern int	WaitInterruptOrSocket(uint32 interruptMask, int wakeEvents, pgsocket sock,
+								  long timeout, uint32 wait_event_info);
+extern void SwitchToLocalInterrupts(void);
+extern void SwitchToSharedInterrupts(void);
+extern void InitializeInterruptWaitSet(void);
+
+#endif
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
deleted file mode 100644
index 7e194d536f0..00000000000
--- a/src/include/storage/latch.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * latch.h
- *	  Routines for interprocess latches
- *
- * A latch is a boolean variable, with operations that let processes sleep
- * until it is set. A latch can be set from another process, or a signal
- * handler within the same process.
- *
- * The latch interface is a reliable replacement for the common pattern of
- * using pg_usleep() or select() to wait until a signal arrives, where the
- * signal handler sets a flag variable. Because on some platforms an
- * incoming signal doesn't interrupt sleep, and even on platforms where it
- * does there is a race condition if the signal arrives just before
- * entering the sleep, the common pattern must periodically wake up and
- * poll the flag variable. The pselect() system call was invented to solve
- * this problem, but it is not portable enough. Latches are designed to
- * overcome these limitations, allowing you to sleep without polling and
- * ensuring quick response to signals from other processes.
- *
- * There are two kinds of latches: local and shared. A local latch is
- * initialized by InitLatch, and can only be set from the same process.
- * A local latch can be used to wait for a signal to arrive, by calling
- * SetLatch in the signal handler. A shared latch resides in shared memory,
- * and must be initialized at postmaster startup by InitSharedLatch. Before
- * a shared latch can be waited on, it must be associated with a process
- * with OwnLatch. Only the process owning the latch can wait on it, but any
- * process can set it.
- *
- * There are three basic operations on a latch:
- *
- * SetLatch		- Sets the latch
- * ResetLatch	- Clears the latch, allowing it to be set again
- * WaitLatch	- Waits for the latch to become set
- *
- * WaitLatch includes a provision for timeouts (which should be avoided
- * when possible, as they incur extra overhead) and a provision for
- * postmaster child processes to wake up immediately on postmaster death.
- * See latch.c for detailed specifications for the exported functions.
- *
- * The correct pattern to wait for event(s) is:
- *
- * for (;;)
- * {
- *	   ResetLatch();
- *	   if (work to do)
- *		   Do Stuff();
- *	   WaitLatch();
- * }
- *
- * It's important to reset the latch *before* checking if there's work to
- * do. Otherwise, if someone sets the latch between the check and the
- * ResetLatch call, you will miss it and Wait will incorrectly block.
- *
- * Another valid coding pattern looks like:
- *
- * for (;;)
- * {
- *	   if (work to do)
- *		   Do Stuff(); // in particular, exit loop if some condition satisfied
- *	   WaitLatch();
- *	   ResetLatch();
- * }
- *
- * This is useful to reduce latch traffic if it's expected that the loop's
- * termination condition will often be satisfied in the first iteration;
- * the cost is an extra loop iteration before blocking when it is not.
- * What must be avoided is placing any checks for asynchronous events after
- * WaitLatch and before ResetLatch, as that creates a race condition.
- *
- * To wake up the waiter, you must first set a global flag or something
- * else that the wait loop tests in the "if (work to do)" part, and call
- * SetLatch *after* that. SetLatch is designed to return quickly if the
- * latch is already set.
- *
- * On some platforms, signals will not interrupt the latch wait primitive
- * by themselves.  Therefore, it is critical that any signal handler that
- * is meant to terminate a WaitLatch wait calls SetLatch.
- *
- * Note that use of the process latch (PGPROC.procLatch) is generally better
- * than an ad-hoc shared latch for signaling auxiliary processes.  This is
- * because generic signal handlers will call SetLatch on the process latch
- * only, so using any latch other than the process latch effectively precludes
- * use of any generic handler.
- *
- *
- * WaitEventSets allow to wait for latches being set and additional events -
- * postmaster dying and socket readiness of several sockets currently - at the
- * same time.  On many platforms using a long lived event set is more
- * efficient than using WaitLatch or WaitLatchOrSocket.
- *
- *
- * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * src/include/storage/latch.h
- *
- *-------------------------------------------------------------------------
- */
-#ifndef LATCH_H
-#define LATCH_H
-
-#include <signal.h>
-
-#include "utils/resowner.h"
-
-/*
- * Latch structure should be treated as opaque and only accessed through
- * the public functions. It is defined here to allow embedding Latches as
- * part of bigger structs.
- */
-typedef struct Latch
-{
-	sig_atomic_t is_set;
-	sig_atomic_t maybe_sleeping;
-	bool		is_shared;
-	int			owner_pid;
-#ifdef WIN32
-	HANDLE		event;
-#endif
-} Latch;
-
-/*
- * Bitmasks for events that may wake-up WaitLatch(), WaitLatchOrSocket(), or
- * WaitEventSetWait().
- */
-#define WL_LATCH_SET		 (1 << 0)
-#define WL_SOCKET_READABLE	 (1 << 1)
-#define WL_SOCKET_WRITEABLE  (1 << 2)
-#define WL_TIMEOUT			 (1 << 3)	/* not for WaitEventSetWait() */
-#define WL_POSTMASTER_DEATH  (1 << 4)
-#define WL_EXIT_ON_PM_DEATH	 (1 << 5)
-#ifdef WIN32
-#define WL_SOCKET_CONNECTED  (1 << 6)
-#else
-/* avoid having to deal with case on platforms not requiring it */
-#define WL_SOCKET_CONNECTED  WL_SOCKET_WRITEABLE
-#endif
-#define WL_SOCKET_CLOSED 	 (1 << 7)
-#ifdef WIN32
-#define WL_SOCKET_ACCEPT	 (1 << 8)
-#else
-/* avoid having to deal with case on platforms not requiring it */
-#define WL_SOCKET_ACCEPT	WL_SOCKET_READABLE
-#endif
-#define WL_SOCKET_MASK		(WL_SOCKET_READABLE | \
-							 WL_SOCKET_WRITEABLE | \
-							 WL_SOCKET_CONNECTED | \
-							 WL_SOCKET_ACCEPT | \
-							 WL_SOCKET_CLOSED)
-
-typedef struct WaitEvent
-{
-	int			pos;			/* position in the event data structure */
-	uint32		events;			/* triggered events */
-	pgsocket	fd;				/* socket fd associated with event */
-	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
-#ifdef WIN32
-	bool		reset;			/* Is reset of the event required? */
-#endif
-} WaitEvent;
-
-/* forward declaration to avoid exposing latch.c implementation details */
-typedef struct WaitEventSet WaitEventSet;
-
-/*
- * prototypes for functions in latch.c
- */
-extern void InitializeLatchSupport(void);
-extern void InitLatch(Latch *latch);
-extern void InitSharedLatch(Latch *latch);
-extern void OwnLatch(Latch *latch);
-extern void DisownLatch(Latch *latch);
-extern void SetLatch(Latch *latch);
-extern void ResetLatch(Latch *latch);
-extern void ShutdownLatchSupport(void);
-
-extern WaitEventSet *CreateWaitEventSet(ResourceOwner resowner, int nevents);
-extern void FreeWaitEventSet(WaitEventSet *set);
-extern void FreeWaitEventSetAfterFork(WaitEventSet *set);
-extern int	AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
-							  Latch *latch, void *user_data);
-extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
-
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
-							 WaitEvent *occurred_events, int nevents,
-							 uint32 wait_event_info);
-extern int	WaitLatch(Latch *latch, int wakeEvents, long timeout,
-					  uint32 wait_event_info);
-extern int	WaitLatchOrSocket(Latch *latch, int wakeEvents,
-							  pgsocket sock, long timeout, uint32 wait_event_info);
-extern void InitializeLatchWaitSet(void);
-extern int	GetNumRegisteredWaitEvents(WaitEventSet *set);
-extern bool WaitEventSetCanReportClosed(void);
-
-#endif							/* LATCH_H */
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 5a3dd5d2d40..6060b57a0a9 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -17,7 +17,6 @@
 #include "access/clog.h"
 #include "access/xlogdefs.h"
 #include "lib/ilist.h"
-#include "storage/latch.h"
 #include "storage/lock.h"
 #include "storage/pg_sema.h"
 #include "storage/proclist_types.h"
@@ -172,9 +171,6 @@ struct PGPROC
 	PGSemaphore sem;			/* ONE semaphore to sleep on */
 	ProcWaitStatus waitStatus;
 
-	Latch		procLatch;		/* generic latch for process */
-
-
 	TransactionId xid;			/* id of top-level transaction currently being
 								 * executed by this proc, if running and XID
 								 * is assigned; else InvalidTransactionId.
@@ -310,6 +306,14 @@ 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 pendingInterrupts;
+	pg_atomic_uint32 maybeSleepingOnInterrupts;
+
+#ifdef WIN32
+	/* Event handle to wake up the process when sending an interrupt */
+	HANDLE		interruptWakeupEvent;
+#endif
 };
 
 /* NOTE: "typedef struct PGPROC PGPROC" appears in storage/lock.h. */
@@ -425,6 +429,8 @@ typedef struct PROC_HDR
 	 */
 	ProcNumber	walwriterProc;
 	ProcNumber	checkpointerProc;
+	ProcNumber	walreceiverProc;
+	ProcNumber	startupProc;
 
 	/* Current shared estimate of appropriate spins_per_delay value */
 	int			spins_per_delay;
@@ -492,9 +498,6 @@ extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
 extern void CheckDeadLockAlert(void);
 extern void LockErrorCleanup(void);
 
-extern void ProcWaitForSignal(uint32 wait_event_info);
-extern void ProcSendSignal(ProcNumber procNumber);
-
 extern PGPROC *AuxiliaryPidGetProc(int pid);
 
 extern void BecomeLockGroupLeader(void);
diff --git a/src/include/storage/waiteventset.h b/src/include/storage/waiteventset.h
new file mode 100644
index 00000000000..f8f555a3b5b
--- /dev/null
+++ b/src/include/storage/waiteventset.h
@@ -0,0 +1,119 @@
+/*-------------------------------------------------------------------------
+ *
+ * waiteventset.h
+ *	  ppoll() / pselect() like interface for waiting for events
+ *
+ * This is a reliable replacement for the common pattern of using pg_usleep()
+ * or select() to wait until a signal arrives, where the signal handler raises
+ * an interrupt (see storage/interrupt.h). Because on some platforms an
+ * incoming signal doesn't interrupt sleep, and even on platforms where it
+ * does there is a race condition if the signal arrives just before entering
+ * the sleep, the common pattern must periodically wake up and poll the flag
+ * variable. The pselect() system call was invented to solve this problem, but
+ * it is not portable enough. WaitEventSets and Interrupts are designed to
+ * overcome these limitations, allowing you to sleep without polling and
+ * ensuring quick response to signals from other processes.
+ *
+ * WaitInterrupt includes a provision for timeouts (which should be avoided
+ * when possible, as they incur extra overhead) and a provision for postmaster
+ * child processes to wake up immediately on postmaster death.  See
+ * interrupt.c for detailed specifications for the exported functions.
+ *
+ * On some platforms, signals will not interrupt the wait primitive by
+ * themselves.  Therefore, it is critical that any signal handler that is
+ * meant to terminate a WaitInterrupt wait calls RaiseInterrupt.
+ *
+ * WaitEventSets allow to wait for interrupts being set and additional events -
+ * postmaster dying and socket readiness of several sockets currently - at the
+ * same time.  On many platforms using a long lived event set is more
+ * efficient than using WaitInterrupt or WaitInterruptOrSocket.
+ *
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/waiteventset.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef WAITEVENTSET_H
+#define WAITEVENTSET_H
+
+#include <signal.h>
+
+#include "storage/procnumber.h"
+#include "utils/resowner.h"
+
+/*
+ * Bitmasks for events that may wake-up WaitInterrupt(),
+ * WaitInterruptOrSocket(), or WaitEventSetWait().
+ */
+#define WL_INTERRUPT		 (1 << 0)
+#define WL_SOCKET_READABLE	 (1 << 1)
+#define WL_SOCKET_WRITEABLE  (1 << 2)
+#define WL_TIMEOUT			 (1 << 3)	/* not for WaitEventSetWait() */
+#define WL_POSTMASTER_DEATH  (1 << 4)
+#define WL_EXIT_ON_PM_DEATH	 (1 << 5)
+#ifdef WIN32
+#define WL_SOCKET_CONNECTED  (1 << 6)
+#else
+/* avoid having to deal with case on platforms not requiring it */
+#define WL_SOCKET_CONNECTED  WL_SOCKET_WRITEABLE
+#endif
+#define WL_SOCKET_CLOSED 	 (1 << 7)
+#ifdef WIN32
+#define WL_SOCKET_ACCEPT	 (1 << 8)
+#else
+/* avoid having to deal with case on platforms not requiring it */
+#define WL_SOCKET_ACCEPT	WL_SOCKET_READABLE
+#endif
+#define WL_SOCKET_MASK		(WL_SOCKET_READABLE | \
+							 WL_SOCKET_WRITEABLE | \
+							 WL_SOCKET_CONNECTED | \
+							 WL_SOCKET_ACCEPT | \
+							 WL_SOCKET_CLOSED)
+
+typedef struct WaitEvent
+{
+	int			pos;			/* position in the event data structure */
+	uint32		events;			/* triggered events */
+	pgsocket	fd;				/* socket fd associated with event */
+	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
+#ifdef WIN32
+	bool		reset;			/* Is reset of the event required? */
+#endif
+} WaitEvent;
+
+/* forward declaration to avoid exposing interrupt.c implementation details */
+typedef struct WaitEventSet WaitEventSet;
+
+struct PGPROC;
+
+/*
+ * prototypes for functions in waiteventset.c
+ */
+extern void InitializeWaitEventSupport(void);
+extern void ShutdownWaitEventSupport(void);
+#ifdef WIN32
+extern HANDLE CreateSharedWakeupHandle(void);
+#endif
+
+extern WaitEventSet *CreateWaitEventSet(ResourceOwner resowner, int nevents);
+extern void FreeWaitEventSet(WaitEventSet *set);
+extern void FreeWaitEventSetAfterFork(WaitEventSet *set);
+extern int	AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
+							  uint32 interruptMask, void *user_data);
+extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, uint32 interruptMask);
+
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+							 WaitEvent *occurred_events, int nevents,
+							 uint32 wait_event_info);
+extern void InitializeInterruptWaitSet(void);
+extern int	GetNumRegisteredWaitEvents(WaitEventSet *set);
+extern bool WaitEventSetCanReportClosed(void);
+
+/* low level functions used to implement SendInterrupt */
+extern void WakeupMyProc(void);
+extern void WakeupOtherProc(struct PGPROC *proc);
+
+#endif							/* WAITEVENTSET_H */
diff --git a/src/port/pgsleep.c b/src/port/pgsleep.c
index 1284458bfce..9d8ef261297 100644
--- a/src/port/pgsleep.c
+++ b/src/port/pgsleep.c
@@ -32,10 +32,10 @@
  *
  * CAUTION: It's not a good idea to use long sleeps in the backend.  They will
  * silently return early if a signal is caught, but that doesn't include
- * latches being set on most OSes, and even signal handlers that set MyLatch
- * might happen to run before the sleep begins, allowing the full delay.
- * Better practice is to use WaitLatch() with a timeout, so that backends
- * respond to latches and signals promptly.
+ * interrupts being set on most OSes, and even signal handlers that raise an
+ * interrupt might happen to run before the sleep begins, allowing the full
+ * delay.  Better practice is to use WaitInterrupt() with a timeout, so that
+ * backends respond to interrupts and signals promptly.
  */
 void
 pg_usleep(long microsec)
diff --git a/src/test/isolation/isolationtester.c b/src/test/isolation/isolationtester.c
index 0b342b5c2bb..aed50f9f685 100644
--- a/src/test/isolation/isolationtester.c
+++ b/src/test/isolation/isolationtester.c
@@ -762,7 +762,7 @@ try_complete_steps(TestSpec *testspec, PermutationStep **waiting,
 	{
 		int			w = 0;
 
-		/* Reset latch; we only care about notices received within loop. */
+		/* Reset flag; we only care about notices received within loop. */
 		any_new_notice = false;
 
 		/* Likewise, these variables reset for each retry. */
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index fb235604394..fa4c427943c 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -18,6 +18,7 @@
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "postmaster/bgworker.h"
+#include "storage/interrupt.h"
 #include "storage/shm_toc.h"
 #include "test_shm_mq.h"
 #include "utils/memutils.h"
@@ -285,11 +286,11 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 			we_bgworker_startup = WaitEventExtensionNew("TestShmMqBgWorkerStartup");
 
 		/* Wait to be signaled. */
-		(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
-						 we_bgworker_startup);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+							 we_bgworker_startup);
 
-		/* Reset the latch so we don't spin. */
-		ResetLatch(MyLatch);
+		/* Clear the interrupt flag so we don't spin. */
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		/* An interrupt may have occurred while we were waiting. */
 		CHECK_FOR_INTERRUPTS();
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index 3d235568b81..c458b89047b 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -16,6 +16,7 @@
 #include "fmgr.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "storage/interrupt.h"
 #include "varatt.h"
 
 #include "test_shm_mq.h"
@@ -234,13 +235,13 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 
 			/*
 			 * If we made no progress, wait for one of the other processes to
-			 * which we are connected to set our latch, indicating that they
-			 * have read or written data and therefore there may now be work
-			 * for us to do.
+			 * which we are connected to send us an interrupt, indicating that
+			 * they have read or written data and therefore there may now be
+			 * work for us to do.
 			 */
-			(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
-							 we_message_queue);
-			ResetLatch(MyLatch);
+			(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+								 we_message_queue);
+			ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 			CHECK_FOR_INTERRUPTS();
 		}
 	}
diff --git a/src/test/modules/test_shm_mq/worker.c b/src/test/modules/test_shm_mq/worker.c
index 6c4fbc78274..6fe9f9e4eb0 100644
--- a/src/test/modules/test_shm_mq/worker.c
+++ b/src/test/modules/test_shm_mq/worker.c
@@ -20,6 +20,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "storage/interrupt.h"
 #include "storage/ipc.h"
 #include "storage/procarray.h"
 #include "storage/shm_mq.h"
@@ -128,7 +129,7 @@ test_shm_mq_main(Datum main_arg)
 		elog(DEBUG1, "registrant backend has exited prematurely");
 		proc_exit(1);
 	}
-	SetLatch(&registrant->procLatch);
+	SendInterrupt(INTERRUPT_GENERAL_WAKEUP, GetNumberFromPGProc(registrant));
 
 	/* Do the work. */
 	copy_messages(inqh, outqh);
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index d4403b24d98..55febfb1b96 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -5,7 +5,7 @@
  *		patterns: establishing a database connection; starting and committing
  *		transactions; using GUC variables, and heeding SIGHUP to reread
  *		the configuration file; reporting to pg_stat_activity; using the
- *		process latch to sleep and exit in case of postmaster death.
+ *		WaitInterrupt to sleep and exit in case of postmaster death.
  *
  * This code connects to a database, creates a schema and table, and summarizes
  * the numbers contained therein.  To see it working, insert an initial value
@@ -26,7 +26,7 @@
 #include "miscadmin.h"
 #include "postmaster/bgworker.h"
 #include "postmaster/interrupt.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
 
 /* these headers are used by this particular worker's code */
 #include "access/xact.h"
@@ -223,15 +223,15 @@ worker_spi_main(Datum main_arg)
 
 		/*
 		 * Background workers mustn't call usleep() or any direct equivalent:
-		 * instead, they may wait on their process latch, which sleeps as
-		 * necessary, but is awakened if postmaster dies.  That way the
-		 * background process goes away immediately in an emergency.
+		 * instead, they may use WaitInterrupt, which sleeps as necessary, but
+		 * is awakened if postmaster dies.  That way the background process
+		 * goes away immediately in an emergency.
 		 */
-		(void) WaitLatch(MyLatch,
-						 WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
-						 worker_spi_naptime * 1000L,
-						 worker_spi_wait_event_main);
-		ResetLatch(MyLatch);
+		(void) WaitInterrupt(1 << INTERRUPT_GENERAL_WAKEUP,
+							 WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 worker_spi_naptime * 1000L,
+							 worker_spi_wait_event_main);
+		ClearInterrupt(INTERRUPT_GENERAL_WAKEUP);
 
 		CHECK_FOR_INTERRUPTS();
 
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 08521d51a9b..1eaad58b1be 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1254,6 +1254,7 @@ Integer
 IntegerSet
 InternalDefaultACL
 InternalGrant
+InterruptType
 Interval
 IntervalAggState
 IntoClause
@@ -1492,7 +1493,6 @@ LZ4State
 LabelProvider
 LagTracker
 LargeObjectDesc
-Latch
 LauncherLastStartTimesEntry
 LerpFunc
 LexDescr
-- 
2.39.5

