public inbox for [email protected]
help / color / mirror / Atom feedFrom: Heikki Linnakangas <[email protected]>
To: Andres Freund <[email protected]>
To: Thomas Munro <[email protected]>
Cc: Michael Paquier <[email protected]>
Cc: Robert Haas <[email protected]>
Cc: Fujii Masao <[email protected]>
Cc: pgsql-hackers <[email protected]>
Subject: Re: Interrupts vs signals
Date: Thu, 6 Mar 2025 18:43:47 +0200
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>
<CA+TgmoZQK2HfGqz3py1TaiQ3-NiVwYK4+ZTWGnm6z41mHeAksA@mail.gmail.com>
<[email protected]>
<[email protected]>
<[email protected]>
<CA+hUKGKs+ozrxt_X2FQ=yv5Mx8ZJ2fB+j8yLVZwa13LedT6CSg@mail.gmail.com>
<[email protected]>
<CA+hUKGKroPrwnUvnN0xORPYvXRY-90PC9g_C4suvp9Cws30yAg@mail.gmail.com>
<[email protected]>
<[email protected]>
<xa36hiwyk3qdxre56kj3txyd4etttqlliquzirdvofvfnz4svn@bbfvk5uw747b>
<[email protected]>
On 06/03/2025 02:47, Heikki Linnakangas wrote:
> Here's a new patch set. It includes the previous work, and also goes the
> whole hog and replaces procsignals and many other signalling with
> interrupts. It's based on Thomas's v3-0002-Redesign-interrupts-remove-
> ProcSignals.patch much earlier in this thread.
And here's yet another version. It's the same at high level, but with a
ton of little fixes.
One notable change is that I merged storage/interrupt.[ch] with
postmaster/interrupt.[ch], so the awkwardness of having two files with
same name is gone.
--
Heikki Linnakangas
Neon (https://neon.tech)
Attachments:
[text/x-patch] v7-0001-Replace-Latches-with-Interrupts.patch (219.4K, ../[email protected]/2-v7-0001-Replace-Latches-with-Interrupts.patch)
download | inline diff:
From 2614f4edda78169fa3f70b67f07032c770706a06 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Sat, 1 Mar 2025 00:18:03 +0200
Subject: [PATCH v7 1/4] Replace Latches with Interrupts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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 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 a per-process interrupt mask. You can no longer
allocate Latches in arbitrary shared memory structs and 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 replaces
the general-purpose per-process latch. All code that previously set a
process's process latch now sets its INTERRUPT_GENERAL interrupt bit
instead.
The second interrupt bit, INTERRUPT_RECOVERY_CONTINUE, replaces
recoveryWakeupLatch. With this new interface, the code can easily wait
for the regular interrupts (INTERRUPT_GENERAL) 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 it was reverted in commit 00f690a239 because it caused
a lot of spurious wakeups when the 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.
Reviewed-by: Thomas Munro, Andres Freund, Robert Haas, Álvaro Herrera
Discussion: https://www.postgresql.org/message-id/[email protected]
---
contrib/pg_prewarm/autoprewarm.c | 19 +-
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 | 30 +-
src/backend/access/transam/xlog.c | 14 +-
src/backend/access/transam/xlogfuncs.c | 12 +-
src/backend/access/transam/xlogrecovery.c | 91 ++--
src/backend/backup/basebackup_throttle.c | 18 +-
src/backend/commands/async.c | 19 +-
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/postmaster/autovacuum.c | 21 +-
src/backend/postmaster/auxprocess.c | 1 +
src/backend/postmaster/bgworker.c | 18 +-
src/backend/postmaster/bgwriter.c | 36 +-
src/backend/postmaster/checkpointer.c | 47 +--
src/backend/postmaster/interrupt.c | 260 +++++++++++-
src/backend/postmaster/pgarch.c | 26 +-
src/backend/postmaster/postmaster.c | 31 +-
src/backend/postmaster/startup.c | 11 +-
src/backend/postmaster/syslogger.c | 17 +-
src/backend/postmaster/walsummarizer.c | 23 +-
src/backend/postmaster/walwriter.c | 22 +-
.../libpqwalreceiver/libpqwalreceiver.c | 32 +-
.../replication/logical/applyparallelworker.c | 38 +-
src/backend/replication/logical/launcher.c | 53 +--
src/backend/replication/logical/slotsync.c | 22 +-
src/backend/replication/logical/tablesync.c | 35 +-
src/backend/replication/logical/worker.c | 24 +-
src/backend/replication/syncrep.c | 29 +-
src/backend/replication/walreceiver.c | 46 ++-
src/backend/replication/walreceiverfuncs.c | 3 +-
src/backend/replication/walsender.c | 26 +-
src/backend/storage/buffer/bufmgr.c | 10 +-
src/backend/storage/buffer/freelist.c | 27 +-
src/backend/storage/ipc/Makefile | 1 -
src/backend/storage/ipc/latch.c | 387 ------------------
src/backend/storage/ipc/meson.build | 1 -
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 | 10 +-
src/backend/storage/ipc/standby.c | 22 +-
src/backend/storage/ipc/waiteventset.c | 384 +++++++++--------
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 | 24 +-
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 | 55 +--
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/postmaster/interrupt.h | 151 ++++++-
src/include/storage/latch.h | 140 -------
src/include/storage/proc.h | 16 +-
src/include/storage/waiteventset.h | 35 +-
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 | 19 +-
src/tools/pgindent/typedefs.list | 2 +-
82 files changed, 1386 insertions(+), 1527 deletions(-)
delete mode 100644 src/backend/storage/ipc/latch.c
delete mode 100644 src/include/storage/latch.h
diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index b45755b3347..20dc7885d6a 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -38,7 +38,6 @@
#include "storage/dsm_registry.h"
#include "storage/fd.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/lwlock.h"
#include "storage/procsignal.h"
#include "storage/smgr.h"
@@ -216,10 +215,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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+ -1L,
+ PG_WAIT_EXTENSION);
}
else
{
@@ -243,14 +242,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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ delay_in_ms,
+ PG_WAIT_EXTENSION);
}
/* Reset the latch, loop. */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
/*
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ef9702c05c..0c9fdeb92fd 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -27,7 +27,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "postgres_fdw.h"
-#include "storage/latch.h"
+#include "postmaster/interrupt.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/inval.h"
@@ -802,8 +802,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);
@@ -1446,7 +1446,7 @@ pgfdw_cancel_query_end(PGconn *conn, TimestampTz endtime,
/*
* 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,7 +1541,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))
@@ -1654,12 +1654,12 @@ pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime,
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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_SOCKET_READABLE |
+ WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ PQsocket(conn),
+ cur_timeout, pgfdw_we_cleanup_result);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1131a8bf77e..31cd92f9e36 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -39,7 +39,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"
@@ -7355,7 +7355,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..fc642f5f713 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);
}
</programlisting>
</para>
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 3b91d02605a..45f25270dd8 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -150,6 +150,7 @@
#include "pgstat.h"
#include "portability/instr_time.h"
#include "postmaster/autovacuum.h"
+#include "postmaster/interrupt.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/lmgr.h"
@@ -3242,11 +3243,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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL,
+ WAIT_EVENT_VACUUM_TRUNCATE);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
/*
diff --git a/src/backend/access/transam/README b/src/backend/access/transam/README
index 231106270fd..7c29b23b01a 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 94db1ec3012..30cdce00b47 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -34,6 +34,7 @@
#include "miscadmin.h"
#include "optimizer/optimizer.h"
#include "pgstat.h"
+#include "postmaster/interrupt.h"
#include "storage/ipc.h"
#include "storage/predicate.h"
#include "storage/spin.h"
@@ -759,16 +760,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(INTERRUPT_GENERAL,
+ 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);
}
}
@@ -877,15 +878,16 @@ 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 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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, -1,
+ WAIT_EVENT_PARALLEL_FINISH);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
if (pcxt->toc != NULL)
@@ -1038,7 +1040,7 @@ HandleParallelMessageInterrupt(void)
{
InterruptPending = true;
ParallelMessagePending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 799fc739e18..940554def1a 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -74,6 +74,7 @@
#include "pgstat.h"
#include "port/atomics.h"
#include "postmaster/bgwriter.h"
+#include "postmaster/interrupt.h"
#include "postmaster/startup.h"
#include "postmaster/walsummarizer.h"
#include "postmaster/walwriter.h"
@@ -86,7 +87,6 @@
#include "storage/fd.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"
@@ -2664,7 +2664,7 @@ XLogSetAsyncXactLSN(XLogRecPtr asyncXactLSN)
ProcNumber walwriterProc = procglobal->walwriterProc;
if (walwriterProc != INVALID_PROC_NUMBER)
- SetLatch(&GetPGProcByNumber(walwriterProc)->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, ProcGlobal->walwriterProc);
}
}
@@ -9382,11 +9382,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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ 1000L,
+ WAIT_EVENT_BACKUP_WAIT_WAL_ARCHIVE);
+ ClearInterrupt(INTERRUPT_GENERAL);
if (++waits >= seconds_before_warning)
{
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 8c3090165f0..8f43ce62909 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -26,9 +26,9 @@
#include "funcapi.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/interrupt.h"
#include "replication/walreceiver.h"
#include "storage/fd.h"
-#include "storage/latch.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);
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(INTERRUPT_GENERAL,
+ 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 a829a055a97..c244f6c5ec0 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -47,14 +47,15 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/bgwriter.h"
+#include "postmaster/interrupt.h"
#include "postmaster/startup.h"
#include "replication/slot.h"
#include "replication/slotsync.h"
#include "replication/walreceiver.h"
#include "storage/fd.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"
@@ -316,23 +317,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.
*/
@@ -467,7 +451,6 @@ XLogRecoveryShmemInit(void)
memset(XLogRecoveryCtl, 0, sizeof(XLogRecoveryCtlData));
SpinLockInit(&XLogRecoveryCtl->info_lck);
- InitSharedLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
ConditionVariableInit(&XLogRecoveryCtl->recoveryNotPausedCV);
}
@@ -541,13 +524,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).
@@ -1637,13 +1613,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);
}
/*
@@ -1803,7 +1772,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))
@@ -3026,8 +2995,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)
@@ -3035,11 +3004,19 @@ recoveryApplyDelay(XLogReaderState *record)
while (true)
{
- ResetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
+ /*
+ * INTERRUPT_GENERAL 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);
/* This might change recovery_min_apply_delay. */
ProcessStartupProcInterrupts();
+ ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE);
if (CheckForStandbyTrigger())
break;
@@ -3060,10 +3037,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(INTERRUPT_RECOVERY_CONTINUE |
+ INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ msecs,
+ WAIT_EVENT_RECOVERY_APPLY_DELAY);
}
return true;
}
@@ -3718,15 +3696,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(INTERRUPT_RECOVERY_CONTINUE |
+ INTERRUPT_GENERAL,
+ 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);
ProcessStartupProcInterrupts();
}
last_fail_time = now;
@@ -3993,11 +3973,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(INTERRUPT_RECOVERY_CONTINUE |
+ INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+ -1L,
+ WAIT_EVENT_RECOVERY_WAL_STREAM);
+ ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE);
break;
}
@@ -4017,6 +3998,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
* This possibly-long loop needs to handle interrupts of startup
* process.
*/
+ ClearInterrupt(INTERRUPT_GENERAL);
ProcessStartupProcInterrupts();
}
@@ -4491,7 +4473,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/backup/basebackup_throttle.c b/src/backend/backup/basebackup_throttle.c
index b2b743238f9..861043c6b4a 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 "postmaster/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);
- /* 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(INTERRUPT_GENERAL,
+ 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 4bd37d5beb5..2b5b92abe38 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, 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 "postmaster/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 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);
}
/*
@@ -1821,10 +1822,10 @@ HandleNotifyInterrupt(void)
* This is called if we see notifyInterruptPending set, just before
* transmitting ReadyForQuery at the end of a frontend command, and
* also if a notify signal occurs while reading from the frontend.
- * HandleNotifyInterrupt() will cause the read to be interrupted
- * via the process's latch, and this routine will get called.
- * If we are truly idle (ie, *not* inside a transaction block),
- * process the incoming notifies.
+ * HandleNotifyInterrupt() will cause the read to be interrupted with
+ * INTERRUPT_GENERAL, and this routine will get called. If we are truly
+ * idle (ie, *not* inside a transaction block), process the incoming
+ * notifies.
*
* If "flush" is true, force any frontend messages out immediately.
* This can be false when being called at the end of a frontend command,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index e81c9a8aba3..65b897476ce 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2496,8 +2496,8 @@ vacuum_delay_point(bool is_analyze)
/*
* 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 15c4227cc62..df4b5fe1627 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
@@ -1043,7 +1042,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 dc7d1830259..11e5b3f083b 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 "postmaster/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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ WAIT_EVENT_EXECUTE_GATHER);
+ ClearInterrupt(INTERRUPT_GENERAL);
nvisited = 0;
}
}
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 81e2f8184e3..af282b3e802 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -1653,8 +1653,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)
@@ -3088,8 +3089,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 a3d610b1373..07a4fb3f3d0 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 "postmaster/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 64ff3ce3d6a..782a0265978 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -31,8 +31,9 @@
#include "libpq/libpq.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/interrupt.h"
#include "storage/fd.h"
-#include "storage/latch.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 91576f94285..765ca0f3166 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 "postmaster/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);
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);
ProcessClientWriteInterrupt(true);
/*
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index e5171467de1..f8465cb2d7b 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -76,6 +76,7 @@
#include "libpq/libpq.h"
#include "miscadmin.h"
#include "port/pg_bswap.h"
+#include "postmaster/interrupt.h"
#include "postmaster/postmaster.h"
#include "storage/ipc.h"
#include "utils/guc_hooks.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,
+ INTERRUPT_GENERAL, 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;
}
@@ -2063,7 +2064,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);
@@ -2071,15 +2072,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 to survive
+ * across CHECK_FOR_INTERRUPTS().
*/
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
goto retry;
}
}
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index f1a08bc32ca..b6928536984 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -19,6 +19,7 @@
#include "libpq/pqmq.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/interrupt.h"
#include "replication/logicalworker.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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ WAIT_EVENT_MESSAGE_QUEUE_PUT_MESSAGE);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 800815dfbcc..c45b332e822 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -90,7 +90,6 @@
#include "postmaster/postmaster.h"
#include "storage/bufmgr.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/lmgr.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
@@ -575,24 +574,24 @@ AutoVacLauncherMain(const void *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(av_worker_available(), false, &nap);
/*
* 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(INTERRUPT_GENERAL,
+ 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);
ProcessAutoVacLauncherInterrupts();
@@ -1359,7 +1358,7 @@ static void
avl_sigusr2_handler(SIGNAL_ARGS)
{
got_SIGUSR2 = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
diff --git a/src/backend/postmaster/auxprocess.c b/src/backend/postmaster/auxprocess.c
index 4f6795f7265..61bcac8d9f8 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 116ddf7b835..c82971e9274 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -18,11 +18,11 @@
#include "pgstat.h"
#include "port/atomics.h"
#include "postmaster/bgworker_internals.h"
+#include "postmaster/interrupt.h"
#include "postmaster/postmaster.h"
#include "replication/logicallauncher.h"
#include "replication/logicalworker.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(INTERRUPT_GENERAL,
+ 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);
}
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(INTERRUPT_GENERAL,
+ 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);
}
return status;
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index a688cc5d2a1..ae7d6f66cc6 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -224,7 +224,7 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len)
int rc;
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
ProcessMainLoopInterrupts();
@@ -299,22 +299,22 @@ BackgroundWriterMain(const void *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(INTERRUPT_GENERAL,
+ 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 +329,10 @@ BackgroundWriterMain(const void *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(INTERRUPT_GENERAL,
+ 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 0e228d143a0..363a42018e0 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -352,7 +352,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
bool chkpt_or_rstpt_timed = false;
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
/*
* Process any requests or signals received recently.
@@ -534,7 +534,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
/*
* We may have received an interrupt during the checkpoint and the
- * latch might have been reset (e.g. in CheckpointWriteDelay).
+ * interrupt might have been reset (e.g. in CheckpointWriteDelay).
*/
ProcessCheckpointerInterrupts();
if (ShutdownXLOGPending || ShutdownRequestPending)
@@ -572,10 +572,10 @@ CheckpointerMain(const void *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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ cur_timeout * 1000L /* convert to ms */ ,
+ WAIT_EVENT_CHECKPOINTER_MAIN);
}
/*
@@ -613,17 +613,17 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
for (;;)
{
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
ProcessCheckpointerInterrupts();
if (ShutdownRequestPending)
break;
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_EXIT_ON_PM_DEATH,
- 0,
- WAIT_EVENT_CHECKPOINTER_SHUTDOWN);
+ (void) WaitInterrupt(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+ 0,
+ WAIT_EVENT_CHECKPOINTER_SHUTDOWN);
}
/* Normal exit from the checkpointer is here */
@@ -804,10 +804,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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH | WL_TIMEOUT,
+ 100,
+ WAIT_EVENT_CHECKPOINT_WRITE_DELAY);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
else if (--absorb_counter <= 0)
{
@@ -916,7 +917,7 @@ static void
ReqShutdownXLOG(SIGNAL_ARGS)
{
ShutdownXLOGPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
@@ -1033,14 +1034,14 @@ RequestCheckpoint(int flags)
SpinLockRelease(&CheckpointerShmem->ckpt_lck);
/*
- * Set checkpointer's latch to request checkpoint. It's possible that the
- * checkpointer hasn't started yet, so we will retry a few times if
+ * Send INTERRUPT_GENERAL to wake up the checkpointer. It's possible that
+ * the checkpointer hasn't started yet, so we will retry a few times if
* needed. (Actually, more than a few times, since on slow or overloaded
* buildfarm machines, it's been observed that the checkpointer can take
* several seconds to start.) However, if not told to wait for the
- * checkpoint to occur, we consider failure to set the latch to be
- * nonfatal and merely LOG it. The checkpointer should see the request
- * when it does start, with or without the SetLatch().
+ * checkpoint to occur, we consider failure to wake up the checkpointer to
+ * be nonfatal and merely LOG it. The checkpointer should see the request
+ * when it does start, with or without the SendInterrupt().
*/
#define MAX_SIGNAL_TRIES 600 /* max wait 60.0 sec */
for (ntries = 0;; ntries++)
@@ -1059,7 +1060,7 @@ RequestCheckpoint(int flags)
}
else
{
- SetLatch(&GetPGProcByNumber(checkpointerProc)->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, checkpointerProc);
/* notified successfully */
break;
}
@@ -1186,7 +1187,7 @@ ForwardSyncRequest(const FileTag *ftag, SyncRequestType type)
ProcNumber checkpointerProc = procglobal->checkpointerProc;
if (checkpointerProc != INVALID_PROC_NUMBER)
- SetLatch(&GetPGProcByNumber(checkpointerProc)->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, ProcGlobal->checkpointerProc);
}
return true;
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 0ae9bf906ec..4cde7d52bd3 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* interrupt.c
- * Interrupt handling routines.
+ * Inter-process interrupts
*
- * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * src/backend/postmaster/interrupt.c
+ * src/backend/storage/ipc/interrupt.c
*
*-------------------------------------------------------------------------
*/
@@ -17,16 +17,264 @@
#include <unistd.h>
#include "miscadmin.h"
+#include "port/atomics.h"
#include "postmaster/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
+#include "storage/proc.h"
#include "storage/procsignal.h"
+#include "storage/waiteventset.h"
#include "utils/guc.h"
#include "utils/memutils.h"
volatile sig_atomic_t ConfigReloadPending = false;
volatile sig_atomic_t ShutdownRequestPending = false;
+/* A common WaitEventSet used to implement WaitInterrupt() */
+static WaitEventSet *InterruptWaitSet;
+
+/* The position of the interrupt in InterruptWaitSet. */
+#define InterruptWaitSetInterruptPos 0
+#define InterruptWaitSetPostmasterDeathPos 1
+
+static pg_atomic_uint32 LocalPendingInterrupts;
+
+pg_atomic_uint32 *MyPendingInterrupts = &LocalPendingInterrupts;
+
+/*
+ * 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;
+
+ /*
+ * 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;
+
+ /*
+ * 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(uint32 interruptMask)
+{
+ uint32 old_pending;
+
+ old_pending = pg_atomic_fetch_or_u32(MyPendingInterrupts, interruptMask);
+
+ /*
+ * If the process is currently blocked waiting for an interrupt to arrive,
+ * and the interrupt wasn't already pending, wake it up.
+ */
+ if ((old_pending & (interruptMask | SLEEPING_ON_INTERRUPTS)) == SLEEPING_ON_INTERRUPTS)
+ WakeupMyProc();
+}
+
+/*
+ * Set an interrupt flag in another backend.
+ */
+void
+SendInterrupt(uint32 interruptMask, ProcNumber pgprocno)
+{
+ PGPROC *proc;
+ uint32 old_pending;
+
+ Assert(pgprocno != INVALID_PROC_NUMBER);
+ Assert(pgprocno >= 0);
+ Assert(pgprocno < ProcGlobal->allProcCount);
+
+ proc = &ProcGlobal->allProcs[pgprocno];
+ old_pending = pg_atomic_fetch_or_u32(&proc->pendingInterrupts, interruptMask);
+
+ /*
+ * If the process is currently blocked waiting for an interrupt to arrive,
+ * and the interrupt wasn't already pending, wake it up.
+ */
+ if ((old_pending & (interruptMask | SLEEPING_ON_INTERRUPTS)) == SLEEPING_ON_INTERRUPTS)
+ WakeupOtherProc(proc);
+}
+
+void
+InitializeInterruptWaitSet(void)
+{
+ int interrupt_pos PG_USED_FOR_ASSERTS_ONLY;
+
+ Assert(InterruptWaitSet == 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(InterruptWaitSet, WL_EXIT_ON_PM_DEATH,
+ PGINVALID_SOCKET, 0, NULL);
+
+ Assert(interrupt_pos == InterruptWaitSetInterruptPos);
+}
+
+
+/*
+ * 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.
+ *
+ * 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
+WaitInterrupt(uint32 interruptMask, int wakeEvents, long timeout,
+ uint32 wait_event_info)
+{
+ WaitEvent event;
+
+ /* Postmaster-managed callers must handle postmaster death somehow. */
+ Assert(!IsUnderPostmaster ||
+ (wakeEvents & WL_EXIT_ON_PM_DEATH) ||
+ (wakeEvents & WL_POSTMASTER_DEATH));
+
+ /*
+ * 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_INTERRUPT))
+ interruptMask = 0;
+ ModifyWaitEvent(InterruptWaitSet, InterruptWaitSetInterruptPos,
+ WL_INTERRUPT, interruptMask);
+
+ ModifyWaitEvent(InterruptWaitSet, InterruptWaitSetPostmasterDeathPos,
+ (wakeEvents & (WL_EXIT_ON_PM_DEATH | WL_POSTMASTER_DEATH)),
+ 0);
+
+ if (WaitEventSetWait(InterruptWaitSet,
+ (wakeEvents & WL_TIMEOUT) ? timeout : -1,
+ &event, 1,
+ wait_event_info) == 0)
+ return WL_TIMEOUT;
+ else
+ return event.events;
+}
+
+/*
+ * 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
+ * to be reported as readable/writable/connected, so that the caller can deal
+ * with the condition.
+ *
+ * wakeEvents must include either WL_EXIT_ON_PM_DEATH for automatic exit
+ * if the postmaster dies or WL_POSTMASTER_DEATH for a flag set in the
+ * return value if the postmaster dies. The latter is useful for rare cases
+ * where some behavior other than immediate exit is needed.
+ *
+ * NB: These days this is just a wrapper around the WaitEventSet API. When
+ * using an interrupt very frequently, consider creating a longer living
+ * WaitEventSet instead; that's more efficient.
+ */
+int
+WaitInterruptOrSocket(uint32 interruptMask, int wakeEvents, pgsocket sock,
+ long timeout, uint32 wait_event_info)
+{
+ int ret = 0;
+ int rc;
+ WaitEvent event;
+ WaitEventSet *set = CreateWaitEventSet(CurrentResourceOwner, 3);
+
+ if (wakeEvents & WL_TIMEOUT)
+ Assert(timeout >= 0);
+ else
+ timeout = -1;
+
+ if (wakeEvents & WL_INTERRUPT)
+ AddWaitEventToSet(set, WL_INTERRUPT, PGINVALID_SOCKET,
+ interruptMask, NULL);
+
+ /* Postmaster-managed callers must handle postmaster death somehow. */
+ Assert(!IsUnderPostmaster ||
+ (wakeEvents & WL_EXIT_ON_PM_DEATH) ||
+ (wakeEvents & WL_POSTMASTER_DEATH));
+
+ if ((wakeEvents & WL_POSTMASTER_DEATH) && IsUnderPostmaster)
+ AddWaitEventToSet(set, WL_POSTMASTER_DEATH, PGINVALID_SOCKET,
+ 0, NULL);
+
+ if ((wakeEvents & WL_EXIT_ON_PM_DEATH) && IsUnderPostmaster)
+ AddWaitEventToSet(set, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET,
+ 0, NULL);
+
+ if (wakeEvents & WL_SOCKET_MASK)
+ {
+ int ev;
+
+ ev = wakeEvents & WL_SOCKET_MASK;
+ AddWaitEventToSet(set, ev, sock, 0, NULL);
+ }
+
+ rc = WaitEventSetWait(set, timeout, &event, 1, wait_event_info);
+
+ if (rc == 0)
+ ret |= WL_TIMEOUT;
+ else
+ {
+ ret |= event.events & (WL_INTERRUPT |
+ WL_POSTMASTER_DEATH |
+ WL_SOCKET_MASK);
+ }
+
+ FreeWaitEventSet(set);
+
+ return ret;
+}
+
/*
* Simple interrupt handler for main loops of background processes.
*/
@@ -61,7 +309,7 @@ void
SignalHandlerForConfigReload(SIGNAL_ARGS)
{
ConfigReloadPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -105,5 +353,5 @@ void
SignalHandlerForShutdownRequest(SIGNAL_ARGS)
{
ShutdownRequestPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index dbe4e1d426b..4f28ea5b613 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -42,7 +42,6 @@
#include "storage/condition_variable.h"
#include "storage/fd.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
@@ -247,8 +246,8 @@ PgArchiverMain(const void *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 +281,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, arch_pgprocno);
}
@@ -298,7 +296,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);
}
/*
@@ -318,7 +316,7 @@ pgarch_MainLoop(void)
*/
do
{
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
/* When we get SIGUSR2, we do one more archive cycle, then exit */
time_to_stop = ready_to_stop;
@@ -355,10 +353,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(INTERRUPT_GENERAL,
+ 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 d2a7a7add6f..82044abdb5d 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -101,6 +101,7 @@
#include "port/pg_bswap.h"
#include "postmaster/autovacuum.h"
#include "postmaster/bgworker_internals.h"
+#include "postmaster/interrupt.h"
#include "postmaster/pgarch.h"
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
@@ -549,7 +550,6 @@ PostmasterMain(int argc, char *argv[])
/* This may configure SIGURG, depending on platform. */
InitializeWaitEventSupport();
- InitProcessLocalLatch();
/*
* No other place in Postgres should touch SIGTTIN/SIGTTOU handling. We
@@ -1620,14 +1620,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,
+ INTERRUPT_GENERAL, NULL);
if (accept_connections)
{
for (int i = 0; i < NumListenSockets; i++)
AddWaitEventToSet(pm_wait_set, WL_SOCKET_ACCEPT, ListenSockets[i],
- NULL, NULL);
+ 0, NULL);
}
}
@@ -1656,19 +1656,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);
/*
* 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)
@@ -1961,7 +1962,7 @@ static void
handle_pm_pmsignal_signal(SIGNAL_ARGS)
{
pending_pm_pmsignal = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -1971,7 +1972,7 @@ static void
handle_pm_reload_request_signal(SIGNAL_ARGS)
{
pending_pm_reload_request = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -2048,7 +2049,7 @@ handle_pm_shutdown_request_signal(SIGNAL_ARGS)
pending_pm_shutdown_request = true;
break;
}
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -2209,7 +2210,7 @@ static void
handle_pm_child_exit_signal(SIGNAL_ARGS)
{
pending_pm_child_exit = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 27e86cf393f..f2ac862513b 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 ProcessStartupProcInterrupts().
* 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(const void *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 50c2edec1f6..c4c04ae86d4 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -45,7 +45,6 @@
#include "storage/dsm.h"
#include "storage/fd.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/pg_shmem.h"
#include "tcop/tcopprot.h"
#include "utils/guc.h"
@@ -328,7 +327,7 @@ SysLoggerMain(const void *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 +337,9 @@ SysLoggerMain(const void *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, INTERRUPT_GENERAL, 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 +355,7 @@ SysLoggerMain(const void *startup_data, size_t startup_data_len)
#endif
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
/*
* Process any requests or signals received recently.
@@ -1188,7 +1187,7 @@ pipeThread(void *arg)
ftello(csvlogFile) >= Log_RotationSize * (pgoff_t) 1024) ||
(jsonlogFile != NULL &&
ftello(jsonlogFile) >= Log_RotationSize * (pgoff_t) 1024))
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
LeaveCriticalSection(&sysloggerSection);
}
@@ -1199,8 +1198,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);
LeaveCriticalSection(&sysloggerSection);
_endthread();
@@ -1595,5 +1594,5 @@ static void
sigUsr1Handler(SIGNAL_ARGS)
{
rotation_requested = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c
index ccba0f84e6e..3381767149d 100644
--- a/src/backend/postmaster/walsummarizer.c
+++ b/src/backend/postmaster/walsummarizer.c
@@ -40,7 +40,6 @@
#include "replication/walreceiver.h"
#include "storage/fd.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/lwlock.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
@@ -316,10 +315,8 @@ WalSummarizerMain(const void *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) */
@@ -631,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)
@@ -647,7 +644,7 @@ WakeupWalSummarizer(void)
LWLockRelease(WALSummarizerLock);
if (pgprocno != INVALID_PROC_NUMBER)
- SetLatch(&ProcGlobal->allProcs[pgprocno].procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, pgprocno);
}
/*
@@ -1641,11 +1638,11 @@ summarizer_wait_for_wal(void)
pgstat_report_wal(false);
/* 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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ sleep_quanta * MS_PER_SLEEP_QUANTUM,
+ WAIT_EVENT_WAL_SUMMARIZER_WAL);
+ ClearInterrupt(INTERRUPT_GENERAL);
/* 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 0380601bcbb..ae097472200 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -222,12 +222,12 @@ WalWriterMain(const void *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 +236,7 @@ WalWriterMain(const void *startup_data, size_t startup_data_len)
}
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
/* Process any signals received recently */
ProcessMainLoopInterrupts();
@@ -263,9 +263,9 @@ WalWriterMain(const void *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(INTERRUPT_GENERAL,
+ 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 1b158c9d288..aaa12ccdbd0 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -29,8 +29,8 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "pqexpbuffer.h"
+#include "postmaster/interrupt.h"
#include "replication/walreceiver.h"
-#include "storage/latch.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(INTERRUPT_GENERAL,
+ 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);
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(INTERRUPT_GENERAL,
+ 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);
ProcessWalRcvInterrupts();
}
diff --git a/src/backend/replication/logical/applyparallelworker.c b/src/backend/replication/logical/applyparallelworker.c
index d25085d3515..176deb406c6 100644
--- a/src/backend/replication/logical/applyparallelworker.c
+++ b/src/backend/replication/logical/applyparallelworker.c
@@ -804,13 +804,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(INTERRUPT_GENERAL,
+ 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);
}
}
else
@@ -990,7 +990,7 @@ HandleParallelApplyMessageInterrupt(void)
{
InterruptPending = true;
ParallelApplyMessagePending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -1182,14 +1182,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(INTERRUPT_GENERAL,
+ 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);
CHECK_FOR_INTERRUPTS();
}
@@ -1254,13 +1254,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(INTERRUPT_GENERAL,
+ 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);
/* 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 a3c7adbf1a8..071d93ce816 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -209,16 +209,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(INTERRUPT_GENERAL,
+ 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);
CHECK_FOR_INTERRUPTS();
}
}
@@ -544,13 +545,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(INTERRUPT_GENERAL,
+ 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);
CHECK_FOR_INTERRUPTS();
}
@@ -585,13 +586,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(INTERRUPT_GENERAL,
+ 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);
CHECK_FOR_INTERRUPTS();
}
@@ -667,7 +668,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)
@@ -685,7 +686,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.
*/
@@ -694,7 +695,7 @@ logicalrep_worker_wakeup_ptr(LogicalRepWorker *worker)
{
Assert(LWLockHeldByMe(LogicalRepWorkerLock));
- SetLatch(&worker->proc->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, GetNumberFromPGProc(worker->proc));
}
/*
@@ -1212,14 +1213,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(INTERRUPT_GENERAL,
+ 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);
CHECK_FOR_INTERRUPTS();
}
diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index 2c0a7439be4..09b2d0d181e 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -1252,13 +1252,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(INTERRUPT_GENERAL,
+ 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);
}
/*
@@ -1588,13 +1588,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(INTERRUPT_GENERAL,
+ 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);
CHECK_FOR_INTERRUPTS();
}
diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c
index 6af5c9fe16c..dc9077adb75 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -104,6 +104,7 @@
#include "nodes/makefuncs.h"
#include "parser/parse_relation.h"
#include "pgstat.h"
+#include "postmaster/interrupt.h"
#include "replication/logicallauncher.h"
#include "replication/logicalrelation.h"
#include "replication/logicalworker.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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ 1000L, WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE);
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
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(INTERRUPT_GENERAL,
+ 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);
}
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(INTERRUPT_GENERAL,
+ WL_SOCKET_READABLE | WL_INTERRUPT |
+ WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ fd, 1000L, WAIT_EVENT_LOGICAL_SYNC_DATA);
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
return bytesread;
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 31ab69ea13a..bb566c1ee19 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -3742,26 +3742,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(INTERRUPT_GENERAL,
+ 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);
CHECK_FOR_INTERRUPTS();
}
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index d75e3968035..93d8b9d0605 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -78,6 +78,7 @@
#include "common/int.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/interrupt.h"
#include "replication/syncrep.h"
#include "replication/walsender.h"
#include "replication/walsender_private.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);
/*
- * 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(INTERRUPT_GENERAL,
+ 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, GetNumberFromPGProc(proc));
numprocs++;
}
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 2e5dd6deb2c..87b1bad279f 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -146,17 +146,17 @@ static void ProcessWalSndrMessage(XLogRecPtr walEnd, TimestampTz sendTime);
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.
+ * Process any interrupts the walreceiver process may have received. This
+ * should be called any time the INTERRUPT_GENERAL 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.
+ * signal handler sets a flag variable as well as raising INTERRUPT_GENERAL.
* 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
- * libpqrcv_PQgetResult for example.
+ * INTERRUPT_GENERAL 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
ProcessWalRcvInterrupts(void)
@@ -536,25 +536,25 @@ WalReceiverMain(const void *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(INTERRUPT_GENERAL,
+ 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);
ProcessWalRcvInterrupts();
if (walrcv->force_reply)
@@ -702,7 +702,7 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
WakeupRecovery();
for (;;)
{
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
ProcessWalRcvInterrupts();
@@ -734,8 +734,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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+ 0,
+ WAIT_EVENT_WAL_RECEIVER_WAIT_START);
}
if (update_process_title)
@@ -1388,7 +1390,7 @@ WalRcvForceReply(void)
procno = WalRcv->procno;
SpinLockRelease(&WalRcv->mutex);
if (procno != INVALID_PROC_NUMBER)
- SetLatch(&GetPGProcByNumber(procno)->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, procno);
}
/*
diff --git a/src/backend/replication/walreceiverfuncs.c b/src/backend/replication/walreceiverfuncs.c
index 8de2886ff0b..48e25fb7dab 100644
--- a/src/backend/replication/walreceiverfuncs.c
+++ b/src/backend/replication/walreceiverfuncs.c
@@ -25,6 +25,7 @@
#include "access/xlog_internal.h"
#include "access/xlogrecovery.h"
#include "pgstat.h"
+#include "postmaster/interrupt.h"
#include "replication/walreceiver.h"
#include "storage/pmsignal.h"
#include "storage/proc.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, walrcv_proc);
}
/*
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 446d10c1a7d..0afa181550b 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1013,8 +1013,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 +1611,7 @@ ProcessPendingWrites(void)
WAIT_EVENT_WAL_SENDER_WRITE_DATA);
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
@@ -1628,8 +1628,8 @@ ProcessPendingWrites(void)
WalSndShutdown();
}
- /* reactivate latch so WalSndLoop knows to continue */
- SetLatch(MyLatch);
+ /* reactivate interrupt so WalSndLoop knows to continue */
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -1817,7 +1817,7 @@ WalSndWaitForWal(XLogRecPtr loc)
long sleeptime;
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
@@ -1937,8 +1937,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);
return RecentFlushPtr;
}
@@ -2754,7 +2754,7 @@ WalSndLoop(WalSndSendDataCallback send_data)
for (;;)
{
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
@@ -3553,7 +3553,7 @@ static void
WalSndLastCycleHandler(SIGNAL_ARGS)
{
got_SIGUSR2 = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/* Set up signal handlers */
@@ -3659,7 +3659,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
@@ -3671,8 +3671,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 7915ed624c1..d7be4309d4e 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -48,6 +48,7 @@
#include "pg_trace.h"
#include "pgstat.h"
#include "postmaster/bgwriter.h"
+#include "postmaster/interrupt.h"
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
@@ -2880,7 +2881,7 @@ UnpinBufferNoOwner(BufferDesc *buf)
buf_state &= ~BM_PIN_COUNT_WAITER;
UnlockBufHdr(buf, buf_state);
- ProcSendSignal(wait_backend_pgprocno);
+ SendInterrupt(INTERRUPT_GENERAL, wait_backend_pgprocno);
}
else
UnlockBufHdr(buf, buf_state);
@@ -5283,7 +5284,12 @@ LockBufferForCleanup(Buffer buffer)
SetStartupBufferPinWaitBufId(-1);
}
else
- ProcWaitForSignal(WAIT_EVENT_BUFFER_PIN);
+ {
+ WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ WAIT_EVENT_BUFFER_PIN);
+ ClearInterrupt(INTERRUPT_GENERAL);
+ 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 336715b6c63..19389770386 100644
--- a/src/backend/storage/buffer/freelist.c
+++ b/src/backend/storage/buffer/freelist.c
@@ -17,6 +17,7 @@
#include "pgstat.h"
#include "port/atomics.h"
+#include "postmaster/interrupt.h"
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
#include "storage/proc.h"
@@ -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, 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 9a07f6e1d92..2d98a43e749 100644
--- a/src/backend/storage/ipc/Makefile
+++ b/src/backend/storage/ipc/Makefile
@@ -15,7 +15,6 @@ OBJS = \
dsm_registry.o \
ipc.o \
ipci.o \
- latch.o \
pmsignal.o \
procarray.o \
procsignal.o \
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
deleted file mode 100644
index c6aefd2f688..00000000000
--- a/src/backend/storage/ipc/latch.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * latch.c
- * Routines for inter-process latches
- *
- * 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. See latch.h for more information
- * on how to use them.
- *
- * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/backend/storage/ipc/latch.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "miscadmin.h"
-#include "port/atomics.h"
-#include "storage/latch.h"
-#include "storage/waiteventset.h"
-#include "utils/resowner.h"
-
-/* A common WaitEventSet used to implement WaitLatch() */
-static WaitEventSet *LatchWaitSet;
-
-/* The positions of the latch and PM death events in LatchWaitSet */
-#define LatchWaitSetLatchPos 0
-#define LatchWaitSetPostmasterDeathPos 1
-
-void
-InitializeLatchWaitSet(void)
-{
- int latch_pos PG_USED_FOR_ASSERTS_ONLY;
-
- Assert(LatchWaitSet == NULL);
-
- /* Set up the WaitEventSet used by WaitLatch(). */
- LatchWaitSet = CreateWaitEventSet(NULL, 2);
- latch_pos = AddWaitEventToSet(LatchWaitSet, WL_LATCH_SET, PGINVALID_SOCKET,
- MyLatch, NULL);
- Assert(latch_pos == LatchWaitSetLatchPos);
-
- /*
- * WaitLatch will modify this to WL_EXIT_ON_PM_DEATH or
- * WL_POSTMASTER_DEATH on each call.
- */
- if (IsUnderPostmaster)
- {
- latch_pos = AddWaitEventToSet(LatchWaitSet, WL_EXIT_ON_PM_DEATH,
- PGINVALID_SOCKET, NULL, NULL);
- Assert(latch_pos == LatchWaitSetPostmasterDeathPos);
- }
-}
-
-/*
- * 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;
-
-#ifdef 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);
-
- 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.
- *
- * 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)
-{
- WaitEvent event;
-
- /* Postmaster-managed callers must handle postmaster death somehow. */
- Assert(!IsUnderPostmaster ||
- (wakeEvents & WL_EXIT_ON_PM_DEATH) ||
- (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.
- */
- if (!(wakeEvents & WL_LATCH_SET))
- latch = NULL;
- ModifyWaitEvent(LatchWaitSet, LatchWaitSetLatchPos, WL_LATCH_SET, latch);
- ModifyWaitEvent(LatchWaitSet, LatchWaitSetPostmasterDeathPos,
- (wakeEvents & (WL_EXIT_ON_PM_DEATH | WL_POSTMASTER_DEATH)),
- NULL);
-
- if (WaitEventSetWait(LatchWaitSet,
- (wakeEvents & WL_TIMEOUT) ? timeout : -1,
- &event, 1,
- wait_event_info) == 0)
- return WL_TIMEOUT;
- else
- return event.events;
-}
-
-/*
- * Like WaitLatch, but with an extra socket argument for WL_SOCKET_*
- * conditions.
- *
- * When waiting on a socket, EOF and error conditions always cause the socket
- * to be reported as readable/writable/connected, so that the caller can deal
- * with the condition.
- *
- * wakeEvents must include either WL_EXIT_ON_PM_DEATH for automatic exit
- * if the postmaster dies or WL_POSTMASTER_DEATH for a flag set in the
- * return value if the postmaster dies. The latter is useful for rare cases
- * 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
- * WaitEventSet instead; that's more efficient.
- */
-int
-WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
- long timeout, uint32 wait_event_info)
-{
- int ret = 0;
- int rc;
- WaitEvent event;
- WaitEventSet *set = CreateWaitEventSet(CurrentResourceOwner, 3);
-
- if (wakeEvents & WL_TIMEOUT)
- Assert(timeout >= 0);
- else
- timeout = -1;
-
- if (wakeEvents & WL_LATCH_SET)
- AddWaitEventToSet(set, WL_LATCH_SET, PGINVALID_SOCKET,
- latch, NULL);
-
- /* Postmaster-managed callers must handle postmaster death somehow. */
- Assert(!IsUnderPostmaster ||
- (wakeEvents & WL_EXIT_ON_PM_DEATH) ||
- (wakeEvents & WL_POSTMASTER_DEATH));
-
- if ((wakeEvents & WL_POSTMASTER_DEATH) && IsUnderPostmaster)
- AddWaitEventToSet(set, WL_POSTMASTER_DEATH, PGINVALID_SOCKET,
- NULL, NULL);
-
- if ((wakeEvents & WL_EXIT_ON_PM_DEATH) && IsUnderPostmaster)
- AddWaitEventToSet(set, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET,
- NULL, NULL);
-
- if (wakeEvents & WL_SOCKET_MASK)
- {
- int ev;
-
- ev = wakeEvents & WL_SOCKET_MASK;
- AddWaitEventToSet(set, ev, sock, NULL, NULL);
- }
-
- rc = WaitEventSetWait(set, timeout, &event, 1, wait_event_info);
-
- if (rc == 0)
- ret |= WL_TIMEOUT;
- else
- {
- ret |= event.events & (WL_LATCH_SET |
- WL_POSTMASTER_DEATH |
- WL_SOCKET_MASK);
- }
-
- FreeWaitEventSet(set);
-
- return ret;
-}
-
-/*
- * Sets a latch and wakes up anyone waiting on it.
- *
- * This is cheap if the latch is already set, otherwise not so much.
- *
- * 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: this function is called from critical sections and signal handlers so
- * throwing an error is not a good idea.
- */
-void
-SetLatch(Latch *latch)
-{
-#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)
- WakeupMyProc();
- else
- WakeupOtherProc(owner_pid);
-
-#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().
- */
- }
-#endif
-}
-
-/*
- * Clear the latch. Calling WaitLatch after this will sleep, unless
- * the latch is set again before the WaitLatch call.
- */
-void
-ResetLatch(Latch *latch)
-{
- /* Only the owner should reset the latch */
- Assert(latch->owner_pid == MyProcPid);
- Assert(latch->maybe_sleeping == false);
-
- latch->is_set = false;
-
- /*
- * 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.
- */
- pg_memory_barrier();
-}
diff --git a/src/backend/storage/ipc/meson.build b/src/backend/storage/ipc/meson.build
index b1b73dac3be..3215830d41b 100644
--- a/src/backend/storage/ipc/meson.build
+++ b/src/backend/storage/ipc/meson.build
@@ -7,7 +7,6 @@ backend_sources += files(
'dsm_registry.c',
'ipc.c',
'ipci.c',
- 'latch.c',
'pmsignal.c',
'procarray.c',
'procsignal.c',
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 7d201965503..ddbb041ff9f 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -22,11 +22,11 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "port/pg_bitutils.h"
+#include "postmaster/interrupt.h"
#include "replication/logicalworker.h"
#include "replication/walsender.h"
#include "storage/condition_variable.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/shmem.h"
#include "storage/sinval.h"
#include "storage/smgr.h"
@@ -483,7 +483,7 @@ HandleProcSignalBarrierInterrupt(void)
{
InterruptPending = true;
ProcSignalBarrierPending = true;
- /* latch will be set by procsignal_sigusr1_handler */
+ /* interrupt will be raised by procsignal_sigusr1_handler */
}
/*
@@ -714,7 +714,7 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 2c79a649f46..0a9879d7024 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 "postmaster/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, 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, 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, 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, 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, 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(INTERRUPT_GENERAL, 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);
/* 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(INTERRUPT_GENERAL, 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);
/* 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(INTERRUPT_GENERAL, 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);
/* 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, GetNumberFromPGProc(sender));
}
/*
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index bebc97ecffd..ab22e0b981d 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -19,6 +19,7 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/interrupt.h"
#include "postmaster/syslogger.h"
#include "storage/pmsignal.h"
#include "storage/proc.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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ waittime,
+ WAIT_EVENT_BACKEND_TERMINATION);
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
remainingtime -= waittime;
} while (remainingtime > 0);
diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c
index 6eea8e87169..4a66e1b7f7c 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 "postmaster/interrupt.h"
#include "storage/sinvaladt.h"
#include "utils/inval.h"
@@ -31,8 +31,8 @@ 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
+ * The signal handler will set an interrupt pending flag and raise the
+ * INTERRUPT_GENERAL. Whenever starting to read from the client, or when
* interrupted while doing so, ProcessClientReadInterrupt() will call
* ProcessCatchupEvent().
*/
@@ -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.
*/
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);
}
/*
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 5acb4508f85..1ef7600522c 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -24,6 +24,7 @@
#include "access/xlogutils.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/interrupt.h"
#include "replication/slot.h"
#include "storage/bufmgr.h"
#include "storage/proc.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(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ PG_WAIT_LOCK | locktag.locktag_type);
+ ClearInterrupt(INTERRUPT_GENERAL);
+ 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(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ PG_WAIT_LOCK | locktag.locktag_type);
+ ClearInterrupt(INTERRUPT_GENERAL);
+ 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(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ WAIT_EVENT_BUFFER_PIN);
+ ClearInterrupt(INTERRUPT_GENERAL);
+ CHECK_FOR_INTERRUPTS();
if (got_standby_delay_timeout)
SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
diff --git a/src/backend/storage/ipc/waiteventset.c b/src/backend/storage/ipc/waiteventset.c
index 7c0e66900f9..1eba76a19c4 100644
--- a/src/backend/storage/ipc/waiteventset.c
+++ b/src/backend/storage/ipc/waiteventset.c
@@ -8,8 +8,8 @@
* pselect() (as opposed to plain poll()/select()).
*
* You can wait for:
- * - a latch being set from another process or from signal handler in the same
- * process (WL_LATCH_SET)
+ * - 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)
@@ -17,15 +17,16 @@
* 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 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.
+ * 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
@@ -68,11 +69,12 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "portability/instr_time.h"
+#include "postmaster/interrupt.h"
#include "postmaster/postmaster.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/pmsignal.h"
-#include "storage/latch.h"
+#include "storage/proc.h"
#include "storage/waiteventset.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
@@ -127,13 +129,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
@@ -166,7 +168,7 @@ struct WaitEventSet
};
#ifndef WIN32
-/* Are we currently in WaitLatch? The signal handler would like to know. */
+/* Are we currently on a WaitEventSet? The signal handler would like to know. */
static volatile sig_atomic_t waiting = false;
#endif
@@ -182,9 +184,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
@@ -234,7 +243,7 @@ ResourceOwnerForgetWaitEventSet(ResourceOwner owner, WaitEventSet *set)
* 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
InitializeWaitEventSupport(void)
@@ -284,12 +293,12 @@ InitializeWaitEventSupport(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");
@@ -310,7 +319,7 @@ InitializeWaitEventSupport(void)
ReserveExternalFD();
ReserveExternalFD();
- pqsignal(SIGURG, latch_sigurg_handler);
+ pqsignal(SIGURG, interrupt_sigurg_handler);
#endif
#ifdef WAIT_USE_SIGNALFD
@@ -348,6 +357,12 @@ InitializeWaitEventSupport(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
}
/*
@@ -411,7 +426,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;
@@ -496,9 +512,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)
{
@@ -535,7 +551,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
@@ -553,10 +569,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.
@@ -566,7 +578,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;
@@ -580,19 +592,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 */
@@ -608,10 +617,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)
@@ -645,14 +654,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)
@@ -688,34 +697,28 @@ ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch)
* 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 && events != event->events)
- elog(ERROR, "cannot modify latch event");
+ if (event->events & WL_INTERRUPT && events != event->events)
+ elog(ERROR, "cannot modify interrupts event");
/* 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 wake us up
+ * if the SLEEPING_ON_INTERRUPTS bit is not armed, though, so it
+ * should be rare.
*/
-#if defined(WAIT_USE_WIN32)
- if (!latch)
- return;
-#else
return;
-#endif
}
#if defined(WAIT_USE_EPOLL)
@@ -745,9 +748,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)
@@ -794,9 +796,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)
@@ -858,9 +859,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;
@@ -886,8 +887,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 |
@@ -902,10 +902,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
{
@@ -983,10 +983,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)
{
@@ -1042,6 +1045,7 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
instr_time start_time;
instr_time cur_time;
long cur_timeout = -1;
+ bool sleeping_flag_armed = false;
Assert(nevents > 0);
@@ -1063,64 +1067,57 @@ 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)
+
+ /*
+ * Atomically check if the interrupt is already pending and advertise that
+ * we are about to start sleeping. If it was already pending, avoid
+ * blocking altogether.
+ *
+ * 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.
+ */
+ if (set->interrupt_mask != 0)
{
- int rc;
+ uint32 old_mask;
+ bool already_pending = false;
/*
- * Check if the latch is set already first. If so, we either exit
- * immediately or ask the kernel for further events available right
- * now without waiting, depending on how many events the caller wants.
- *
- * If someone sets the latch 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
- * self pipe isn't filled unless we're blocking (waiting = true), or
- * from inside a signal handler in latch_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.".
- *
- * 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.
+ * Perform a plain atomic read first as a fast path for the case that
+ * an interrupt is already pending.
*/
- if (set->latch && !set->latch->is_set)
+ old_mask = pg_atomic_read_u32(MyPendingInterrupts);
+ already_pending = ((old_mask & set->interrupt_mask) != 0);
+
+ if (!already_pending)
{
- /* about to sleep on a latch */
- set->latch->maybe_sleeping = true;
- pg_memory_barrier();
- /* and recheck */
+ /*
+ * Atomically set the SLEEPING_ON_INTERRUPTS bit and re-check if
+ * an interrupt is already pending. The atomic op provides
+ * synchronization so that if an interrupt bit is set after this,
+ * the setter will wake us up.
+ */
+ old_mask = pg_atomic_fetch_or_u32(MyPendingInterrupts, 1 << SLEEPING_ON_INTERRUPTS);
+ already_pending = ((old_mask & set->interrupt_mask) != 0);
+
+ /* remember to clear the SLEEPING_ON_INTERRUPTS flag later */
+ sleeping_flag_armed = true;
}
- if (set->latch && set->latch->is_set)
+ if (already_pending)
{
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;
-
- if (returned_events == nevents)
- break; /* output buffer full already */
-
/*
* Even though we already have an event, we'll poll just once with
* zero timeout to see what non-latch events we can fit into the
@@ -1129,34 +1126,58 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
cur_timeout = 0;
timeout = 0;
}
+ }
+ if (nevents > returned_events)
+ {
/*
* Wait for events using the readiness primitive chosen at the top of
* this file. If -1 is returned, a timeout has occurred, if 0 we have
* to retry, everything >= 1 is the number of returned events.
+ *
+ * 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 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 interrupt_sigurg_handler().
+ *
+ * On windows, we'll also notice if there's a pending event for the
+ * interrupt when blocking, but there's no danger of anything filling up,
+ * as "Setting an event that is already set has no effect.".
*/
- rc = WaitEventSetWaitBlock(set, cur_timeout,
- occurred_events, nevents - returned_events);
+ for (;;)
+ {
+ int rc;
- if (set->latch &&
- set->latch->maybe_sleeping)
- set->latch->maybe_sleeping = false;
+ rc = WaitEventSetWaitBlock(set, cur_timeout,
+ occurred_events, nevents - returned_events);
- if (rc == -1)
- break; /* timeout occurred */
- else
- returned_events += rc;
+ if (rc == -1)
+ break; /* timeout occurred */
+ else
+ returned_events += rc;
- /* If we're not done, update cur_timeout for next iteration */
- if (returned_events == 0 && timeout >= 0)
- {
- INSTR_TIME_SET_CURRENT(cur_time);
- INSTR_TIME_SUBTRACT(cur_time, start_time);
- cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
- if (cur_timeout <= 0)
+ if (returned_events > 0)
break;
+
+ /* If we're not done, update cur_timeout for next iteration */
+ if (timeout >= 0)
+ {
+ INSTR_TIME_SET_CURRENT(cur_time);
+ INSTR_TIME_SUBTRACT(cur_time, start_time);
+ cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
+ if (cur_timeout <= 0)
+ break;
+ }
}
}
+
+ /* If we set the SLEEPING_ON_INTERRUPTS flag, reset it again */
+ if (sleeping_flag_armed)
+ pg_atomic_fetch_and_u32(MyPendingInterrupts, ~((uint32) 1 << SLEEPING_ON_INTERRUPTS));
+
#ifndef WIN32
waiting = false;
#endif
@@ -1227,16 +1248,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->maybe_sleeping && 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++;
}
@@ -1389,13 +1410,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->maybe_sleeping && 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++;
}
@@ -1511,16 +1532,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->maybe_sleeping && 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++;
}
@@ -1619,6 +1640,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
@@ -1724,19 +1754,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->maybe_sleeping && 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++;
}
@@ -1781,7 +1807,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
@@ -1887,18 +1913,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)
{
@@ -1915,7 +1940,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;
@@ -2002,14 +2027,13 @@ ResOwnerReleaseWaitEventSet(Datum res)
FreeWaitEventSet(set);
}
-#ifndef WIN32
/*
* Wake up my process if it's currently sleeping in WaitEventSetWaitBlock()
*
* 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.
*
@@ -2018,6 +2042,7 @@ ResOwnerReleaseWaitEventSet(Datum res)
void
WakeupMyProc(void)
{
+#ifndef WIN32
#if defined(WAIT_USE_SELF_PIPE)
if (waiting)
sendSelfPipeByte();
@@ -2025,12 +2050,23 @@ WakeupMyProc(void)
if (waiting)
kill(MyProcPid, SIGURG);
#endif
+#else
+ SetEvent(MyProc ? MyProc->interruptWakeupEvent : LocalInterruptWakeupEvent);
+#endif
}
/* Similar to WakeupMyProc, but wake up another process */
void
-WakeupOtherProc(int pid)
+WakeupOtherProc(PGPROC *proc)
{
- kill(pid, SIGURG);
-}
+#ifndef WIN32
+ kill(proc->pid, SIGURG);
+#else
+ SetEvent(proc->interruptWakeupEvent);
+
+ /*
+ * 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().
+ */
#endif
+}
diff --git a/src/backend/storage/lmgr/condition_variable.c b/src/backend/storage/lmgr/condition_variable.c
index 228303e77f7..295914cf032 100644
--- a/src/backend/storage/lmgr/condition_variable.c
+++ b/src/backend/storage/lmgr/condition_variable.c
@@ -20,6 +20,7 @@
#include "miscadmin.h"
#include "portability/instr_time.h"
+#include "postmaster/interrupt.h"
#include "storage/condition_variable.h"
#include "storage/proc.h"
#include "storage/proclist.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(INTERRUPT_GENERAL, 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);
/*
* 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, 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, 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, GetNumberFromPGProc(proc));
}
}
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 5b21a053981..ab9b46b9cb9 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 "postmaster/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(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ WAIT_EVENT_SAFE_SNAPSHOT);
+ ClearInterrupt(INTERRUPT_GENERAL);
+ 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, roXact->pgprocno);
}
}
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 749a79d48ef..4ebff9a4b37 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -39,6 +39,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
+#include "postmaster/interrupt.h"
#include "replication/slotsync.h"
#include "replication/syncrep.h"
#include "storage/condition_variable.h"
@@ -258,14 +259,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);
}
@@ -479,13 +494,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);
@@ -649,13 +659,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);
@@ -932,21 +937,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;
@@ -1009,13 +1013,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);
@@ -1342,18 +1345,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
{
@@ -1399,9 +1402,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(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ PG_WAIT_LOCK | locallock->tag.lock.locktag_type);
+ ClearInterrupt(INTERRUPT_GENERAL);
/* check for deadlocks first, as that's probably log-worthy */
if (got_deadlock_timeout)
{
@@ -1693,7 +1696,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.
*
@@ -1722,7 +1725,7 @@ ProcWakeup(PGPROC *proc, ProcWaitStatus waitStatus)
pg_atomic_write_u64(&MyProc->waitStart, 0);
/* And awaken it */
- SetLatch(&proc->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, GetNumberFromPGProc(proc));
}
/*
@@ -1874,45 +1877,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);
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 fc16db90133..e61df4675c9 100644
--- a/src/backend/storage/sync/sync.c
+++ b/src/backend/storage/sync/sync.c
@@ -26,8 +26,8 @@
#include "pgstat.h"
#include "portability/instr_time.h"
#include "postmaster/bgwriter.h"
+#include "postmaster/interrupt.h"
#include "storage/fd.h"
-#include "storage/latch.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 947ffb40421..8784ea78dcd 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -519,15 +519,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);
}
errno = save_errno;
@@ -553,9 +553,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)
@@ -579,7 +579,7 @@ ProcessClientWriteInterrupt(bool blocked)
}
}
else
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
errno = save_errno;
@@ -3012,12 +3012,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);
/*
* 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.
*/
@@ -3041,8 +3041,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);
}
/* signal handler for floating point exception */
@@ -3068,7 +3068,7 @@ HandleRecoveryConflictInterrupt(ProcSignalReason reason)
RecoveryConflictPendingReasons[reason] = true;
RecoveryConflictPending = true;
InterruptPending = true;
- /* latch will be set by procsignal_sigusr1_handler */
+ /* INTERRUPT_GENERAL will be raised by procsignal_sigusr1_handler */
}
/*
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 6fcfd031428..d7e9da57bd1 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -35,10 +35,10 @@
#include "parser/parse_type.h"
#include "parser/scansup.h"
#include "pgstat.h"
+#include "postmaster/interrupt.h"
#include "postmaster/syslogger.h"
#include "rewrite/rewriteHandler.h"
#include "storage/fd.h"
-#include "storage/latch.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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ delay_ms,
+ WAIT_EVENT_PG_SLEEP);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
PG_RETURN_VOID();
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 9682f9dbdca..e4d145b0529 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 b844f9fdaef..79bc999cf26 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 dc3521457c7..81664fc11ff 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -43,7 +43,6 @@
#include "replication/slotsync.h"
#include "storage/fd.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
@@ -66,8 +65,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
*
@@ -126,10 +123,9 @@ InitPostmasterChild(void)
pqinitmask();
#endif
- /* Initialize process-local latch support */
+ /* Initialize process-local interrupt support */
InitializeWaitEventSupport();
- InitProcessLocalLatch();
- InitializeLatchWaitSet();
+ InitializeInterruptWaitSet();
/*
* If possible, make this process a group leader, so that the postmaster
@@ -187,10 +183,9 @@ InitStandaloneProcess(const char *argv0)
InitProcessGlobals();
- /* Initialize process-local latch support */
+ /* Initialize process-local interrupt support */
InitializeWaitEventSupport();
- InitProcessLocalLatch();
- InitializeLatchWaitSet();
+ InitializeInterruptWaitSet();
/*
* For consistency with InitPostmasterChild, initialize signal mask here.
@@ -211,48 +206,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 ee1a9d5d98b..e11101a36b0 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -39,6 +39,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
+#include "postmaster/interrupt.h"
#include "postmaster/postmaster.h"
#include "replication/slot.h"
#include "replication/slotsync.h"
@@ -1383,7 +1384,7 @@ TransactionTimeoutHandler(void)
{
TransactionTimeoutPending = true;
InterruptPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
static void
@@ -1391,7 +1392,7 @@ IdleInTransactionSessionTimeoutHandler(void)
{
IdleInTransactionSessionTimeoutPending = true;
InterruptPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
static void
@@ -1399,7 +1400,7 @@ IdleSessionTimeoutHandler(void)
{
IdleSessionTimeoutPending = true;
InterruptPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
static void
@@ -1407,7 +1408,7 @@ IdleStatsUpdateTimeoutHandler(void)
{
IdleStatsUpdateTimeoutPending = true;
InterruptPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
static void
@@ -1415,7 +1416,7 @@ ClientCheckTimeoutHandler(void)
{
CheckClientConnectionPending = true;
InterruptPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index d92efa12550..e90d82c7ddc 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 "postmaster/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.
*/
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
/*
* 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 91060de0ab7..5233a65aaa0 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 will be raised by procsignal_sigusr1_handler */
}
/*
diff --git a/src/include/access/parallel.h b/src/include/access/parallel.h
index f37be6d5690..a257d0a8ee6 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 16205b824fa..dd361be4afd 100644
--- a/src/include/libpq/libpq-be-fe-helpers.h
+++ b/src/include/libpq/libpq-be-fe-helpers.h
@@ -42,8 +42,8 @@
#include "libpq-fe.h"
#include "miscadmin.h"
+#include "postmaster/interrupt.h"
#include "storage/fd.h"
-#include "storage/latch.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(INTERRUPT_GENERAL,
+ 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);
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(INTERRUPT_GENERAL,
+ 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);
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(INTERRUPT_GENERAL, waitEvents,
+ PQcancelSocket(cancel_conn),
+ cur_timeout, PG_WAIT_CLIENT);
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
}
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index aeb66ca40cf..6de03ce1163 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 a2b63495eec..f5b773b79e5 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;
@@ -323,9 +322,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/postmaster/interrupt.h b/src/include/postmaster/interrupt.h
index 3bf49320846..346f9cae8e1 100644
--- a/src/include/postmaster/interrupt.h
+++ b/src/include/postmaster/interrupt.h
@@ -1,13 +1,68 @@
/*-------------------------------------------------------------------------
*
* interrupt.h
- * Interrupt handling routines.
+ * Inter-process interrupts
*
- * Responses to interrupts are fairly varied and many types of backends
- * have their own implementations, but we provide a few generic things
- * here to facilitate code reuse.
+ * "Interrupts" are a set of flags that represent conditions that should be
+ * handled at a later time. They are roughly analogous to Unix signals,
+ * except that they are handled cooperatively by checking for them at many
+ * points in the code.
*
- * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * 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 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 is:
+ *
+ * for (;;)
+ * {
+ * ClearInterrupt(INTERRUPT_GENERAL);
+ * if (work to do)
+ * Do Stuff();
+ * WaitInterrupt(INTERRUPT_GENERAL, ...);
+ * }
+ *
+ * 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(INTERRUPT_GENERAL, ...);
+ * ClearInterrupt(INTERRUPT_GENERAL);
+ * }
+ *
+ * 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) *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.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
@@ -19,11 +74,97 @@
#ifndef INTERRUPT_H
#define 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 volatile sig_atomic_t ConfigReloadPending;
extern PGDLLIMPORT volatile sig_atomic_t ShutdownRequestPending;
+/*
+ * Flags in the pending interrupts bitmask. Each value represents one bit in
+ * the bitmask.
+ */
+typedef enum
+{
+ /*
+ * SLEEPING_ON_INTERRUPTS indicates that the backend is currently blocked
+ * waiting for an interrupt. If it's set, the backend needs to be woken up
+ * when a bit in the pending interrupts mask is set. It's used internally
+ * by the interrupt machinery, and cannot be used directly in the public
+ * functions. It's named differently to distinguish it from the actual
+ * interrupt flags.
+ */
+ SLEEPING_ON_INTERRUPTS = 1 << 0,
+
+ /*
+ * INTERRUPT_GENERAL is multiplexed for many reasons, like query
+ * cancellation termination requests, recovery conflicts, and config
+ * reload requests. Upon receiving INTERRUPT_GENERAL, 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 = 1 << 1,
+
+ /*
+ * 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 = 1 << 2,
+} InterruptType;
+
+/*
+ * Test an interrupt flag (of flags).
+ */
+static inline bool
+IsInterruptPending(uint32 interruptMask)
+{
+ return (pg_atomic_read_u32(MyPendingInterrupts) & interruptMask) != 0;
+}
+
+/*
+ * Clear an interrupt flag.
+ */
+static inline void
+ClearInterrupt(uint32 interruptMask)
+{
+ pg_atomic_fetch_and_u32(MyPendingInterrupts, ~interruptMask);
+}
+
+/*
+ * Test and clear an interrupt flag.
+ */
+static inline bool
+ConsumeInterrupt(uint32 interruptMask)
+{
+ if (likely(!IsInterruptPending(interruptMask)))
+ return false;
+
+ ClearInterrupt(interruptMask);
+
+ return true;
+}
+
+extern PGDLLIMPORT volatile sig_atomic_t ConfigReloadPending;
+extern PGDLLIMPORT volatile sig_atomic_t ShutdownRequestPending;
+
+extern void RaiseInterrupt(uint32 interruptMask);
+extern void SendInterrupt(uint32 interruptMask, 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);
+
extern void ProcessMainLoopInterrupts(void);
extern void SignalHandlerForConfigReload(SIGNAL_ARGS);
extern void SignalHandlerForCrashExit(SIGNAL_ARGS);
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
deleted file mode 100644
index e41dc70785a..00000000000
--- a/src/include/storage/latch.h
+++ /dev/null
@@ -1,140 +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.
- *
- *
- * See also WaitEventSets in waiteventset.h. They 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-2025, 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 "storage/waiteventset.h" /* for WL_* arguments to WaitLatch */
-
-/*
- * 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;
-
-/*
- * prototypes for functions in latch.c
- */
-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 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);
-
-#endif /* LATCH_H */
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 114eb1f8f76..3a9221cfbc5 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"
@@ -173,9 +172,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.
@@ -311,6 +307,13 @@ 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;
+
+#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. */
@@ -413,6 +416,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;
@@ -490,9 +495,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
index aa65b7a35e7..60ececb785c 100644
--- a/src/include/storage/waiteventset.h
+++ b/src/include/storage/waiteventset.h
@@ -3,18 +3,17 @@
* waiteventset.h
* ppoll() / pselect() like interface for waiting for events
*
- * WaitEventSets allow to wait for latches being set and additional events -
+ * 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 WaitLatch or WaitLatchOrSocket.
*
- * WaitEventSetWait includes a provision for timeouts (which should be avoided
+ * 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
- * storage/ipc/waiteventset.c for detailed specifications for the exported
+ * storage/ipc/interrupt.c for detailed specifications for the exported
* functions.
*
- *
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -25,13 +24,16 @@
#ifndef WAITEVENTSET_H
#define WAITEVENTSET_H
+#include <signal.h>
+
+#include "storage/procnumber.h"
#include "utils/resowner.h"
/*
- * Bitmasks for events that may wake-up WaitLatch(), WaitLatchOrSocket(), or
- * WaitEventSetWait().
+ * Bitmasks for events that may wake-up WaitInterrupt(),
+ * WaitInterruptOrSocket(), or WaitEventSetWait().
*/
-#define WL_LATCH_SET (1 << 0)
+#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() */
@@ -67,32 +69,35 @@ typedef struct WaitEvent
#endif
} WaitEvent;
-/* forward declarations to avoid exposing waiteventset.c implementation details */
+/* forward declaration to avoid exposing interrupt.c implementation details */
typedef struct WaitEventSet WaitEventSet;
-struct Latch;
+struct PGPROC;
/*
* prototypes for functions in waiteventset.c
*/
extern void InitializeWaitEventSupport(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,
- struct Latch *latch, void *user_data);
-extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events,
- struct Latch *latch);
+ 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);
-#ifndef WIN32
+/* low level functions used to implement SendInterrupt */
extern void WakeupMyProc(void);
-extern void WakeupOtherProc(int pid);
-#endif
+extern void WakeupOtherProc(struct PGPROC *proc);
#endif /* WAITEVENTSET_H */
diff --git a/src/port/pgsleep.c b/src/port/pgsleep.c
index dfe824fe462..404d4086057 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 e01c0c9de93..03078078893 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 2a20ffb1273..1d019faa2e5 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 "postmaster/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(INTERRUPT_GENERAL, 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);
/* 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 443281addd0..240fe315d47 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 "postmaster/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(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ we_message_queue);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
}
}
diff --git a/src/test/modules/test_shm_mq/worker.c b/src/test/modules/test_shm_mq/worker.c
index 96cd304dbbc..751cac60513 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 "postmaster/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(®istrant->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, 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 5b87d4f7038..1c23b4eadeb 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,6 @@
#include "miscadmin.h"
#include "postmaster/bgworker.h"
#include "postmaster/interrupt.h"
-#include "storage/latch.h"
/* these headers are used by this particular worker's code */
#include "access/xact.h"
@@ -213,15 +212,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(INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ worker_spi_naptime * 1000L,
+ worker_spi_wait_event_main);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9840060997f..93cf3ea31dd 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1271,6 +1271,7 @@ Integer
IntegerSet
InternalDefaultACL
InternalGrant
+InterruptType
Interval
IntervalAggState
IntoClause
@@ -1509,7 +1510,6 @@ LZ4State
LabelProvider
LagTracker
LargeObjectDesc
-Latch
LauncherLastStartTimesEntry
LerpFunc
LexDescr
--
2.39.5
[text/x-patch] v7-0002-Fix-lost-wakeup-issue-in-logical-replication-laun.patch (4.1K, ../[email protected]/3-v7-0002-Fix-lost-wakeup-issue-in-logical-replication-laun.patch)
download | inline diff:
From 2425bf35af1e7bc690830fd0875fc7e08f274ae2 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Mon, 2 Dec 2024 15:55:18 +0200
Subject: [PATCH v7 2/4] Fix lost wakeup issue in logical replication launcher
Fix it by using a separate interrupt bit for subscription changes.
Discussion: https://www.postgresql.org/message-id/[email protected]
Discussion: https://www.postgresql.org/message-id/[email protected]
---
src/backend/replication/logical/launcher.c | 22 ++++++++++++++--------
src/include/postmaster/interrupt.h | 5 ++++-
2 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 071d93ce816..d915b9a9fce 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -56,7 +56,7 @@ LogicalRepWorker *MyLogicalRepWorker = NULL;
typedef struct LogicalRepCtxStruct
{
/* Supervisor process. */
- pid_t launcher_pid;
+ ProcNumber launcher_procno;
/* Hash table holding last start times of subscriptions' apply workers. */
dsa_handle last_start_dsa;
@@ -804,7 +804,7 @@ logicalrep_worker_cleanup(LogicalRepWorker *worker)
static void
logicalrep_launcher_onexit(int code, Datum arg)
{
- LogicalRepCtx->launcher_pid = 0;
+ LogicalRepCtx->launcher_procno = INVALID_PROC_NUMBER;
}
/*
@@ -964,6 +964,7 @@ ApplyLauncherShmemInit(void)
memset(LogicalRepCtx, 0, ApplyLauncherShmemSize());
+ LogicalRepCtx->launcher_procno = INVALID_PROC_NUMBER;
LogicalRepCtx->last_start_dsa = DSA_HANDLE_INVALID;
LogicalRepCtx->last_start_dsh = DSHASH_HANDLE_INVALID;
@@ -1109,8 +1110,12 @@ ApplyLauncherWakeupAtCommit(void)
static void
ApplyLauncherWakeup(void)
{
- if (LogicalRepCtx->launcher_pid != 0)
- kill(LogicalRepCtx->launcher_pid, SIGUSR1);
+ volatile LogicalRepCtxStruct *repctx = LogicalRepCtx;
+ ProcNumber launcher_procno;
+
+ launcher_procno = repctx->launcher_procno;
+ if (launcher_procno != INVALID_PROC_NUMBER)
+ SendInterrupt(INTERRUPT_SUBSCRIPTION_CHANGE, launcher_procno);
}
/*
@@ -1124,8 +1129,8 @@ ApplyLauncherMain(Datum main_arg)
before_shmem_exit(logicalrep_launcher_onexit, (Datum) 0);
- Assert(LogicalRepCtx->launcher_pid == 0);
- LogicalRepCtx->launcher_pid = MyProcPid;
+ Assert(LogicalRepCtx->launcher_procno == INVALID_PROC_NUMBER);
+ LogicalRepCtx->launcher_procno = MyProcNumber;
/* Establish signal handlers. */
pqsignal(SIGHUP, SignalHandlerForConfigReload);
@@ -1157,6 +1162,7 @@ ApplyLauncherMain(Datum main_arg)
oldctx = MemoryContextSwitchTo(subctx);
/* Start any missing workers for enabled subscriptions. */
+ ClearInterrupt(INTERRUPT_SUBSCRIPTION_CHANGE);
sublist = get_subscription_list();
foreach(lc, sublist)
{
@@ -1213,7 +1219,7 @@ ApplyLauncherMain(Datum main_arg)
MemoryContextDelete(subctx);
/* Wait for more work. */
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_GENERAL | INTERRUPT_SUBSCRIPTION_CHANGE,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
wait_time,
WAIT_EVENT_LOGICAL_LAUNCHER_MAIN);
@@ -1240,7 +1246,7 @@ ApplyLauncherMain(Datum main_arg)
bool
IsLogicalLauncher(void)
{
- return LogicalRepCtx->launcher_pid == MyProcPid;
+ return LogicalRepCtx->launcher_procno == MyProcNumber;
}
/*
diff --git a/src/include/postmaster/interrupt.h b/src/include/postmaster/interrupt.h
index 346f9cae8e1..09dc6f0330d 100644
--- a/src/include/postmaster/interrupt.h
+++ b/src/include/postmaster/interrupt.h
@@ -113,11 +113,14 @@ typedef enum
INTERRUPT_GENERAL = 1 << 1,
/*
- * INTERRUPT_RECOVERY_WAKEUP is used to wake up startup process, to tell
+ * INTERRUPT_RECOVERY_CONTINUE 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 = 1 << 2,
+
+ /* sent to logical replication launcher, when a subscription changes */
+ INTERRUPT_SUBSCRIPTION_CHANGE = 1 << 3,
} InterruptType;
/*
--
2.39.5
[text/x-patch] v7-0003-Use-INTERRUPT_GENERAL-for-bgworker-state-change-n.patch (23.9K, ../[email protected]/4-v7-0003-Use-INTERRUPT_GENERAL-for-bgworker-state-change-n.patch)
download | inline diff:
From 0d2e581acb90d411a58994d3119963261cdf9b87 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Mon, 2 Dec 2024 15:55:21 +0200
Subject: [PATCH v7 3/4] Use INTERRUPT_GENERAL for bgworker state change
notification
As part of a project to remove the reliance on PIDs to identify
backends, backends that register dynamic background workers will now
receive INTERRUPT_GENERAL sent directly by the postmaster when the
worker state changes. Previously, the postmaster would send a SIGUSR1
signal, which relied on the ProcSignal system's handler setting the
interrupt flag, with the same end effect. Now that intermediate step
is skipped.
The field bgw_notify_pid still exists for backwards compatibility, but
has no effect and may be removed in a later release.
RegisterBackgroundWorker() now automatically assumes that the caller
would like state change notifications delivered to its proc number,
unless BGW_NO_NOTIFY is set in bgw_flags.
Removing other cases of PIDs from the bgworker API is left for later
work; this patch is concerned only with removing a dependency on
ProcSignal infrastructure that is due to be removed by a later commit.
This represents the first use of SendInterrupt() in the postmaster.
It might seem more natural to use condition variables in the
wait-for-state-change functions, so that anyone with a handle can
wait, but condition variables as currently implemented would be a lot
less robust than simple interrupts.
Author: Thomas Munro <[email protected]>
Reviewed-by: Heikki Linnakangas <[email protected]>
Discussion: https://postgr.es/m/CA%2BhUKG%2B3MkS21yK4jL4cgZywdnnGKiBg0jatoV6kzaniBmcqbQ%40mail.gmail.com
---
contrib/pg_prewarm/autoprewarm.c | 6 -
doc/src/sgml/bgworker.sgml | 27 +++--
src/backend/access/transam/parallel.c | 1 -
src/backend/postmaster/bgworker.c | 128 +++++++++++---------
src/backend/postmaster/interrupt.c | 4 +-
src/backend/postmaster/postmaster.c | 8 +-
src/backend/replication/logical/launcher.c | 2 -
src/backend/storage/ipc/waiteventset.c | 5 +
src/include/postmaster/bgworker.h | 10 +-
src/include/postmaster/bgworker_internals.h | 4 +-
src/test/modules/test_shm_mq/setup.c | 3 +-
src/test/modules/test_shm_mq/test_shm_mq.h | 2 +
src/test/modules/test_shm_mq/worker.c | 9 +-
src/test/modules/worker_spi/worker_spi.c | 3 -
14 files changed, 112 insertions(+), 100 deletions(-)
diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index 20dc7885d6a..a52058be9bb 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -816,9 +816,6 @@ apw_start_leader_worker(void)
return;
}
- /* must set notify PID to wait for startup */
- worker.bgw_notify_pid = MyProcPid;
-
if (!RegisterDynamicBackgroundWorker(&worker, &handle))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
@@ -852,9 +849,6 @@ apw_start_database_worker(void)
strcpy(worker.bgw_name, "autoprewarm worker");
strcpy(worker.bgw_type, "autoprewarm worker");
- /* must set notify PID to wait for shutdown */
- worker.bgw_notify_pid = MyProcPid;
-
if (!RegisterDynamicBackgroundWorker(&worker, &handle))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 3171054e55d..871f6869d6a 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -63,7 +63,7 @@ typedef struct BackgroundWorker
char bgw_function_name[BGW_MAXLEN];
Datum bgw_main_arg;
char bgw_extra[BGW_EXTRALEN];
- pid_t bgw_notify_pid;
+ pid_t bgw_notify_pid; /* not used */
} BackgroundWorker;
</programlisting>
</para>
@@ -108,6 +108,18 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_NO_NOTIFY</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_NO_NOTIFY</primary></indexterm>
+ Normally, the backend that registers a dynamic worker will be notified
+ with <literal>INTERRUPT_GENERAL</literal> when the workers state changes, which allows the
+ caller to wait for the worker to start and shut down. That can be
+ suppressed by setting this flag.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
@@ -181,12 +193,8 @@ typedef struct BackgroundWorker
</para>
<para>
- <structfield>bgw_notify_pid</structfield> is the PID of a PostgreSQL
- backend process to which the postmaster should send <literal>SIGUSR1</literal>
- when the process is started or exits. It should be 0 for workers registered
- at postmaster startup time, or when the backend registering the worker does
- not wish to wait for the worker to start up. Otherwise, it should be
- initialized to <literal>MyProcPid</literal>.
+ <structfield>bgw_notify_pid</structfield> is not used and may be removed
+ in a future release.
</para>
<para>Once running, the process can connect to a database by calling
@@ -258,10 +266,7 @@ typedef struct BackgroundWorker
<para>
In some cases, a process which registers a background worker may wish to
- wait for the worker to start up. This can be accomplished by initializing
- <structfield>bgw_notify_pid</structfield> to <literal>MyProcPid</literal> and
- then passing the <type>BackgroundWorkerHandle *</type> obtained at
- registration time to
+ wait for the worker to start up. This can be accomplished with the
<function>WaitForBackgroundWorkerStartup(<parameter>BackgroundWorkerHandle
*handle</parameter>, <parameter>pid_t *</parameter>)</function> function.
This function will block until the postmaster has attempted to start the
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index 30cdce00b47..c4a1f69c619 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -604,7 +604,6 @@ LaunchParallelWorkers(ParallelContext *pcxt)
sprintf(worker.bgw_library_name, "postgres");
sprintf(worker.bgw_function_name, "ParallelWorkerMain");
worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(pcxt->seg));
- worker.bgw_notify_pid = MyProcPid;
/*
* Start workers.
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index c82971e9274..4425e404a35 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -78,6 +78,8 @@ typedef struct BackgroundWorkerSlot
pid_t pid; /* InvalidPid = not started yet; 0 = dead */
uint64 generation; /* incremented when slot is recycled */
BackgroundWorker worker;
+ int notify_pmchild;
+ ProcNumber notify_proc_number;
} BackgroundWorkerSlot;
/*
@@ -192,8 +194,9 @@ BackgroundWorkerShmemInit(void)
slot->terminate = false;
slot->pid = InvalidPid;
slot->generation = 0;
+ slot->notify_pmchild = 0;
+ slot->notify_proc_number = INVALID_PROC_NUMBER;
rw->rw_shmem_slot = slotno;
- rw->rw_worker.bgw_notify_pid = 0; /* might be reinit after crash */
memcpy(&slot->worker, &rw->rw_worker, sizeof(BackgroundWorker));
++slotno;
}
@@ -234,6 +237,14 @@ FindRegisteredWorkerBySlotNumber(int slotno)
return NULL;
}
+ProcNumber
+GetNotifyProcNumberForRegisteredWorker(RegisteredBgWorker *rw)
+{
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[rw->rw_shmem_slot];
+
+ return slot->notify_proc_number;
+}
+
/*
* Notice changes to shared memory made by other backends.
* Accept new worker requests only if allow_new_workers is true.
@@ -315,20 +326,20 @@ BackgroundWorkerStateChange(bool allow_new_workers)
/*
* If the worker is marked for termination, we don't need to add it to
* the registered workers list; we can just free the slot. However, if
- * bgw_notify_pid is set, the process that registered the worker may
- * need to know that we've processed the terminate request, so be sure
- * to signal it.
+ * bgw_notify_proc_number is set, the process that registered the
+ * worker may need to know that we've processed the terminate request,
+ * so be sure to signal it.
*/
if (slot->terminate)
{
- int notify_pid;
+ int notify_proc_number;
/*
* We need a memory barrier here to make sure that the load of
- * bgw_notify_pid and the update of parallel_terminate_count
- * complete before the store to in_use.
+ * bgw_notify_proc_number and the update of
+ * parallel_terminate_count complete before the store to in_use.
*/
- notify_pid = slot->worker.bgw_notify_pid;
+ notify_proc_number = slot->notify_proc_number;
if ((slot->worker.bgw_flags & BGWORKER_CLASS_PARALLEL) != 0)
BackgroundWorkerData->parallel_terminate_count++;
slot->pid = 0;
@@ -336,8 +347,8 @@ BackgroundWorkerStateChange(bool allow_new_workers)
pg_memory_barrier();
slot->in_use = false;
- if (notify_pid != 0)
- kill(notify_pid, SIGUSR1);
+ if (notify_proc_number != INVALID_PROC_NUMBER)
+ SendInterrupt(INTERRUPT_GENERAL, notify_proc_number);
continue;
}
@@ -383,23 +394,6 @@ BackgroundWorkerStateChange(bool allow_new_workers)
rw->rw_worker.bgw_main_arg = slot->worker.bgw_main_arg;
memcpy(rw->rw_worker.bgw_extra, slot->worker.bgw_extra, BGW_EXTRALEN);
- /*
- * Copy the PID to be notified about state changes, but only if the
- * postmaster knows about a backend with that PID. It isn't an error
- * if the postmaster doesn't know about the PID, because the backend
- * that requested the worker could have died (or been killed) just
- * after doing so. Nonetheless, at least until we get some experience
- * with how this plays out in the wild, log a message at a relative
- * high debug level.
- */
- rw->rw_worker.bgw_notify_pid = slot->worker.bgw_notify_pid;
- if (!PostmasterMarkPIDForWorkerNotify(rw->rw_worker.bgw_notify_pid))
- {
- elog(DEBUG1, "worker notification PID %d is not valid",
- (int) rw->rw_worker.bgw_notify_pid);
- rw->rw_worker.bgw_notify_pid = 0;
- }
-
/* Initialize postmaster bookkeeping. */
rw->rw_pid = 0;
rw->rw_crashed_at = 0;
@@ -421,7 +415,7 @@ BackgroundWorkerStateChange(bool allow_new_workers)
* NOTE: The entry is unlinked from BackgroundWorkerList. If the caller is
* iterating through it, better use a mutable iterator!
*
- * Caller is responsible for notifying bgw_notify_pid, if appropriate.
+ * Caller is responsible for notifying bgw_notify_proc_number, if appropriate.
*
* This function must be invoked only in the postmaster.
*/
@@ -466,8 +460,8 @@ ReportBackgroundWorkerPID(RegisteredBgWorker *rw)
slot = &BackgroundWorkerData->slot[rw->rw_shmem_slot];
slot->pid = rw->rw_pid;
- if (rw->rw_worker.bgw_notify_pid != 0)
- kill(rw->rw_worker.bgw_notify_pid, SIGUSR1);
+ if (slot->notify_proc_number != INVALID_PROC_NUMBER)
+ SendInterrupt(INTERRUPT_GENERAL, slot->notify_proc_number);
}
/*
@@ -483,12 +477,12 @@ void
ReportBackgroundWorkerExit(RegisteredBgWorker *rw)
{
BackgroundWorkerSlot *slot;
- int notify_pid;
+ ProcNumber notify_proc_number;
Assert(rw->rw_shmem_slot < max_worker_processes);
slot = &BackgroundWorkerData->slot[rw->rw_shmem_slot];
slot->pid = rw->rw_pid;
- notify_pid = rw->rw_worker.bgw_notify_pid;
+ notify_proc_number = slot->notify_proc_number;
/*
* If this worker is slated for deregistration, do that before notifying
@@ -501,27 +495,34 @@ ReportBackgroundWorkerExit(RegisteredBgWorker *rw)
rw->rw_worker.bgw_restart_time == BGW_NEVER_RESTART)
ForgetBackgroundWorker(rw);
- if (notify_pid != 0)
- kill(notify_pid, SIGUSR1);
+ if (notify_proc_number != INVALID_PROC_NUMBER)
+ SendInterrupt(INTERRUPT_GENERAL, notify_proc_number);
}
/*
- * Cancel SIGUSR1 notifications for a PID belonging to an exiting backend.
+ * Cancel notifications for a PID belonging to an exiting backend.
*
* This function should only be called from the postmaster.
*/
void
-BackgroundWorkerStopNotifications(pid_t pid)
+BackgroundWorkerStopNotifications(int pmchild)
{
dlist_iter iter;
dlist_foreach(iter, &BackgroundWorkerList)
{
RegisteredBgWorker *rw;
+ BackgroundWorkerSlot *slot;
rw = dlist_container(RegisteredBgWorker, rw_lnode, iter.cur);
- if (rw->rw_worker.bgw_notify_pid == pid)
- rw->rw_worker.bgw_notify_pid = 0;
+ Assert(rw->rw_shmem_slot < max_worker_processes);
+ slot = &BackgroundWorkerData->slot[rw->rw_shmem_slot];
+
+ if (slot->notify_pmchild == pmchild)
+ {
+ slot->notify_pmchild = 0;
+ slot->notify_proc_number = INVALID_PROC_NUMBER;
+ }
}
}
@@ -553,14 +554,14 @@ ForgetUnstartedBackgroundWorkers(void)
/* If it's not yet started, and there's someone waiting ... */
if (slot->pid == InvalidPid &&
- rw->rw_worker.bgw_notify_pid != 0)
+ slot->notify_proc_number != INVALID_PROC_NUMBER)
{
/* ... then zap it, and notify the waiter */
- int notify_pid = rw->rw_worker.bgw_notify_pid;
+ int notify_proc_number = slot->notify_proc_number;
ForgetBackgroundWorker(rw);
- if (notify_pid != 0)
- kill(notify_pid, SIGUSR1);
+ if (notify_proc_number != INVALID_PROC_NUMBER)
+ SendInterrupt(INTERRUPT_GENERAL, notify_proc_number);
}
}
}
@@ -613,11 +614,6 @@ ResetBackgroundWorkerCrashTimes(void)
* resetting.
*/
rw->rw_crashed_at = 0;
-
- /*
- * If there was anyone waiting for it, they're history.
- */
- rw->rw_worker.bgw_notify_pid = 0;
}
}
}
@@ -981,15 +977,6 @@ RegisterBackgroundWorker(BackgroundWorker *worker)
if (!SanityCheckBackgroundWorker(worker, LOG))
return;
- if (worker->bgw_notify_pid != 0)
- {
- ereport(LOG,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("background worker \"%s\": only dynamic background workers can request notification",
- worker->bgw_name)));
- return;
- }
-
/*
* Enforce maximum number of workers. Note this is overly restrictive: we
* could allow more non-shmem-connected workers, because these don't count
@@ -1105,6 +1092,25 @@ RegisterDynamicBackgroundWorker(BackgroundWorker *worker,
if (parallel)
BackgroundWorkerData->parallel_register_count++;
+ if ((slot->worker.bgw_flags & BGWORKER_SHMEM_ACCESS) != 0 &&
+ (slot->worker.bgw_flags & BGWORKER_NO_NOTIFY) == 0)
+ {
+ /*
+ * Set notify_proc_number so that postmaster will send us an
+ * interrupt. Also remember the pmchild slot number;
+ * postmaster needs it to detect when we exit, to disarm the
+ * notification.
+ */
+ slot->notify_pmchild = MyPMChildSlot;
+ slot->notify_proc_number = MyProcNumber;
+ }
+ else
+ {
+ /* No notifications. */
+ slot->notify_pmchild = 0;
+ slot->notify_proc_number = INVALID_PROC_NUMBER;
+ }
+
/*
* Make sure postmaster doesn't see the slot as in use before it
* sees the new contents.
@@ -1205,8 +1211,9 @@ GetBackgroundWorkerPid(BackgroundWorkerHandle *handle, pid_t *pidp)
* BGWH_POSTMASTER_DIED, since it that case we know that startup will not
* take place.
*
- * The caller *must* have set our PID as the worker's bgw_notify_pid,
- * else we will not be awoken promptly when the worker's state changes.
+ * This works only if the worker was registered with BGWORKER_SHMEM_ACCESS and
+ * without BGWORKER_NO_NOTIFY, else we will not be awoken promptly when the
+ * worker's state changes.
*/
BgwHandleStatus
WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
@@ -1250,8 +1257,9 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
* up and return BGWH_POSTMASTER_DIED, because it's the postmaster that
* notifies us when a worker's state changes.
*
- * The caller *must* have set our PID as the worker's bgw_notify_pid,
- * else we will not be awoken promptly when the worker's state changes.
+ * This works only if the worker was registered with BGWORKER_SHMEM_ACCESS and
+ * without BGWORKER_NO_NOTIFY, else we will not be awoken promptly when the
+ * worker's state changes.
*/
BgwHandleStatus
WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index 4cde7d52bd3..f325020ade2 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -112,6 +112,9 @@ RaiseInterrupt(uint32 interruptMask)
/*
* Set an interrupt flag in another backend.
+ *
+ * Note: This can also be called from the postmaster, so be careful to not
+ * trust the contents of shared memory.
*/
void
SendInterrupt(uint32 interruptMask, ProcNumber pgprocno)
@@ -152,7 +155,6 @@ InitializeInterruptWaitSet(void)
Assert(interrupt_pos == InterruptWaitSetInterruptPos);
}
-
/*
* Wait for any of the interrupts in interruptMask to be set, or for
* postmaster death, or until timeout is exceeded. 'wakeEvents' is a bitmask
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 82044abdb5d..f1332eb2195 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -4203,15 +4203,15 @@ maybe_start_bgworkers(void)
{
if (rw->rw_worker.bgw_restart_time == BGW_NEVER_RESTART)
{
- int notify_pid;
+ ProcNumber notify_proc_number;
- notify_pid = rw->rw_worker.bgw_notify_pid;
+ notify_proc_number = GetNotifyProcNumberForRegisteredWorker(rw);
ForgetBackgroundWorker(rw);
/* Report worker is gone now. */
- if (notify_pid != 0)
- kill(notify_pid, SIGUSR1);
+ if (notify_proc_number != INVALID_PROC_NUMBER)
+ SendInterrupt(INTERRUPT_GENERAL, notify_proc_number);
continue;
}
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index d915b9a9fce..168112a3220 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -495,7 +495,6 @@ retry:
}
bgw.bgw_restart_time = BGW_NEVER_RESTART;
- bgw.bgw_notify_pid = MyProcPid;
bgw.bgw_main_arg = Int32GetDatum(slot);
if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
@@ -938,7 +937,6 @@ ApplyLauncherRegister(void)
snprintf(bgw.bgw_type, BGW_MAXLEN,
"logical replication launcher");
bgw.bgw_restart_time = 5;
- bgw.bgw_notify_pid = 0;
bgw.bgw_main_arg = (Datum) 0;
RegisterBackgroundWorker(&bgw);
diff --git a/src/backend/storage/ipc/waiteventset.c b/src/backend/storage/ipc/waiteventset.c
index 1eba76a19c4..9380dd58d66 100644
--- a/src/backend/storage/ipc/waiteventset.c
+++ b/src/backend/storage/ipc/waiteventset.c
@@ -2059,6 +2059,11 @@ WakeupMyProc(void)
void
WakeupOtherProc(PGPROC *proc)
{
+ /*
+ * Note: This can also be called from the postmaster, so be careful to not
+ * assume that the contents of shared memory are valid. Reading the 'pid'
+ * (or event handle on Windows) is safe enough.
+ */
#ifndef WIN32
kill(proc->pid, SIGURG);
#else
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..ea37157b2c8 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,14 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * Dynamic workers created with shared memory access usually send an interrupt
+ * to the creating backend when they start and stop, allowing
+ * WaitForBackgroundWorker{Startup,Shutdown}() to work. Such notifications
+ * can be suppressed with this flag.
+ */
+#define BGWORKER_NO_NOTIFY 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -97,7 +105,7 @@ typedef struct BackgroundWorker
char bgw_function_name[BGW_MAXLEN];
Datum bgw_main_arg;
char bgw_extra[BGW_EXTRALEN];
- pid_t bgw_notify_pid; /* SIGUSR1 this backend on start/stop */
+ pid_t bgw_notify_pid; /* not used */
} BackgroundWorker;
typedef enum BgwHandleStatus
diff --git a/src/include/postmaster/bgworker_internals.h b/src/include/postmaster/bgworker_internals.h
index 29e6f44cf08..9b4d4d5166b 100644
--- a/src/include/postmaster/bgworker_internals.h
+++ b/src/include/postmaster/bgworker_internals.h
@@ -15,6 +15,7 @@
#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "postmaster/bgworker.h"
+#include "storage/procnumber.h"
/* GUC options */
@@ -47,9 +48,10 @@ extern void BackgroundWorkerStateChange(bool allow_new_workers);
extern void ForgetBackgroundWorker(RegisteredBgWorker *rw);
extern void ReportBackgroundWorkerPID(RegisteredBgWorker *rw);
extern void ReportBackgroundWorkerExit(RegisteredBgWorker *rw);
-extern void BackgroundWorkerStopNotifications(pid_t pid);
+extern void BackgroundWorkerStopNotifications(int pmchild);
extern void ForgetUnstartedBackgroundWorkers(void);
extern void ResetBackgroundWorkerCrashTimes(void);
+extern ProcNumber GetNotifyProcNumberForRegisteredWorker(RegisteredBgWorker *rw);
/* Entry point for background worker processes */
extern void BackgroundWorkerMain(const void *startup_data, size_t startup_data_len) pg_attribute_noreturn();
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 1d019faa2e5..02aa420360c 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -134,6 +134,7 @@ setup_dynamic_shared_memory(int64 queue_size, int nworkers,
/* Set up the header region. */
hdr = shm_toc_allocate(toc, sizeof(test_shm_mq_header));
+ hdr->leader_proc_number = MyProcNumber;
SpinLockInit(&hdr->mutex);
hdr->workers_total = nworkers;
hdr->workers_attached = 0;
@@ -223,8 +224,6 @@ setup_background_workers(int nworkers, dsm_segment *seg)
sprintf(worker.bgw_function_name, "test_shm_mq_main");
snprintf(worker.bgw_type, BGW_MAXLEN, "test_shm_mq");
worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(seg));
- /* set bgw_notify_pid, so we can detect if the worker stops */
- worker.bgw_notify_pid = MyProcPid;
/* Register the workers. */
for (i = 0; i < nworkers; ++i)
diff --git a/src/test/modules/test_shm_mq/test_shm_mq.h b/src/test/modules/test_shm_mq/test_shm_mq.h
index 9ad9f63b44e..5b1b6cf8357 100644
--- a/src/test/modules/test_shm_mq/test_shm_mq.h
+++ b/src/test/modules/test_shm_mq/test_shm_mq.h
@@ -28,6 +28,8 @@
*/
typedef struct
{
+ ProcNumber leader_proc_number;
+
slock_t mutex;
int workers_total;
int workers_attached;
diff --git a/src/test/modules/test_shm_mq/worker.c b/src/test/modules/test_shm_mq/worker.c
index 751cac60513..3d31c46269e 100644
--- a/src/test/modules/test_shm_mq/worker.c
+++ b/src/test/modules/test_shm_mq/worker.c
@@ -53,7 +53,6 @@ test_shm_mq_main(Datum main_arg)
shm_mq_handle *outqh;
volatile test_shm_mq_header *hdr;
int myworkernumber;
- PGPROC *registrant;
/*
* Establish signal handlers.
@@ -123,13 +122,7 @@ test_shm_mq_main(Datum main_arg)
SpinLockAcquire(&hdr->mutex);
++hdr->workers_ready;
SpinLockRelease(&hdr->mutex);
- registrant = BackendPidGetProc(MyBgworkerEntry->bgw_notify_pid);
- if (registrant == NULL)
- {
- elog(DEBUG1, "registrant backend has exited prematurely");
- proc_exit(1);
- }
- SendInterrupt(INTERRUPT_GENERAL, GetNumberFromPGProc(registrant));
+ SendInterrupt(INTERRUPT_GENERAL, hdr->leader_proc_number);
/* 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 1c23b4eadeb..28a2798037a 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -365,7 +365,6 @@ _PG_init(void)
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
sprintf(worker.bgw_function_name, "worker_spi_main");
- worker.bgw_notify_pid = 0;
/*
* Now fill in worker-specific data, and do the actual registrations.
@@ -414,8 +413,6 @@ worker_spi_launch(PG_FUNCTION_ARGS)
snprintf(worker.bgw_name, BGW_MAXLEN, "worker_spi dynamic worker %d", i);
snprintf(worker.bgw_type, BGW_MAXLEN, "worker_spi dynamic");
worker.bgw_main_arg = Int32GetDatum(i);
- /* set bgw_notify_pid so that we can use WaitForBackgroundWorkerStartup */
- worker.bgw_notify_pid = MyProcPid;
/* extract flags, if any */
ndim = ARR_NDIM(arr);
--
2.39.5
[text/x-patch] v7-0004-Replace-ProcSignals-and-other-ad-hoc-signals-with.patch (240.3K, ../[email protected]/5-v7-0004-Replace-ProcSignals-and-other-ad-hoc-signals-with.patch)
download | inline diff:
From e623492590052f16841ffa171383766eb157074b Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Thu, 6 Mar 2025 18:16:16 +0200
Subject: [PATCH v7 4/4] Replace ProcSignals and other ad hoc signals with
interrupts
This replaces the INTERRUPT_GENERAL, or setting the process's latch
before that, with more fine-grained interrupts. All the ProcSignals
are converted into interrupts, as well as things like timers and
config-reload requests.
Most callers of WaitInterrupt now use INTERRUPT_CFI_MASK(), which is
a bitmask that includes all the interrupts that are handled by
CHECK_FOR_INTERRUPTS() in the current state. CHECK_FOR_INTERRUPTS()
now clears the interrupts bits that it processes, the caller is no
longer required to do ClearInterrupt (or ResetLatch) for those
bits. If you wake up for other interrupts that are not handled by
CHECK_FOR_INTERRUPTS(), you still need to clear those.
Now that there are separate bits for different things, it's possible
to do e.g. WaitInterrupt(INTERRUPT_CONFIG_RELOAD |
INTERRUPT_QUERY_CANCEL) to wait just for a config reload request or
query cancellation, and not wake up on other events like a shutdown
request. That's not very useful in practice, as CHECK_FOR_INTERRUPTS()
still processes those interrupts if you call it, and you would want to
process all the standard interrupts promptly anyway. It might be
useful to check for specific interrupts with InterruptPending(),
without immediately processing them, however. spgdoinsert() does
something like that. In this commit I didn't change its overall logic,
but it probably could be made more fine-grained now.
More importantly, you can now have additional interrupts that are
processed less frequently and do not wake up the backend when it's not
prepared to process them. For example, the SINVAL_CATCHUP interrupt is
only processed when the backend is idle. Previously, a catchup request
would wake up the process whether it was ready to handle it or not;
now it only wakes up when the backend is idle. That's not too
significant for catchup interrupts because they don't occur that often
anyway, but it's still nice and could be more significant with other
interrupts.
There's still a general-purpose INTERRUPT_GENERAL interrupt that is
multiplexed for many different purposes in different processes, for
example to wake up the walwriter when it has work to do, and to wake
up processes waiting on a condition variable. The common property of
those uses is that there's some other flag or condition that is
checked on each wakeup, the wakeup interrupt itself means merely that
something interesting might've happened.
TODO:
* ShutdownRequestPending became INTERRUPT_SHUTDOWN_AUX and
ProcDiePending became INTERRUPT_DIE, but do we really need two
different interrupts for requesting a process to terminate?
* There are only a few interrupts bits left. Consolidate the existing
ones, switch to a 64-bit atomic, or mulitplex some of them?
XXX: original text from Thomas's patch:
"ProcSignals" were a two-step way for one backend to ask another backend
to do something, by multiplexing SIGUSR1. Historically, real work was
done in the SIGUSR1 handler, but commit 0da096d7 removed the last case
of that. Now the handler just set "interrupt pending" flags, called
SetLatch(MyLatch) to close races, and then did all the interesting work
at the next CHECK_FOR_INTERRUPTS() call.
This commit removes the ProcSignal step, replacing ProcSignals with
Interrupts.
Benefits of this refactoring include removing unnecessary concepts and
terminology, avoiding the risk of people putting buggy code back into
signal handlers, removing a whole lot of global variables, removing
dependencies on PIDs and the process model, removing risks of sending
a signal that has default action of termination to the wrong process,
and taking a step towards being able remove the fake signal system
from the Windows port.
Notable changes:
* all calls to SendProcSignal(pid, PROCSIG_XXX) become
SendInterrupt(INTERRUPT_XXX, procno)
* all XxxPending global variables are gone; they are represented
as bits in MyProc->pending_interrupts
* the SIGUSR1 handler is gone
* places that know the PIDs of other backends must now use procnos
instead
The only code left in src/backend/storage/ipc/procsignal.c relates to
ProcSignalBarriers. XXX Those should perhaps be renamed.
SIGUSR1 can now be set to SIG_IGN in most backends. Some special cases
remain, that were not using the general sigusr1_handler (now removed).
Author: Thomas Munro <[email protected]>
Author: Heikki Linnakangas <[email protected]>
Discussion: https://postgr.es/m/CA%2BhUKG%2B3MkS21yK4jL4cgZywdnnGKiBg0jatoV6kzaniBmcqbQ%40mail.gmail.com
---
contrib/pg_prewarm/autoprewarm.c | 25 +-
contrib/postgres_fdw/connection.c | 3 +-
doc/src/sgml/sources.sgml | 3 +-
src/backend/access/heap/vacuumlazy.c | 3 +-
src/backend/access/spgist/spgdoinsert.c | 9 +-
src/backend/access/transam/README.parallel | 2 +-
src/backend/access/transam/parallel.c | 50 +--
src/backend/access/transam/xlog.c | 3 +-
src/backend/access/transam/xlogfuncs.c | 6 +-
src/backend/access/transam/xlogrecovery.c | 31 +-
src/backend/backup/basebackup_throttle.c | 12 +-
src/backend/commands/async.c | 103 +----
src/backend/commands/dbcommands.c | 1 +
src/backend/commands/tablespace.c | 1 +
src/backend/commands/vacuum.c | 7 +-
src/backend/executor/execAsync.c | 2 +-
src/backend/executor/nodeGather.c | 2 +-
src/backend/libpq/be-secure.c | 18 +-
src/backend/libpq/pqcomm.c | 22 +-
src/backend/libpq/pqmq.c | 22 +-
src/backend/postmaster/autovacuum.c | 43 +-
src/backend/postmaster/bgworker.c | 7 +-
src/backend/postmaster/bgwriter.c | 15 +-
src/backend/postmaster/checkpointer.c | 70 ++--
src/backend/postmaster/interrupt.c | 33 +-
src/backend/postmaster/pgarch.c | 20 +-
src/backend/postmaster/postmaster.c | 3 +-
src/backend/postmaster/startup.c | 16 +-
src/backend/postmaster/syslogger.c | 8 +-
src/backend/postmaster/walsummarizer.c | 26 +-
src/backend/postmaster/walwriter.c | 8 +-
.../libpqwalreceiver/libpqwalreceiver.c | 12 +-
.../replication/logical/applyparallelworker.c | 57 +--
src/backend/replication/logical/launcher.c | 42 +-
src/backend/replication/logical/slotsync.c | 57 +--
src/backend/replication/logical/tablesync.c | 12 +-
src/backend/replication/logical/worker.c | 19 +-
src/backend/replication/slot.c | 89 ++--
src/backend/replication/slotfuncs.c | 18 +-
src/backend/replication/syncrep.c | 13 +-
src/backend/replication/walreceiver.c | 19 +-
src/backend/replication/walsender.c | 78 ++--
src/backend/storage/buffer/bufmgr.c | 18 +-
src/backend/storage/ipc/ipc.c | 6 +-
src/backend/storage/ipc/procarray.c | 20 +-
src/backend/storage/ipc/procsignal.c | 242 ++---------
src/backend/storage/ipc/shm_mq.c | 64 +--
src/backend/storage/ipc/signalfuncs.c | 2 +-
src/backend/storage/ipc/sinval.c | 52 +--
src/backend/storage/ipc/sinvaladt.c | 14 +-
src/backend/storage/ipc/standby.c | 81 ++--
src/backend/storage/ipc/waiteventset.c | 4 +-
src/backend/storage/lmgr/condition_variable.c | 3 +-
src/backend/storage/lmgr/predicate.c | 5 +-
src/backend/storage/lmgr/proc.c | 15 +-
src/backend/tcop/postgres.c | 349 +++++++---------
src/backend/utils/activity/pgstat_database.c | 20 +-
src/backend/utils/adt/mcxtfuncs.c | 16 +-
src/backend/utils/adt/misc.c | 3 +-
src/backend/utils/init/globals.c | 15 -
src/backend/utils/init/postinit.c | 20 +-
src/backend/utils/misc/timeout.c | 6 -
src/backend/utils/mmgr/mcxt.c | 20 +-
src/include/access/parallel.h | 4 -
src/include/commands/async.h | 6 -
src/include/libpq/libpq-be-fe-helpers.h | 14 +-
src/include/libpq/pqmq.h | 2 +-
src/include/miscadmin.h | 122 +-----
src/include/pgstat.h | 2 +-
src/include/postmaster/interrupt.h | 386 +++++++++++++++---
src/include/postmaster/startup.h | 8 +
src/include/replication/logicalworker.h | 5 -
src/include/replication/slot.h | 6 +-
src/include/replication/walsender.h | 2 +-
src/include/replication/walsender_private.h | 1 +
src/include/replication/worker_internal.h | 5 +-
src/include/storage/procarray.h | 7 +-
src/include/storage/procsignal.h | 41 --
src/include/storage/sinval.h | 5 -
src/include/storage/standby.h | 4 +-
src/include/storage/waiteventset.h | 5 +-
src/include/tcop/tcopprot.h | 6 +-
src/include/utils/memutils.h | 1 -
src/test/modules/test_shm_mq/setup.c | 9 +-
src/test/modules/test_shm_mq/test.c | 6 +-
src/test/modules/worker_spi/worker_spi.c | 9 +-
86 files changed, 1172 insertions(+), 1449 deletions(-)
diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index a52058be9bb..af00cfd9a05 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -39,7 +39,6 @@
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/lwlock.h"
-#include "storage/procsignal.h"
#include "storage/smgr.h"
#include "tcop/tcopprot.h"
#include "utils/guc.h"
@@ -149,7 +148,7 @@ autoprewarm_main(Datum main_arg)
/* Establish signal handlers; once that's done, unblock signals. */
pqsignal(SIGTERM, SignalHandlerForShutdownRequest);
pqsignal(SIGHUP, SignalHandlerForConfigReload);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
BackgroundWorkerUnblockSignals();
/* Create (if necessary) and attach to our shared memory area. */
@@ -198,24 +197,22 @@ autoprewarm_main(Datum main_arg)
if (first_time)
{
apw_load_buffers();
- final_dump_allowed = !ShutdownRequestPending;
+ final_dump_allowed = !IsInterruptPending(INTERRUPT_SHUTDOWN_AUX);
last_dump_time = GetCurrentTimestamp();
}
/* Periodically dump buffers until terminated. */
- while (!ShutdownRequestPending)
+ while (!IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
{
/* In case of a SIGHUP, just reload the configuration. */
- if (ConfigReloadPending)
- {
- ConfigReloadPending = false;
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
ProcessConfigFile(PGC_SIGHUP);
- }
if (autoprewarm_interval <= 0)
{
/* We're only dumping at shutdown, so just wait forever. */
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_SHUTDOWN_AUX |
+ INTERRUPT_CONFIG_RELOAD,
WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
-1L,
PG_WAIT_EXTENSION);
@@ -242,14 +239,12 @@ autoprewarm_main(Datum main_arg)
}
/* Sleep until the next dump time. */
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_SHUTDOWN_AUX |
+ INTERRUPT_CONFIG_RELOAD,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
delay_in_ms,
PG_WAIT_EXTENSION);
}
-
- /* Reset the latch, loop. */
- ClearInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -394,7 +389,7 @@ apw_load_buffers(void)
* Likewise, don't launch if we've already been told to shut down.
* (The launch would fail anyway, but we might as well skip it.)
*/
- if (ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
break;
/*
@@ -415,7 +410,7 @@ apw_load_buffers(void)
LWLockRelease(&apw_state->lock);
/* Report our success, if we were able to finish. */
- if (!ShutdownRequestPending)
+ if (!IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
ereport(LOG,
(errmsg("autoprewarm successfully prewarmed %d of %d previously-loaded blocks",
apw_state->prewarmed_blocks, num_elements)));
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 0c9fdeb92fd..88f08ac2465 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -1654,12 +1654,11 @@ pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime,
pgfdw_we_cleanup_result = WaitEventExtensionNew("PostgresFdwCleanupResult");
/* Sleep until there's something to do */
- wc = WaitInterruptOrSocket(INTERRUPT_GENERAL,
+ wc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK(),
WL_INTERRUPT | WL_SOCKET_READABLE |
WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
PQsocket(conn),
cur_timeout, pgfdw_we_cleanup_result);
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/sources.sgml b/doc/src/sgml/sources.sgml
index fc642f5f713..018d6970c24 100644
--- a/doc/src/sgml/sources.sgml
+++ b/doc/src/sgml/sources.sgml
@@ -1000,8 +1000,7 @@ MemoryContextSwitchTo(MemoryContext context)
static void
handle_sighup(SIGNAL_ARGS)
{
- got_SIGHUP = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_CONFIG_RELOAD);
}
</programlisting>
</para>
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 45f25270dd8..5890dc31820 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -3243,11 +3243,10 @@ lazy_truncate_heap(LVRelState *vacrel)
return;
}
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK(),
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL,
WAIT_EVENT_VACUUM_TRUNCATE);
- ClearInterrupt(INTERRUPT_GENERAL);
}
/*
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index af6b27b2135..fccd2fffdc8 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -2040,8 +2040,11 @@ spgdoinsert(Relation index, SpGistState *state,
* pending, break out of the loop and deal with the situation below.
* Set result = false because we must restart the insertion if the
* interrupt isn't a query-cancel-or-die case.
+ *
+ * XXX: INTERRUPT_CFI_ALL_MASK covers more than just query cancel and die.
+ * Could we be more precise here?
*/
- if (INTERRUPTS_PENDING_CONDITION())
+ if (IsInterruptPending(INTERRUPT_CFI_ALL_MASK))
{
result = false;
break;
@@ -2167,7 +2170,7 @@ spgdoinsert(Relation index, SpGistState *state,
* repeatedly, check for query cancel (see comments above).
*/
process_inner_tuple:
- if (INTERRUPTS_PENDING_CONDITION())
+ if (IsInterruptPending(INTERRUPT_CFI_ALL_MASK))
{
result = false;
break;
@@ -2343,7 +2346,7 @@ spgdoinsert(Relation index, SpGistState *state,
* were the case, telling the caller to retry would create an infinite
* loop.
*/
- Assert(INTERRUPTS_CAN_BE_PROCESSED());
+ Assert(INTERRUPTS_CAN_BE_PROCESSED(INTERRUPT_CFI_ALL_MASK));
/*
* Finally, check for interrupts again. If there was a query cancel,
diff --git a/src/backend/access/transam/README.parallel b/src/backend/access/transam/README.parallel
index 9df3da91b0c..a3593dd4f54 100644
--- a/src/backend/access/transam/README.parallel
+++ b/src/backend/access/transam/README.parallel
@@ -37,7 +37,7 @@ with fewer parallel workers than it originally requested, so catering to
this case imposes no additional burden.
Whenever a new message (or partial message; very large messages may wrap) is
-sent to the error-reporting queue, PROCSIG_PARALLEL_MESSAGE is sent to the
+sent to the error-reporting queue, INTERRUPT_PARALLEL_MESSAGE is sent to the
initiating backend. This causes the next CHECK_FOR_INTERRUPTS() in the
initiating backend to read and rethrow the message. For the most part, this
makes error reporting in parallel mode "just work". Of course, to work
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index c4a1f69c619..25322a99522 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -115,9 +115,6 @@ typedef struct FixedParallelState
*/
int ParallelWorkerNumber = -1;
-/* Is there a parallel message pending which we need to receive? */
-volatile sig_atomic_t ParallelMessagePending = false;
-
/* Are we initializing a parallel worker? */
bool InitializingParallelWorker = false;
@@ -127,9 +124,6 @@ static FixedParallelState *MyFixedParallelState;
/* List of active parallel contexts. */
static dlist_head pcxt_list = DLIST_STATIC_INIT(pcxt_list);
-/* Backend-local copy of data from FixedParallelState. */
-static pid_t ParallelLeaderPid;
-
/*
* List of internal parallel worker entry points. We need this for
* reasons explained in LookupParallelWorkerFunction(), below.
@@ -242,8 +236,10 @@ InitializeParallelDSM(ParallelContext *pcxt)
* launch any workers: we would fail to process interrupts sent by them.
* We can deal with that edge case by pretending no workers were
* requested.
+ *
+ * XXX: could this be relaxed now that we have different interrupts bits?
*/
- if (!INTERRUPTS_CAN_BE_PROCESSED())
+ if (!INTERRUPTS_CAN_BE_PROCESSED(INTERRUPT_CFI_ALL_MASK))
pcxt->nworkers = 0;
/*
@@ -759,16 +755,20 @@ WaitForParallelWorkersToAttach(ParallelContext *pcxt)
{
/*
* Worker not yet started, so we must wait. The postmaster
- * 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.
+ * will notify us with INTERRUPT_GENERAL 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 = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
-1, WAIT_EVENT_BGWORKER_STARTUP);
if (rc & WL_INTERRUPT)
+ {
+ CHECK_FOR_INTERRUPTS();
ClearInterrupt(INTERRUPT_GENERAL);
+ }
}
}
@@ -883,7 +883,7 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
}
}
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, -1,
WAIT_EVENT_PARALLEL_FINISH);
ClearInterrupt(INTERRUPT_GENERAL);
@@ -1027,21 +1027,6 @@ ParallelContextActive(void)
return !dlist_is_empty(&pcxt_list);
}
-/*
- * Handle receipt of an interrupt indicating a parallel worker message.
- *
- * Note: this is called within a signal handler! All we can do is set
- * a flag that will cause the next CHECK_FOR_INTERRUPTS() to invoke
- * ProcessParallelMessages().
- */
-void
-HandleParallelMessageInterrupt(void)
-{
- InterruptPending = true;
- ParallelMessagePending = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
-}
-
/*
* Process any queued protocol messages received from parallel workers.
*/
@@ -1077,7 +1062,7 @@ ProcessParallelMessages(void)
oldcontext = MemoryContextSwitchTo(hpm_context);
/* OK to process messages. Reset the flag saying there are more to do. */
- ParallelMessagePending = false;
+ ClearInterrupt(INTERRUPT_PARALLEL_MESSAGE);
dlist_foreach(iter, &pcxt_list)
{
@@ -1358,7 +1343,6 @@ ParallelWorkerMain(Datum main_arg)
MyFixedParallelState = fps;
/* Arrange to signal the leader if we exit. */
- ParallelLeaderPid = fps->parallel_leader_pid;
ParallelLeaderProcNumber = fps->parallel_leader_proc_number;
before_shmem_exit(ParallelWorkerShutdown, PointerGetDatum(seg));
@@ -1374,8 +1358,7 @@ ParallelWorkerMain(Datum main_arg)
shm_mq_set_sender(mq, MyProc);
mqh = shm_mq_attach(mq, seg, NULL);
pq_redirect_to_shm_mq(seg, mqh);
- pq_set_parallel_leader(fps->parallel_leader_pid,
- fps->parallel_leader_proc_number);
+ pq_set_parallel_leader(fps->parallel_leader_proc_number);
/*
* Hooray! Primary initialization is complete. Now, we need to set up our
@@ -1614,9 +1597,8 @@ ParallelWorkerReportLastRecEnd(XLogRecPtr last_xlog_end)
static void
ParallelWorkerShutdown(int code, Datum arg)
{
- SendProcSignal(ParallelLeaderPid,
- PROCSIG_PARALLEL_MESSAGE,
- ParallelLeaderProcNumber);
+ SendInterrupt(INTERRUPT_PARALLEL_MESSAGE,
+ ParallelLeaderProcNumber);
dsm_detach((dsm_segment *) DatumGetPointer(arg));
}
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 940554def1a..df0dc61772f 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -9382,11 +9382,10 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive)
reported_waiting = true;
}
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK(),
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
1000L,
WAIT_EVENT_BACKUP_WAIT_WAL_ARCHIVE);
- ClearInterrupt(INTERRUPT_GENERAL);
if (++waits >= seconds_before_warning)
{
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 8f43ce62909..91700ca4b41 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -708,6 +708,8 @@ pg_promote(PG_FUNCTION_ARGS)
errmsg("failed to send signal to postmaster: %m")));
}
+ /* XXX: we could interrupt the startup process directly */
+
/* return immediately if waiting was not requested */
if (!wait)
PG_RETURN_BOOL(true);
@@ -718,14 +720,12 @@ pg_promote(PG_FUNCTION_ARGS)
{
int rc;
- ClearInterrupt(INTERRUPT_GENERAL);
-
if (!RecoveryInProgress())
PG_RETURN_BOOL(true);
CHECK_FOR_INTERRUPTS();
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK(),
WL_INTERRUPT | WL_TIMEOUT | WL_POSTMASTER_DEATH,
1000L / WAITS_PER_SECOND,
WAIT_EVENT_PROMOTE);
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index c244f6c5ec0..4837739a868 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -3004,18 +3004,12 @@ recoveryApplyDelay(XLogReaderState *record)
while (true)
{
- /*
- * INTERRUPT_GENERAL 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);
-
/* This might change recovery_min_apply_delay. */
ProcessStartupProcInterrupts();
+ /*
+ * INTERRUPT_RECOVERY_CONTINUE indicates that more WAL has arrived.
+ */
ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE);
if (CheckForStandbyTrigger())
break;
@@ -3037,8 +3031,8 @@ recoveryApplyDelay(XLogReaderState *record)
elog(DEBUG2, "recovery apply delay %ld milliseconds", msecs);
- (void) WaitInterrupt(INTERRUPT_RECOVERY_CONTINUE |
- INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_STARTUP_PROC_MASK |
+ INTERRUPT_RECOVERY_CONTINUE,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
msecs,
WAIT_EVENT_RECOVERY_APPLY_DELAY);
@@ -3696,18 +3690,18 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
/* Do background tasks that might benefit us later. */
KnownAssignedTransactionIdsIdleMaintenance();
- (void) WaitInterrupt(INTERRUPT_RECOVERY_CONTINUE |
- INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_STARTUP_PROC_MASK |
+ INTERRUPT_RECOVERY_CONTINUE,
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);
+ /* Handle standard interrupts of startup process */
ProcessStartupProcInterrupts();
+
+ ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE);
}
last_fail_time = now;
currentSource = XLOG_FROM_ARCHIVE;
@@ -3973,8 +3967,8 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
* Wait for more WAL to arrive, when we will be woken
* immediately by the WAL receiver.
*/
- (void) WaitInterrupt(INTERRUPT_RECOVERY_CONTINUE |
- INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_STARTUP_PROC_MASK |
+ INTERRUPT_RECOVERY_CONTINUE,
WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
-1L,
WAIT_EVENT_RECOVERY_WAL_STREAM);
@@ -3998,7 +3992,6 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
* This possibly-long loop needs to handle interrupts of startup
* process.
*/
- ClearInterrupt(INTERRUPT_GENERAL);
ProcessStartupProcInterrupts();
}
diff --git a/src/backend/backup/basebackup_throttle.c b/src/backend/backup/basebackup_throttle.c
index 861043c6b4a..02ce65da3b6 100644
--- a/src/backend/backup/basebackup_throttle.c
+++ b/src/backend/backup/basebackup_throttle.c
@@ -155,6 +155,8 @@ throttle(bbsink_throttle *sink, size_t increment)
sleep;
int wait_result;
+ CHECK_FOR_INTERRUPTS();
+
/* Time elapsed since the last measurement (and possible wake up). */
elapsed = GetCurrentTimestamp() - sink->throttled_last;
@@ -163,23 +165,15 @@ throttle(bbsink_throttle *sink, size_t increment)
if (sleep <= 0)
break;
- ClearInterrupt(INTERRUPT_GENERAL);
-
- /* 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 = WaitInterrupt(INTERRUPT_GENERAL,
+ wait_result = WaitInterrupt(INTERRUPT_CFI_MASK(),
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
(long) (sleep / 1000),
WAIT_EVENT_BASE_BACKUP_THROTTLE);
- if (wait_result & WL_INTERRUPT)
- CHECK_FOR_INTERRUPTS();
-
/* Done waiting? */
if (wait_result & WL_TIMEOUT)
break;
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 2b5b92abe38..df4feee41a0 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -39,7 +39,7 @@
* is no need for WAL support or fsync'ing.
*
* 3. Every backend that is listening on at least one channel registers by
- * entering its PID into the array in AsyncQueueControl. It then scans all
+ * entering itself into the array in AsyncQueueControl. It then scans all
* incoming notifications in the central queue and first compares the
* database OID of the notification with its own database OID and then
* compares the notified channel with the list of channels that it listens
@@ -72,7 +72,7 @@
* Then we signal any backends that may be interested in our messages
* (including our own backend, if listening). This is done by
* SignalBackends(), which scans the list of listening backends and sends a
- * PROCSIG_NOTIFY_INTERRUPT signal to every listening backend (we don't
+ * INTERRUPT_ASYNC_NOTIFY interrupt to every listening backend (we don't
* know which backend is listening on which channel so we must signal them
* all). We can exclude backends that are already up to date, though, and
* we can also exclude backends that are in other databases (unless they
@@ -90,13 +90,11 @@
* frontend until the command is done; but notifies to other backends
* should go out immediately after each commit.
*
- * 5. Upon receipt of a PROCSIG_NOTIFY_INTERRUPT signal, the signal handler
- * raises INTERRUPT_GENERAL, which triggers the event to be processed
- * immediately if this backend is idle (i.e., it is waiting for a frontend
+ * 5. The INTERRUPT_ASYNC_NOTIFY interrupt is processed immediately in the
+ * listening backend if it 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
- * flag, which will cause the processing to occur just before we next go
- * idle.
+ * ProcessClientReadInterrupt()). Otherwise the interrupt is processed
+ * later, just before we next go idle.
*
* Inbound-notify processing consists of reading all of the notifications
* that have arrived since scanning last time. We read every notification
@@ -127,7 +125,6 @@
#include <limits.h>
#include <unistd.h>
-#include <signal.h>
#include "access/parallel.h"
#include "access/slru.h"
@@ -143,7 +140,6 @@
#include "postmaster/interrupt.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
-#include "storage/procsignal.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/guc_hooks.h"
@@ -271,7 +267,7 @@ typedef struct QueueBackendStatus
* NotifyQueueTailLock, then NotifyQueueLock, and lastly SLRU bank lock.
*
* Each backend uses the backend[] array entry with index equal to its
- * ProcNumber. We rely on this to make SendProcSignal fast.
+ * ProcNumber. We rely on this for sending interrupts.
*
* The backend[] array entries for actively-listening backends are threaded
* together using firstListener and the nextListener links, so that we can
@@ -404,15 +400,6 @@ struct NotificationHash
static NotificationList *pendingNotifies = NULL;
-/*
- * Inbound notifications are initially processed by HandleNotifyInterrupt(),
- * called from inside a signal handler. That just sets the
- * notifyInterruptPending flag and raises the INTERRUPT_GENERAL interrupt.
- * ProcessNotifyInterrupt() will then be called whenever it's safe to actually
- * deal with the interrupt.
- */
-volatile sig_atomic_t notifyInterruptPending = false;
-
/* True if we've registered an on_shmem_exit cleanup */
static bool unlistenExitRegistered = false;
@@ -1581,29 +1568,25 @@ asyncQueueFillWarning(void)
static void
SignalBackends(void)
{
- int32 *pids;
ProcNumber *procnos;
int count;
/*
* Identify backends that we need to signal. We don't want to send
* signals while holding the NotifyQueueLock, so this loop just builds a
- * list of target PIDs.
+ * list of target backends.
*
* XXX in principle these pallocs could fail, which would be bad. Maybe
* preallocate the arrays? They're not that large, though.
*/
- pids = (int32 *) palloc(MaxBackends * sizeof(int32));
procnos = (ProcNumber *) palloc(MaxBackends * sizeof(ProcNumber));
count = 0;
LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
for (ProcNumber i = QUEUE_FIRST_LISTENER; i != INVALID_PROC_NUMBER; i = QUEUE_NEXT_LISTENER(i))
{
- int32 pid = QUEUE_BACKEND_PID(i);
QueuePosition pos;
- Assert(pid != InvalidPid);
pos = QUEUE_BACKEND_POS(i);
if (QUEUE_BACKEND_DBOID(i) == MyDatabaseId)
{
@@ -1625,38 +1608,27 @@ SignalBackends(void)
continue;
}
/* OK, need to signal this one */
- pids[count] = pid;
procnos[count] = i;
count++;
}
LWLockRelease(NotifyQueueLock);
- /* Now send signals */
+ /* Now send the interrupts */
for (int i = 0; i < count; i++)
{
- int32 pid = pids[i];
+ ProcNumber procno = procnos[i];
/*
- * If we are signaling our own process, no need to involve the kernel;
- * just set the flag directly.
+ * Note: it's unlikely but certainly possible that the backend exited
+ * since we released NotifyQueueLock. We'll send the interrupt to
+ * wrong backend in that case, but that's harmless.
*/
- if (pid == MyProcPid)
- {
- notifyInterruptPending = true;
- continue;
- }
-
- /*
- * Note: assuming things aren't broken, a signal failure here could
- * only occur if the target backend exited since we released
- * NotifyQueueLock; which is unlikely but certainly possible. So we
- * just log a low-level debug message if it happens.
- */
- if (SendProcSignal(pid, PROCSIG_NOTIFY_INTERRUPT, procnos[i]) < 0)
- elog(DEBUG3, "could not signal backend with PID %d: %m", pid);
+ if (procno == MyProcNumber)
+ RaiseInterrupt(INTERRUPT_ASYNC_NOTIFY);
+ else
+ SendInterrupt(INTERRUPT_ASYNC_NOTIFY, procno);
}
- pfree(pids);
pfree(procnos);
}
@@ -1793,39 +1765,12 @@ AtSubAbort_Notify(void)
}
}
-/*
- * HandleNotifyInterrupt
- *
- * Signal handler portion of interrupt handling. Let the backend know
- * that there's a pending notify interrupt. If we're currently reading
- * from the client, this will interrupt the read and
- * ProcessClientReadInterrupt() will call ProcessNotifyInterrupt().
- */
-void
-HandleNotifyInterrupt(void)
-{
- /*
- * Note: this is called by a SIGNAL HANDLER. You must be very wary what
- * you do here.
- */
-
- /* signal that work needs to be done */
- notifyInterruptPending = true;
-
- /* make sure the event is processed in due course */
- RaiseInterrupt(INTERRUPT_GENERAL);
-}
-
/*
* ProcessNotifyInterrupt
*
- * This is called if we see notifyInterruptPending set, just before
- * transmitting ReadyForQuery at the end of a frontend command, and
- * also if a notify signal occurs while reading from the frontend.
- * HandleNotifyInterrupt() will cause the read to be interrupted with
- * INTERRUPT_GENERAL, and this routine will get called. If we are truly
- * idle (ie, *not* inside a transaction block), process the incoming
- * notifies.
+ * This is called if we see INTERRUPT_ASYNC_NOTIFY set, just before
+ * transmitting ReadyForQuery at the end of a frontend command, and also
+ * if a notify signal occurs while reading from the frontend.
*
* If "flush" is true, force any frontend messages out immediately.
* This can be false when being called at the end of a frontend command,
@@ -1834,11 +1779,10 @@ HandleNotifyInterrupt(void)
void
ProcessNotifyInterrupt(bool flush)
{
- if (IsTransactionOrTransactionBlock())
- return; /* not really idle */
+ Assert(!IsTransactionOrTransactionBlock());
/* Loop in case another signal arrives while sending messages */
- while (notifyInterruptPending)
+ while (ConsumeInterrupt(INTERRUPT_ASYNC_NOTIFY))
ProcessIncomingNotify(flush);
}
@@ -2183,9 +2127,6 @@ asyncQueueAdvanceTail(void)
static void
ProcessIncomingNotify(bool flush)
{
- /* We *must* reset the flag */
- notifyInterruptPending = false;
-
/* Do nothing else if we aren't actively listening */
if (listenChannels == NIL)
return;
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 1753b289074..d1557542b76 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -60,6 +60,7 @@
#include "storage/lmgr.h"
#include "storage/md.h"
#include "storage/procarray.h"
+#include "storage/procsignal.h"
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index a9005cc7212..80b6c721bac 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -70,6 +70,7 @@
#include "miscadmin.h"
#include "postmaster/bgwriter.h"
#include "storage/fd.h"
+#include "storage/procsignal.h"
#include "storage/standby.h"
#include "utils/acl.h"
#include "utils/builtins.h"
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 65b897476ce..6efcd247fe5 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2397,8 +2397,7 @@ vacuum_delay_point(bool is_analyze)
/* Always check for interrupts */
CHECK_FOR_INTERRUPTS();
- if (InterruptPending ||
- (!VacuumCostActive && !ConfigReloadPending))
+ if (!VacuumCostActive && !IsInterruptPending(INTERRUPT_CONFIG_RELOAD))
return;
/*
@@ -2407,9 +2406,9 @@ vacuum_delay_point(bool is_analyze)
* [autovacuum_]vacuum_cost_delay to take effect while a table is being
* vacuumed or analyzed.
*/
- if (ConfigReloadPending && AmAutoVacuumWorkerProcess())
+ if (IsInterruptPending(INTERRUPT_CONFIG_RELOAD) && AmAutoVacuumWorkerProcess())
{
- ConfigReloadPending = false;
+ ClearInterrupt(INTERRUPT_CONFIG_RELOAD);
ProcessConfigFile(PGC_SIGHUP);
VacuumUpdateCosts();
}
diff --git a/src/backend/executor/execAsync.c b/src/backend/executor/execAsync.c
index 5d3cabe73e3..a9be6285cbc 100644
--- a/src/backend/executor/execAsync.c
+++ b/src/backend/executor/execAsync.c
@@ -56,7 +56,7 @@ ExecAsyncRequest(AsyncRequest *areq)
* for which it wishes to wait. We expect the node-type specific callback to
* make a single call of the following form:
*
- * AddWaitEventToSet(set, WL_SOCKET_READABLE, fd, NULL, areq);
+ * AddWaitEventToSet(set, WL_SOCKET_READABLE, fd, 0, areq);
*/
void
ExecAsyncConfigureWait(AsyncRequest *areq)
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 11e5b3f083b..9702260bf46 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -383,7 +383,7 @@ gather_readnext(GatherState *gatherstate)
return NULL;
/* Nothing to do except wait for developments. */
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
WAIT_EVENT_EXECUTE_GATHER);
ClearInterrupt(INTERRUPT_GENERAL);
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 765ca0f3166..205729f408d 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -179,10 +179,11 @@ ssize_t
secure_read(Port *port, void *ptr, size_t len)
{
ssize_t n;
+ uint32 interruptMask;
int waitfor;
/* Deal with any already-pending interrupt condition. */
- ProcessClientReadInterrupt(false);
+ interruptMask = ProcessClientReadInterrupt(false);
retry:
#ifdef USE_SSL
@@ -214,6 +215,7 @@ retry:
Assert(waitfor);
ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, waitfor, 0);
+ ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetInterruptPos, WL_INTERRUPT, interruptMask);
WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
WAIT_EVENT_CLIENT_READ);
@@ -243,8 +245,7 @@ retry:
/* Handle interrupt. */
if (event.events & WL_INTERRUPT)
{
- ClearInterrupt(INTERRUPT_GENERAL);
- ProcessClientReadInterrupt(true);
+ interruptMask = ProcessClientReadInterrupt(true);
/*
* We'll retry the read. Most likely it will return immediately
@@ -259,7 +260,7 @@ retry:
* Process interrupts that happened during a successful (or non-blocking,
* or hard-failed) read.
*/
- ProcessClientReadInterrupt(false);
+ (void) ProcessClientReadInterrupt(false);
return n;
}
@@ -306,10 +307,11 @@ ssize_t
secure_write(Port *port, const void *ptr, size_t len)
{
ssize_t n;
+ uint32 interruptMask;
int waitfor;
/* Deal with any already-pending interrupt condition. */
- ProcessClientWriteInterrupt(false);
+ interruptMask = ProcessClientWriteInterrupt(false);
retry:
waitfor = 0;
@@ -340,6 +342,7 @@ retry:
Assert(waitfor);
ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, waitfor, 0);
+ ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetInterruptPos, WL_INTERRUPT, interruptMask);
WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
WAIT_EVENT_CLIENT_WRITE);
@@ -353,8 +356,7 @@ retry:
/* Handle interrupt. */
if (event.events & WL_INTERRUPT)
{
- ClearInterrupt(INTERRUPT_GENERAL);
- ProcessClientWriteInterrupt(true);
+ interruptMask = ProcessClientWriteInterrupt(true);
/*
* We'll retry the write. Most likely it will return immediately
@@ -369,7 +371,7 @@ retry:
* Process interrupts that happened during a successful (or non-blocking,
* or hard-failed) write.
*/
- ProcessClientWriteInterrupt(false);
+ (void) ProcessClientWriteInterrupt(false);
return n;
}
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index f8465cb2d7b..77fd228089b 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -305,11 +305,15 @@ pq_init(ClientSocket *client_sock)
elog(FATAL, "fcntl(F_SETFD) failed on socket: %m");
#endif
+ /*
+ * Add initial events to the set. These are modified with the correct
+ * event and interrupt masks on each use.
+ */
FeBeWaitSet = CreateWaitEventSet(NULL, FeBeWaitSetNEvents);
socket_pos = AddWaitEventToSet(FeBeWaitSet, WL_SOCKET_WRITEABLE,
port->sock, 0, NULL);
interrupt_pos = AddWaitEventToSet(FeBeWaitSet, WL_INTERRUPT, PGINVALID_SOCKET,
- INTERRUPT_GENERAL, NULL);
+ INTERRUPT_CFI_MASK(), NULL);
AddWaitEventToSet(FeBeWaitSet, WL_POSTMASTER_DEATH, PGINVALID_SOCKET,
0, NULL);
@@ -1413,8 +1417,7 @@ internal_flush_buffer(const char *buf, size_t *start, size_t *end)
* the connection.
*/
*start = *end = 0;
- ClientConnectionLost = 1;
- InterruptPending = 1;
+ RaiseInterrupt(INTERRUPT_CLIENT_CONNECTION_LOST);
return EOF;
}
@@ -2065,24 +2068,13 @@ pq_check_connection(void)
* all FeBeWaitSet socket wait sites do the same.
*/
ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, WL_SOCKET_CLOSED, 0);
+ ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetInterruptPos, WL_INTERRUPT, 0);
-retry:
rc = WaitEventSetWait(FeBeWaitSet, 0, events, lengthof(events), 0);
for (int i = 0; i < rc; ++i)
{
if (events[i].events & WL_SOCKET_CLOSED)
return false;
- if (events[i].events & WL_INTERRUPT)
- {
- /*
- * 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 INTERRUPT_GENERAL to survive
- * across CHECK_FOR_INTERRUPTS().
- */
- ClearInterrupt(INTERRUPT_GENERAL);
- goto retry;
- }
}
return true;
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index b6928536984..0bc349262fd 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -26,7 +26,6 @@
static shm_mq_handle *pq_mq_handle;
static bool pq_mq_busy = false;
-static pid_t pq_mq_parallel_leader_pid = 0;
static ProcNumber pq_mq_parallel_leader_proc_number = INVALID_PROC_NUMBER;
static void pq_cleanup_redirect_to_shm_mq(dsm_segment *seg, Datum arg);
@@ -72,14 +71,13 @@ pq_cleanup_redirect_to_shm_mq(dsm_segment *seg, Datum arg)
}
/*
- * Arrange to SendProcSignal() to the parallel leader each time we transmit
- * message data via the shm_mq.
+ * Arrange to interrupt to the parallel leader each time we transmit message
+ * data via the shm_mq.
*/
void
-pq_set_parallel_leader(pid_t pid, ProcNumber procNumber)
+pq_set_parallel_leader(ProcNumber procNumber)
{
Assert(PqCommMethods == &PqCommMqMethods);
- pq_mq_parallel_leader_pid = pid;
pq_mq_parallel_leader_proc_number = procNumber;
}
@@ -164,25 +162,23 @@ mq_putmessage(char msgtype, const char *s, size_t len)
*/
result = shm_mq_sendv(pq_mq_handle, iov, 2, true, true);
- if (pq_mq_parallel_leader_pid != 0)
+ if (pq_mq_parallel_leader_proc_number != INVALID_PROC_NUMBER)
{
if (IsLogicalParallelApplyWorker())
- SendProcSignal(pq_mq_parallel_leader_pid,
- PROCSIG_PARALLEL_APPLY_MESSAGE,
- pq_mq_parallel_leader_proc_number);
+ SendInterrupt(INTERRUPT_PARALLEL_APPLY_MESSAGE,
+ pq_mq_parallel_leader_proc_number);
else
{
Assert(IsParallelWorker());
- SendProcSignal(pq_mq_parallel_leader_pid,
- PROCSIG_PARALLEL_MESSAGE,
- pq_mq_parallel_leader_proc_number);
+ SendInterrupt(INTERRUPT_PARALLEL_MESSAGE,
+ pq_mq_parallel_leader_proc_number);
}
}
if (result != SHM_MQ_WOULD_BLOCK)
break;
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
WAIT_EVENT_MESSAGE_QUEUE_PUT_MESSAGE);
ClearInterrupt(INTERRUPT_GENERAL);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index c45b332e822..7bc08df4802 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -400,7 +400,7 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len)
InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, avl_sigusr2_handler);
pqsignal(SIGFPE, FloatExceptionHandler);
pqsignal(SIGCHLD, SIG_DFL);
@@ -450,7 +450,7 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len)
/* Forget any pending QueryCancel or timeout request */
disable_all_timeouts(false);
- QueryCancelPending = false; /* second to avoid race condition */
+ ClearInterrupt(INTERRUPT_QUERY_CANCEL); /* second to avoid race condition */
/* Report the error to the server log */
EmitErrorReport();
@@ -491,7 +491,7 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len)
RESUME_INTERRUPTS();
/* if in shutdown mode, no need for anything further; just go away */
- if (ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
AutoVacLauncherShutdown();
/*
@@ -550,7 +550,7 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len)
*/
if (!AutoVacuumingActive())
{
- if (!ShutdownRequestPending)
+ if (!IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
do_start_worker();
proc_exit(0); /* done */
}
@@ -567,7 +567,7 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len)
rebuild_database_list(InvalidOid);
/* loop until shutdown request */
- while (!ShutdownRequestPending)
+ while (!IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
{
struct timeval nap;
TimestampTz current_time = 0;
@@ -586,15 +586,19 @@ AutoVacLauncherMain(const void *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 RaiseInterrupt).
*/
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_SHUTDOWN_AUX |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_BARRIER |
+ INTERRUPT_LOG_MEMORY_CONTEXT |
+ INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
(nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
WAIT_EVENT_AUTOVACUUM_MAIN);
- ClearInterrupt(INTERRUPT_GENERAL);
-
ProcessAutoVacLauncherInterrupts();
+ ClearInterrupt(INTERRUPT_GENERAL);
+
/*
* a worker finished, or postmaster signaled failure to start a worker
*/
@@ -744,14 +748,13 @@ static void
ProcessAutoVacLauncherInterrupts(void)
{
/* the normal shutdown case */
- if (ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
AutoVacLauncherShutdown();
- if (ConfigReloadPending)
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
{
int autovacuum_max_workers_prev = autovacuum_max_workers;
- ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
/* shutdown requested in config file? */
@@ -771,11 +774,11 @@ ProcessAutoVacLauncherInterrupts(void)
}
/* Process barrier events */
- if (ProcSignalBarrierPending)
+ if (IsInterruptPending(INTERRUPT_BARRIER))
ProcessProcSignalBarrier();
/* Perform logging of memory contexts of this process */
- if (LogMemoryContextPending)
+ if (IsInterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT))
ProcessLogMemoryContextInterrupt();
/* Process sinval catchup interrupts that happened while sleeping */
@@ -1407,7 +1410,7 @@ AutoVacWorkerMain(const void *startup_data, size_t startup_data_len)
InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN);
pqsignal(SIGFPE, FloatExceptionHandler);
pqsignal(SIGCHLD, SIG_DFL);
@@ -1432,7 +1435,7 @@ AutoVacWorkerMain(const void *startup_data, size_t startup_data_len)
* (to wit, BlockSig) will be restored when longjmp'ing to here. Thus,
* signals other than SIGQUIT will be blocked until we exit. It might
* seem that this policy makes the HOLD_INTERRUPTS() call redundant, but
- * it is not since InterruptPending might be set already.
+ * it is not since XXXX InterruptPending might be set already.
*/
if (sigsetjmp(local_sigjmp_buf, 1) != 0)
{
@@ -2277,9 +2280,8 @@ do_autovacuum(void)
/*
* Check for config changes before processing each collected table.
*/
- if (ConfigReloadPending)
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
{
- ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
/*
@@ -2437,7 +2439,7 @@ do_autovacuum(void)
* current table (we're done with it, so it would make no sense to
* cancel at this point.)
*/
- QueryCancelPending = false;
+ ClearInterrupt(INTERRUPT_QUERY_CANCEL);
}
PG_CATCH();
{
@@ -2521,9 +2523,8 @@ deleted:
* Check for config changes before acquiring lock for further jobs.
*/
CHECK_FOR_INTERRUPTS();
- if (ConfigReloadPending)
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
{
- ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
VacuumUpdateCosts();
}
@@ -2635,7 +2636,7 @@ perform_work_item(AutoVacuumWorkItem *workitem)
* (we're done with it, so it would make no sense to cancel at this
* point.)
*/
- QueryCancelPending = false;
+ ClearInterrupt(INTERRUPT_QUERY_CANCEL);
}
PG_CATCH();
{
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 4425e404a35..8c522adb07a 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,7 +26,6 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
-#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
#include "utils/ascii.h"
@@ -751,7 +750,7 @@ BackgroundWorkerMain(const void *startup_data, size_t startup_data_len)
* SIGINT is used to signal canceling the current action
*/
pqsignal(SIGINT, StatementCancelHandler);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGFPE, FloatExceptionHandler);
/* XXX Any other handlers needed here? */
@@ -1233,7 +1232,7 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
if (status != BGWH_NOT_YET_STARTED)
break;
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_POSTMASTER_DEATH, 0,
WAIT_EVENT_BGWORKER_STARTUP);
@@ -1277,7 +1276,7 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
if (status == BGWH_STOPPED)
break;
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_POSTMASTER_DEATH, 0,
WAIT_EVENT_BGWORKER_SHUTDOWN);
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index ae7d6f66cc6..2ab3f5049be 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -44,7 +44,6 @@
#include "storage/fd.h"
#include "storage/lwlock.h"
#include "storage/proc.h"
-#include "storage/procsignal.h"
#include "storage/smgr.h"
#include "storage/standby.h"
#include "utils/memutils.h"
@@ -105,7 +104,7 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len)
/* SIGQUIT handler was already set up by InitPostmasterChild */
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN);
/*
@@ -223,9 +222,7 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len)
bool can_hibernate;
int rc;
- /* Clear any already-pending wakeups */
- ClearInterrupt(INTERRUPT_GENERAL);
-
+ /* Process any pending standard interrupts */
ProcessMainLoopInterrupts();
/*
@@ -302,7 +299,7 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len)
* down with interrupt events that are likely to happen frequently
* during normal operation.
*/
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_MAIN_LOOP_MASK,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
BgWriterDelay /* ms */ , WAIT_EVENT_BGWRITER_MAIN);
@@ -326,10 +323,14 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len)
*/
if (rc == WL_TIMEOUT && can_hibernate && prev_hibernate)
{
+ /* Clear any already-pending wakeups */
+ ClearInterrupt(INTERRUPT_GENERAL);
+
/* Ask for notification at next buffer allocation */
StrategyNotifyBgWriter(MyProcNumber);
/* Sleep ... */
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_MAIN_LOOP_MASK |
+ INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
BgWriterDelay * HIBERNATE_FACTOR,
WAIT_EVENT_BGWRITER_HIBERNATE);
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 363a42018e0..a0c1de9491f 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -80,10 +80,11 @@
* The algorithm for backends is:
* 1. Record current values of ckpt_failed and ckpt_started, and
* set request flags, while holding ckpt_lck.
- * 2. Send signal to request checkpoint.
+ * 2. Send INTERRUPT_GENERAL to wake up the checkpointer.
* 3. Sleep until ckpt_started changes. Now you know a checkpoint has
* begun since you started this algorithm (although *not* that it was
- * specifically initiated by your signal), and that it is using your flags.
+ * specifically initiated by your interrupt), and that it is using your
+ * flags.
* 4. Record new value of ckpt_started.
* 5. Sleep until ckpt_done >= saved value of ckpt_started. (Use modulo
* arithmetic here in case counters wrap around.) Now you know a
@@ -145,7 +146,6 @@ double CheckPointCompletionTarget = 0.9;
* Private state
*/
static bool ckpt_active = false;
-static volatile sig_atomic_t ShutdownXLOGPending = false;
/* these values are valid when ckpt_active is true: */
static pg_time_t ckpt_start_time;
@@ -201,7 +201,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
/* SIGQUIT handler was already set up by InitPostmasterChild */
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, SignalHandlerForShutdownRequest);
/*
@@ -355,12 +355,13 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
ClearInterrupt(INTERRUPT_GENERAL);
/*
- * Process any requests or signals received recently.
+ * Process any requests or interrupts received recently.
*/
AbsorbSyncRequests();
ProcessCheckpointerInterrupts();
- if (ShutdownXLOGPending || ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_XLOG) ||
+ IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
break;
/*
@@ -537,7 +538,8 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
* interrupt might have been reset (e.g. in CheckpointWriteDelay).
*/
ProcessCheckpointerInterrupts();
- if (ShutdownXLOGPending || ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_XLOG) ||
+ IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
break;
}
@@ -556,7 +558,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
continue;
/*
- * Sleep until we are signaled or it's time for another checkpoint or
+ * Sleep until we are woken up or it's time for another checkpoint or
* xlog file switch.
*/
now = (pg_time_t) time(NULL);
@@ -572,10 +574,18 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
cur_timeout = Min(cur_timeout, XLogArchiveTimeout - elapsed_secs);
}
- (void) WaitInterrupt(INTERRUPT_GENERAL,
- WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- cur_timeout * 1000L /* convert to ms */ ,
- WAIT_EVENT_CHECKPOINTER_MAIN);
+ (void) WaitInterrupt(
+ /* these are handled in the main loop */
+ INTERRUPT_GENERAL | /* checkpoint requested */
+ INTERRUPT_SHUTDOWN_XLOG |
+ INTERRUPT_SHUTDOWN_AUX |
+ /* ProcessCheckpointerInterrupts() */
+ INTERRUPT_BARRIER |
+ INTERRUPT_LOG_MEMORY_CONTEXT |
+ INTERRUPT_CONFIG_RELOAD,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ cur_timeout * 1000L /* convert to ms */ ,
+ WAIT_EVENT_CHECKPOINTER_MAIN);
}
/*
@@ -584,7 +594,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
*/
ExitOnAnyError = true;
- if (ShutdownXLOGPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_XLOG))
{
/*
* Close down the database.
@@ -602,7 +612,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
* Tell postmaster that we're done.
*/
SendPostmasterSignal(PMSIGNAL_XLOG_IS_SHUTDOWN);
- ShutdownXLOGPending = false;
+ ClearInterrupt(INTERRUPT_SHUTDOWN_XLOG);
}
/*
@@ -612,15 +622,16 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
*/
for (;;)
{
- /* Clear any already-pending wakeups */
- ClearInterrupt(INTERRUPT_GENERAL);
-
ProcessCheckpointerInterrupts();
- if (ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
break;
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_SHUTDOWN_AUX |
+ /* ProcessCheckpointerInterrupts() */
+ INTERRUPT_BARRIER |
+ INTERRUPT_LOG_MEMORY_CONTEXT |
+ INTERRUPT_CONFIG_RELOAD,
WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
0,
WAIT_EVENT_CHECKPOINTER_SHUTDOWN);
@@ -636,12 +647,11 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
static void
ProcessCheckpointerInterrupts(void)
{
- if (ProcSignalBarrierPending)
+ if (IsInterruptPending(INTERRUPT_BARRIER))
ProcessProcSignalBarrier();
- if (ConfigReloadPending)
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
{
- ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
/*
@@ -659,7 +669,7 @@ ProcessCheckpointerInterrupts(void)
}
/* Perform logging of memory contexts of this process */
- if (LogMemoryContextPending)
+ if (IsInterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT))
ProcessLogMemoryContextInterrupt();
}
@@ -777,14 +787,13 @@ CheckpointWriteDelay(int flags, double progress)
* in which case we just try to catch up as quickly as possible.
*/
if (!(flags & CHECKPOINT_IMMEDIATE) &&
- !ShutdownXLOGPending &&
- !ShutdownRequestPending &&
+ !IsInterruptPending(INTERRUPT_SHUTDOWN_XLOG) &&
+ !IsInterruptPending(INTERRUPT_SHUTDOWN_AUX) &&
!ImmediateCheckpointRequested() &&
IsCheckpointOnSchedule(progress))
{
- if (ConfigReloadPending)
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
{
- ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
/* update shmem copies of config variables */
UpdateSharedMemoryConfig();
@@ -804,7 +813,7 @@ CheckpointWriteDelay(int flags, double progress)
* Checkpointer and bgwriter are no longer related so take the Big
* Sleep.
*/
- WaitInterrupt(INTERRUPT_GENERAL,
+ WaitInterrupt(INTERRUPT_GENERAL | INTERRUPT_CONFIG_RELOAD,
WL_INTERRUPT | WL_EXIT_ON_PM_DEATH | WL_TIMEOUT,
100,
WAIT_EVENT_CHECKPOINT_WRITE_DELAY);
@@ -822,7 +831,7 @@ CheckpointWriteDelay(int flags, double progress)
}
/* Check for barrier events. */
- if (ProcSignalBarrierPending)
+ if (IsInterruptPending(INTERRUPT_BARRIER))
ProcessProcSignalBarrier();
}
@@ -916,8 +925,7 @@ IsCheckpointOnSchedule(double progress)
static void
ReqShutdownXLOG(SIGNAL_ARGS)
{
- ShutdownXLOGPending = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_SHUTDOWN_XLOG);
}
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index f325020ade2..778cd67b641 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -25,9 +25,7 @@
#include "storage/waiteventset.h"
#include "utils/guc.h"
#include "utils/memutils.h"
-
-volatile sig_atomic_t ConfigReloadPending = false;
-volatile sig_atomic_t ShutdownRequestPending = false;
+#include "utils/resowner.h"
/* A common WaitEventSet used to implement WaitInterrupt() */
static WaitEventSet *InterruptWaitSet;
@@ -40,6 +38,10 @@ static pg_atomic_uint32 LocalPendingInterrupts;
pg_atomic_uint32 *MyPendingInterrupts = &LocalPendingInterrupts;
+volatile uint32 InterruptHoldoffCount = 0;
+volatile uint32 QueryCancelHoldoffCount = 0;
+volatile uint32 CritSectionCount = 0;
+
/*
* Switch to local interrupts. Other backends can't send interrupts to this
* one. Only RaiseInterrupt() can set them, from inside this process.
@@ -129,6 +131,8 @@ SendInterrupt(uint32 interruptMask, ProcNumber pgprocno)
proc = &ProcGlobal->allProcs[pgprocno];
old_pending = pg_atomic_fetch_or_u32(&proc->pendingInterrupts, interruptMask);
+ /* need a memory barrier here? Or is WakeupOtherProc() sufficient? */
+
/*
* If the process is currently blocked waiting for an interrupt to arrive,
* and the interrupt wasn't already pending, wake it up.
@@ -283,20 +287,17 @@ WaitInterruptOrSocket(uint32 interruptMask, int wakeEvents, pgsocket sock,
void
ProcessMainLoopInterrupts(void)
{
- if (ProcSignalBarrierPending)
+ if (IsInterruptPending(INTERRUPT_BARRIER))
ProcessProcSignalBarrier();
- if (ConfigReloadPending)
- {
- ConfigReloadPending = false;
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
ProcessConfigFile(PGC_SIGHUP);
- }
- if (ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
proc_exit(0);
/* Perform logging of memory contexts of this process */
- if (LogMemoryContextPending)
+ if (IsInterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT))
ProcessLogMemoryContextInterrupt();
}
@@ -304,14 +305,13 @@ ProcessMainLoopInterrupts(void)
* Simple signal handler for triggering a configuration reload.
*
* Normally, this handler would be used for SIGHUP. The idea is that code
- * which uses it would arrange to check the ConfigReloadPending flag at
- * convenient places inside main loops, or else call ProcessMainLoopInterrupts.
+ * which uses it would arrange to check the INTERRUPT_CONFIG_RELOAD interrupt at
+ * convenient places inside main loops, or else call HandleMainLoopInterrupts.
*/
void
SignalHandlerForConfigReload(SIGNAL_ARGS)
{
- ConfigReloadPending = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_CONFIG_RELOAD);
}
/*
@@ -348,12 +348,11 @@ SignalHandlerForCrashExit(SIGNAL_ARGS)
* writer and the logical replication parallel apply worker exits on either
* SIGINT or SIGTERM.
*
- * ShutdownRequestPending should be checked at a convenient place within the
+ * INTERRUPT_SHUTDOWN_AUX should be checked at a convenient place within the
* main loop, or else the main loop should call ProcessMainLoopInterrupts.
*/
void
SignalHandlerForShutdownRequest(SIGNAL_ARGS)
{
- ShutdownRequestPending = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_SHUTDOWN_AUX);
}
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 4f28ea5b613..faa8e8acbd2 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -230,7 +230,7 @@ PgArchiverMain(const void *startup_data, size_t startup_data_len)
/* SIGQUIT handler was already set up by InitPostmasterChild */
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, pgarch_waken_stop);
/* Reset some signals that are accepted by postmaster but not here */
@@ -331,7 +331,7 @@ pgarch_MainLoop(void)
* idea. If more than 60 seconds pass since SIGTERM, exit anyway, so
* that the postmaster can start a new archiver if needed.
*/
- if (ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
{
time_t curtime = time(NULL);
@@ -353,7 +353,11 @@ pgarch_MainLoop(void)
{
int rc;
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_GENERAL |
+ INTERRUPT_SHUTDOWN_AUX |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_BARRIER |
+ INTERRUPT_LOG_MEMORY_CONTEXT,
WL_INTERRUPT | WL_TIMEOUT | WL_POSTMASTER_DEATH,
PGARCH_AUTOWAKE_INTERVAL * 1000L,
WAIT_EVENT_ARCHIVER_MAIN);
@@ -405,7 +409,7 @@ pgarch_ArchiverCopyLoop(void)
* command, and the second is to avoid conflicts with another
* archiver spawned by a newer postmaster.
*/
- if (ShutdownRequestPending || !PostmasterIsAlive())
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_AUX) || !PostmasterIsAlive())
return;
/*
@@ -856,19 +860,19 @@ pgarch_die(int code, Datum arg)
static void
ProcessPgArchInterrupts(void)
{
- if (ProcSignalBarrierPending)
+ if (IsInterruptPending(INTERRUPT_BARRIER))
ProcessProcSignalBarrier();
/* Perform logging of memory contexts of this process */
- if (LogMemoryContextPending)
+ if (IsInterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT))
ProcessLogMemoryContextInterrupt();
- if (ConfigReloadPending)
+ if (IsInterruptPending(INTERRUPT_CONFIG_RELOAD))
{
char *archiveLib = pstrdup(XLogArchiveLibrary);
bool archiveLibChanged;
- ConfigReloadPending = false;
+ ClearInterrupt(INTERRUPT_CONFIG_RELOAD);
ProcessConfigFile(PGC_SIGHUP);
if (XLogArchiveLibrary[0] != '\0' && XLogArchiveCommand[0] != '\0')
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index f1332eb2195..ef27558aac2 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -1621,7 +1621,8 @@ ConfigurePostmasterWaitSet(bool accept_connections)
pm_wait_set = CreateWaitEventSet(NULL,
accept_connections ? (1 + NumListenSockets) : 1);
AddWaitEventToSet(pm_wait_set, WL_INTERRUPT, PGINVALID_SOCKET,
- INTERRUPT_GENERAL, NULL);
+ INTERRUPT_CONFIG_RELOAD | INTERRUPT_GENERAL,
+ NULL);
if (accept_connections)
{
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index f2ac862513b..abc4e80f1d6 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -25,6 +25,7 @@
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "postmaster/auxprocess.h"
+#include "postmaster/interrupt.h"
#include "postmaster/startup.h"
#include "storage/ipc.h"
#include "storage/pmsignal.h"
@@ -50,7 +51,6 @@
/*
* Flags set by interrupt handlers for later service in the redo loop.
*/
-static volatile sig_atomic_t got_SIGHUP = false;
static volatile sig_atomic_t shutdown_requested = false;
static volatile sig_atomic_t promote_signaled = false;
@@ -101,8 +101,7 @@ StartupProcTriggerHandler(SIGNAL_ARGS)
static void
StartupProcSigHupHandler(SIGNAL_ARGS)
{
- got_SIGHUP = true;
- WakeupRecovery();
+ RaiseInterrupt(INTERRUPT_CONFIG_RELOAD);
}
/* SIGTERM: set flag to abort redo and exit */
@@ -161,11 +160,8 @@ ProcessStartupProcInterrupts(void)
/*
* Process any requests or signals received recently.
*/
- if (got_SIGHUP)
- {
- got_SIGHUP = false;
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
StartupRereadConfig();
- }
/*
* Check if we were requested to exit without finishing recovery.
@@ -187,11 +183,11 @@ ProcessStartupProcInterrupts(void)
exit(1);
/* Process barrier events */
- if (ProcSignalBarrierPending)
+ if (IsInterruptPending(INTERRUPT_BARRIER))
ProcessProcSignalBarrier();
/* Perform logging of memory contexts of this process */
- if (LogMemoryContextPending)
+ if (IsInterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT))
ProcessLogMemoryContextInterrupt();
}
@@ -241,7 +237,7 @@ StartupProcessMain(const void *startup_data, size_t startup_data_len)
/* SIGQUIT handler was already set up by InitPostmasterChild */
InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, StartupProcTriggerHandler);
/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index c4c04ae86d4..e10977869b6 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -337,7 +337,10 @@ SysLoggerMain(const void *startup_data, size_t startup_data_len)
* (including the postmaster).
*/
wes = CreateWaitEventSet(NULL, 2);
- AddWaitEventToSet(wes, WL_INTERRUPT, PGINVALID_SOCKET, INTERRUPT_GENERAL, NULL);
+ AddWaitEventToSet(wes, WL_INTERRUPT, PGINVALID_SOCKET,
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_GENERAL, /* for log rotation */
+ NULL);
#ifndef WIN32
AddWaitEventToSet(wes, WL_SOCKET_READABLE, syslogPipe[0], 0, NULL);
#endif
@@ -360,9 +363,8 @@ SysLoggerMain(const void *startup_data, size_t startup_data_len)
/*
* Process any requests or signals received recently.
*/
- if (ConfigReloadPending)
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
{
- ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
/*
diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c
index 3381767149d..51b2b6d9dd5 100644
--- a/src/backend/postmaster/walsummarizer.c
+++ b/src/backend/postmaster/walsummarizer.c
@@ -250,7 +250,7 @@ WalSummarizerMain(const void *startup_data, size_t startup_data_len)
/* SIGQUIT handler was already set up by InitPostmasterChild */
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN); /* not used */
/* Advertise ourselves. */
@@ -315,7 +315,12 @@ WalSummarizerMain(const void *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) WaitInterrupt(0, WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10000,
+ (void) WaitInterrupt(INTERRUPT_GENERAL |
+ INTERRUPT_BARRIER |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_SHUTDOWN_AUX |
+ INTERRUPT_LOG_MEMORY_CONTEXT,
+ WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10000,
WAIT_EVENT_WAL_SUMMARIZER_ERROR);
}
@@ -855,16 +860,13 @@ GetLatestLSN(TimeLineID *tli)
static void
ProcessWalSummarizerInterrupts(void)
{
- if (ProcSignalBarrierPending)
+ if (IsInterruptPending(INTERRUPT_BARRIER))
ProcessProcSignalBarrier();
- if (ConfigReloadPending)
- {
- ConfigReloadPending = false;
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
ProcessConfigFile(PGC_SIGHUP);
- }
- if (ShutdownRequestPending || !summarize_wal)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_AUX) || !summarize_wal)
{
ereport(DEBUG1,
errmsg_internal("WAL summarizer shutting down"));
@@ -872,7 +874,7 @@ ProcessWalSummarizerInterrupts(void)
}
/* Perform logging of memory contexts of this process */
- if (LogMemoryContextPending)
+ if (IsInterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT))
ProcessLogMemoryContextInterrupt();
}
@@ -1638,7 +1640,11 @@ summarizer_wait_for_wal(void)
pgstat_report_wal(false);
/* OK, now sleep. */
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_GENERAL |
+ INTERRUPT_SHUTDOWN_AUX |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_BARRIER |
+ INTERRUPT_LOG_MEMORY_CONTEXT,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
sleep_quanta * MS_PER_SLEEP_QUANTUM,
WAIT_EVENT_WAL_SUMMARIZER_WAL);
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index ae097472200..341a4a5308e 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -56,7 +56,6 @@
#include "storage/fd.h"
#include "storage/lwlock.h"
#include "storage/proc.h"
-#include "storage/procsignal.h"
#include "storage/smgr.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
@@ -108,7 +107,7 @@ WalWriterMain(const void *startup_data, size_t startup_data_len)
/* SIGQUIT handler was already set up by InitPostmasterChild */
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN); /* not used */
/*
@@ -235,9 +234,6 @@ WalWriterMain(const void *startup_data, size_t startup_data_len)
SetWalWriterSleeping(hibernating);
}
- /* Clear any already-pending wakeups */
- ClearInterrupt(INTERRUPT_GENERAL);
-
/* Process any signals received recently */
ProcessMainLoopInterrupts();
@@ -263,7 +259,7 @@ WalWriterMain(const void *startup_data, size_t startup_data_len)
else
cur_timeout = WalWriterDelay * HIBERNATE_FACTOR;
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_MAIN_LOOP_MASK,
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 aaa12ccdbd0..3704e762c3b 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -237,7 +237,8 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
else
io_flag = WL_SOCKET_WRITEABLE;
- rc = WaitInterruptOrSocket(INTERRUPT_GENERAL,
+ rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK() |
+ INTERRUPT_SHUTDOWN_AUX,
WL_EXIT_ON_PM_DEATH | WL_INTERRUPT | io_flag,
PQsocket(conn->streamConn),
0,
@@ -245,10 +246,7 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
/* Interrupted? */
if (rc & WL_INTERRUPT)
- {
- ClearInterrupt(INTERRUPT_GENERAL);
ProcessWalRcvInterrupts();
- }
/* If socket is ready, advance the libpq state machine */
if (rc & io_flag)
@@ -848,7 +846,8 @@ libpqrcv_PQgetResult(PGconn *streamConn)
* since we'll get interrupted by signals and can handle any
* interrupts here.
*/
- rc = WaitInterruptOrSocket(INTERRUPT_GENERAL,
+ rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK() |
+ INTERRUPT_SHUTDOWN_AUX,
WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE |
WL_INTERRUPT,
PQsocket(streamConn),
@@ -857,10 +856,7 @@ libpqrcv_PQgetResult(PGconn *streamConn)
/* Interrupted? */
if (rc & WL_INTERRUPT)
- {
- ClearInterrupt(INTERRUPT_GENERAL);
ProcessWalRcvInterrupts();
- }
/* Consume whatever data is available from the socket */
if (PQconsumeInput(streamConn) == 0)
diff --git a/src/backend/replication/logical/applyparallelworker.c b/src/backend/replication/logical/applyparallelworker.c
index 176deb406c6..93eeaeda0b2 100644
--- a/src/backend/replication/logical/applyparallelworker.c
+++ b/src/backend/replication/logical/applyparallelworker.c
@@ -238,12 +238,6 @@ static List *ParallelApplyWorkerPool = NIL;
*/
ParallelApplyWorkerShared *MyParallelShared = NULL;
-/*
- * Is there a message sent by a parallel apply worker that the leader apply
- * worker needs to receive?
- */
-volatile sig_atomic_t ParallelApplyMessagePending = false;
-
/*
* Cache the parallel apply worker information required for applying the
* current streaming transaction. It is used to save the cost of searching the
@@ -713,7 +707,7 @@ ProcessParallelApplyInterrupts(void)
{
CHECK_FOR_INTERRUPTS();
- if (ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
{
ereport(LOG,
(errmsg("logical replication parallel apply worker for subscription \"%s\" has finished",
@@ -722,11 +716,8 @@ ProcessParallelApplyInterrupts(void)
proc_exit(0);
}
- if (ConfigReloadPending)
- {
- ConfigReloadPending = false;
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
ProcessConfigFile(PGC_SIGHUP);
- }
}
/* Parallel apply worker main loop. */
@@ -804,7 +795,10 @@ LogicalParallelApplyLoop(shm_mq_handle *mqh)
int rc;
/* Wait for more work. */
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() |
+ INTERRUPT_SHUTDOWN_AUX |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
1000L,
WAIT_EVENT_LOGICAL_PARALLEL_APPLY_MAIN);
@@ -843,9 +837,8 @@ LogicalParallelApplyLoop(shm_mq_handle *mqh)
static void
pa_shutdown(int code, Datum arg)
{
- SendProcSignal(MyLogicalRepWorker->leader_pid,
- PROCSIG_PARALLEL_APPLY_MESSAGE,
- INVALID_PROC_NUMBER);
+ SendInterrupt(INTERRUPT_PARALLEL_APPLY_MESSAGE,
+ MyLogicalRepWorker->leader_pgprocno);
dsm_detach((dsm_segment *) DatumGetPointer(arg));
}
@@ -933,8 +926,7 @@ ParallelApplyWorkerMain(Datum main_arg)
error_mqh = shm_mq_attach(mq, seg, NULL);
pq_redirect_to_shm_mq(seg, error_mqh);
- pq_set_parallel_leader(MyLogicalRepWorker->leader_pid,
- INVALID_PROC_NUMBER);
+ pq_set_parallel_leader(MyLogicalRepWorker->leader_pgprocno);
MyLogicalRepWorker->last_send_time = MyLogicalRepWorker->last_recv_time =
MyLogicalRepWorker->reply_time = 0;
@@ -978,21 +970,6 @@ ParallelApplyWorkerMain(Datum main_arg)
Assert(false);
}
-/*
- * Handle receipt of an interrupt indicating a parallel apply worker message.
- *
- * Note: this is called within a signal handler! All we can do is set a flag
- * that will cause the next CHECK_FOR_INTERRUPTS() to invoke
- * ProcessParallelApplyMessages().
- */
-void
-HandleParallelApplyMessageInterrupt(void)
-{
- InterruptPending = true;
- ParallelApplyMessagePending = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
-}
-
/*
* Process a single protocol message received from a single parallel apply
* worker.
@@ -1057,7 +1034,7 @@ ProcessParallelApplyMessage(StringInfo msg)
}
/*
- * Handle any queued protocol messages received from parallel apply workers.
+ * Process any queued protocol messages received from parallel apply workers.
*/
void
ProcessParallelApplyMessages(void)
@@ -1090,7 +1067,7 @@ ProcessParallelApplyMessages(void)
oldcontext = MemoryContextSwitchTo(hpam_context);
- ParallelApplyMessagePending = false;
+ ClearInterrupt(INTERRUPT_PARALLEL_APPLY_MESSAGE);
foreach(lc, ParallelApplyWorkerPool)
{
@@ -1182,15 +1159,15 @@ pa_send_data(ParallelApplyWorkerInfo *winfo, Size nbytes, const void *data)
Assert(result == SHM_MQ_WOULD_BLOCK);
/* Wait before retrying. */
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
SHM_SEND_RETRY_INTERVAL_MS,
WAIT_EVENT_LOGICAL_APPLY_SEND_DATA);
if (rc & WL_INTERRUPT)
{
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
}
if (startTime == 0)
@@ -1254,16 +1231,16 @@ pa_wait_for_xact_state(ParallelApplyWorkerInfo *winfo,
break;
/* Wait to be signalled. */
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
10L,
WAIT_EVENT_LOGICAL_PARALLEL_APPLY_STATE_CHANGE);
- /* Clear the interrupt flag so we don't spin. */
- ClearInterrupt(INTERRUPT_GENERAL);
-
/* An interrupt may have occurred while we were waiting. */
CHECK_FOR_INTERRUPTS();
+
+ /* Clear the interrupt flag so we don't spin. */
+ ClearInterrupt(INTERRUPT_GENERAL);
}
}
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 168112a3220..76620c80cfb 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -213,14 +213,14 @@ WaitForReplicationWorkerAttach(LogicalRepWorker *worker,
* interrupt about the worker attach. But we don't expect to have to
* wait long.
*/
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
10L, WAIT_EVENT_BGWORKER_STARTUP);
if (rc & WL_INTERRUPT)
{
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
}
}
}
@@ -441,6 +441,7 @@ retry:
worker->relstate_lsn = InvalidXLogRecPtr;
worker->stream_fileset = NULL;
worker->leader_pid = is_parallel_apply_worker ? MyProcPid : InvalidPid;
+ worker->leader_pgprocno = is_parallel_apply_worker ? MyProcNumber : INVALID_PROC_NUMBER;
worker->parallel_apply = is_parallel_apply_worker;
worker->last_lsn = InvalidXLogRecPtr;
TIMESTAMP_NOBEGIN(worker->last_send_time);
@@ -521,7 +522,7 @@ retry:
* slot.
*/
static void
-logicalrep_worker_stop_internal(LogicalRepWorker *worker, int signo)
+logicalrep_worker_stop_internal(LogicalRepWorker *worker, int interrupt)
{
uint16 generation;
@@ -544,14 +545,14 @@ logicalrep_worker_stop_internal(LogicalRepWorker *worker, int signo)
LWLockRelease(LogicalRepWorkerLock);
/* Wait a bit --- we don't expect to have to wait long. */
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
10L, WAIT_EVENT_BGWORKER_STARTUP);
if (rc & WL_INTERRUPT)
{
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
}
/* Recheck worker status. */
@@ -571,7 +572,7 @@ logicalrep_worker_stop_internal(LogicalRepWorker *worker, int signo)
}
/* Now terminate the worker ... */
- kill(worker->proc->pid, signo);
+ SendInterrupt(interrupt, GetNumberFromPGProc(worker->proc));
/* ... and wait for it to die. */
for (;;)
@@ -585,14 +586,14 @@ logicalrep_worker_stop_internal(LogicalRepWorker *worker, int signo)
LWLockRelease(LogicalRepWorkerLock);
/* Wait a bit --- we don't expect to have to wait long. */
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
10L, WAIT_EVENT_BGWORKER_SHUTDOWN);
if (rc & WL_INTERRUPT)
{
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
}
LWLockAcquire(LogicalRepWorkerLock, LW_SHARED);
@@ -614,7 +615,7 @@ logicalrep_worker_stop(Oid subid, Oid relid)
if (worker)
{
Assert(!isParallelApplyWorker(worker));
- logicalrep_worker_stop_internal(worker, SIGTERM);
+ logicalrep_worker_stop_internal(worker, INTERRUPT_DIE);
}
LWLockRelease(LogicalRepWorkerLock);
@@ -661,13 +662,14 @@ logicalrep_pa_worker_stop(ParallelApplyWorkerInfo *winfo)
* Only stop the worker if the generation matches and the worker is alive.
*/
if (worker->generation == generation && worker->proc)
- logicalrep_worker_stop_internal(worker, SIGINT);
+ logicalrep_worker_stop_internal(worker, INTERRUPT_QUERY_CANCEL);
LWLockRelease(LogicalRepWorkerLock);
}
/*
- * Wake up (using interrupt) any logical replication worker for specified sub/rel.
+ * Wake up (using INTERRUPT_GENERAL) any logical replication worker for
+ * specified sub/rel.
*/
void
logicalrep_worker_wakeup(Oid subid, Oid relid)
@@ -762,7 +764,7 @@ logicalrep_worker_detach(void)
LogicalRepWorker *w = (LogicalRepWorker *) lfirst(lc);
if (isParallelApplyWorker(w))
- logicalrep_worker_stop_internal(w, SIGTERM);
+ logicalrep_worker_stop_internal(w, INTERRUPT_DIE);
}
LWLockRelease(LogicalRepWorkerLock);
@@ -792,6 +794,7 @@ logicalrep_worker_cleanup(LogicalRepWorker *worker)
worker->subid = InvalidOid;
worker->relid = InvalidOid;
worker->leader_pid = InvalidPid;
+ worker->leader_pgprocno = INVALID_PROC_NUMBER;
worker->parallel_apply = false;
}
@@ -1217,21 +1220,20 @@ ApplyLauncherMain(Datum main_arg)
MemoryContextDelete(subctx);
/* Wait for more work. */
- rc = WaitInterrupt(INTERRUPT_GENERAL | INTERRUPT_SUBSCRIPTION_CHANGE,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_SUBSCRIPTION_CHANGE |
+ INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
wait_time,
WAIT_EVENT_LOGICAL_LAUNCHER_MAIN);
if (rc & WL_INTERRUPT)
{
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
- }
-
- if (ConfigReloadPending)
- {
- ConfigReloadPending = false;
- ProcessConfigFile(PGC_SIGHUP);
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
+ ProcessConfigFile(PGC_SIGHUP);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
}
diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index 09b2d0d181e..4e564752003 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -72,9 +72,9 @@
/*
* Struct for sharing information to control slot synchronization.
*
- * The slot sync worker's pid is needed by the startup process to shut it
- * down during promotion. The startup process shuts down the slot sync worker
- * and also sets stopSignaled=true to handle the race condition when the
+ * The slot sync worker's proc number is needed by the startup process to shut
+ * it down during promotion. The startup process shuts down the slot sync
+ * worker and also sets stopSignaled=true to handle the race condition when the
* postmaster has not noticed the promotion yet and thus may end up restarting
* the slot sync worker. If stopSignaled is set, the worker will exit in such a
* case. The SQL function pg_sync_replication_slots() will also error out if
@@ -94,7 +94,7 @@
*/
typedef struct SlotSyncCtxStruct
{
- pid_t pid;
+ ProcNumber procno;
bool stopSignaled;
bool syncing;
time_t last_start_time;
@@ -1112,7 +1112,7 @@ slotsync_reread_config(void)
Assert(sync_replication_slots);
- ConfigReloadPending = false;
+ ClearInterrupt(INTERRUPT_CONFIG_RELOAD);
ProcessConfigFile(PGC_SIGHUP);
conninfo_changed = strcmp(old_primary_conninfo, PrimaryConnInfo) != 0;
@@ -1154,7 +1154,7 @@ ProcessSlotSyncInterrupts(WalReceiverConn *wrconn)
{
CHECK_FOR_INTERRUPTS();
- if (ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_DIE))
{
ereport(LOG,
errmsg("replication slot synchronization worker is shutting down on receiving SIGINT"));
@@ -1162,7 +1162,7 @@ ProcessSlotSyncInterrupts(WalReceiverConn *wrconn)
proc_exit(0);
}
- if (ConfigReloadPending)
+ if (IsInterruptPending(INTERRUPT_CONFIG_RELOAD))
slotsync_reread_config();
}
@@ -1206,7 +1206,7 @@ slotsync_worker_onexit(int code, Datum arg)
SpinLockAcquire(&SlotSyncCtx->mutex);
- SlotSyncCtx->pid = InvalidPid;
+ SlotSyncCtx->procno = INVALID_PROC_NUMBER;
/*
* If syncing_slots is true, it indicates that the process errored out
@@ -1252,7 +1252,9 @@ wait_for_slot_activity(bool some_slot_updated)
sleep_ms = MIN_SLOTSYNC_WORKER_NAPTIME_MS;
}
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() |
+ INTERRUPT_GENERAL |
+ INTERRUPT_CONFIG_RELOAD,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
sleep_ms,
WAIT_EVENT_REPLICATION_SLOTSYNC_MAIN);
@@ -1266,12 +1268,12 @@ wait_for_slot_activity(bool some_slot_updated)
* Otherwise, advertise that a sync is in progress.
*/
static void
-check_and_set_sync_info(pid_t worker_pid)
+check_and_set_sync_info(ProcNumber worker_procno)
{
SpinLockAcquire(&SlotSyncCtx->mutex);
/* The worker pid must not be already assigned in SlotSyncCtx */
- Assert(worker_pid == InvalidPid || SlotSyncCtx->pid == InvalidPid);
+ Assert(worker_procno == INVALID_PROC_NUMBER || SlotSyncCtx->procno == INVALID_PROC_NUMBER);
/*
* Emit an error if startup process signaled the slot sync machinery to
@@ -1296,10 +1298,10 @@ check_and_set_sync_info(pid_t worker_pid)
SlotSyncCtx->syncing = true;
/*
- * Advertise the required PID so that the startup process can kill the
- * slot sync worker on promotion.
+ * Advertise the required proc number so that the startup process can kill
+ * the slot sync worker on promotion.
*/
- SlotSyncCtx->pid = worker_pid;
+ SlotSyncCtx->procno = worker_procno;
SpinLockRelease(&SlotSyncCtx->mutex);
@@ -1391,16 +1393,16 @@ ReplSlotSyncWorkerMain(const void *startup_data, size_t startup_data_len)
pqsignal(SIGINT, SignalHandlerForShutdownRequest);
pqsignal(SIGTERM, die);
pqsignal(SIGFPE, FloatExceptionHandler);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGCHLD, SIG_DFL);
- check_and_set_sync_info(MyProcPid);
+ check_and_set_sync_info(MyProcNumber);
ereport(LOG, errmsg("slot sync worker started"));
- /* Register it as soon as SlotSyncCtx->pid is initialized. */
+ /* Register it as soon as SlotSyncCtx->procno is initialized. */
before_shmem_exit(slotsync_worker_onexit, (Datum) 0);
/*
@@ -1521,7 +1523,7 @@ update_synced_slots_inactive_since(void)
return;
/* The slot sync worker or SQL function mustn't be running by now */
- Assert((SlotSyncCtx->pid == InvalidPid) && !SlotSyncCtx->syncing);
+ Assert((SlotSyncCtx->procno == INVALID_PROC_NUMBER) && !SlotSyncCtx->syncing);
LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
@@ -1535,7 +1537,7 @@ update_synced_slots_inactive_since(void)
Assert(SlotIsLogical(s));
/* The slot must not be acquired by any process */
- Assert(s->active_pid == 0);
+ Assert(s->active_proc == INVALID_PROC_NUMBER);
/* Use the same inactive_since time for all the slots. */
if (now == 0)
@@ -1558,7 +1560,7 @@ update_synced_slots_inactive_since(void)
void
ShutDownSlotSync(void)
{
- pid_t worker_pid;
+ ProcNumber worker_procno;
SpinLockAcquire(&SlotSyncCtx->mutex);
@@ -1575,12 +1577,13 @@ ShutDownSlotSync(void)
return;
}
- worker_pid = SlotSyncCtx->pid;
+ worker_procno = SlotSyncCtx->procno;
SpinLockRelease(&SlotSyncCtx->mutex);
- if (worker_pid != InvalidPid)
- kill(worker_pid, SIGINT);
+ /* XXX: we're not holding any locks here. The PGPROC could get recycled for another backend */
+ if (worker_procno != INVALID_PROC_NUMBER)
+ SendInterrupt(INTERRUPT_QUERY_CANCEL, worker_procno);
/* Wait for slot sync to end */
for (;;)
@@ -1588,14 +1591,14 @@ ShutDownSlotSync(void)
int rc;
/* Wait a bit, we don't expect to have to wait long */
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
10L, WAIT_EVENT_REPLICATION_SLOTSYNC_SHUTDOWN);
if (rc & WL_INTERRUPT)
{
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
}
SpinLockAcquire(&SlotSyncCtx->mutex);
@@ -1673,7 +1676,7 @@ SlotSyncShmemInit(void)
if (!found)
{
memset(SlotSyncCtx, 0, size);
- SlotSyncCtx->pid = InvalidPid;
+ SlotSyncCtx->procno = INVALID_PROC_NUMBER;
SpinLockInit(&SlotSyncCtx->mutex);
}
}
@@ -1723,7 +1726,7 @@ SyncReplicationSlots(WalReceiverConn *wrconn)
{
PG_ENSURE_ERROR_CLEANUP(slotsync_failure_callback, PointerGetDatum(wrconn));
{
- check_and_set_sync_info(InvalidPid);
+ check_and_set_sync_info(INVALID_PROC_NUMBER);
validate_remote_info(wrconn);
diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c
index dc9077adb75..5a938d61827 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -211,7 +211,7 @@ wait_for_relation_state_change(Oid relid, char expected_state)
if (!worker)
break;
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
1000L, WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE);
@@ -261,10 +261,10 @@ wait_for_worker_state_change(char expected_state)
break;
/*
- * Wait. We expect to get an interrupt wakeup from the apply worker,
- * but use a timeout in case it dies without sending one.
+ * Wait. We expect to get an INTERRUPT_GENERAL wakeup from the apply
+ * worker, but use a timeout in case it dies without sending one.
*/
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
1000L, WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE);
@@ -774,12 +774,10 @@ copy_read_data(void *outbuf, int minread, int maxread)
/*
* Wait for more data or interrupt.
*/
- (void) WaitInterruptOrSocket(INTERRUPT_GENERAL,
+ (void) WaitInterruptOrSocket(INTERRUPT_CFI_MASK(),
WL_SOCKET_READABLE | WL_INTERRUPT |
WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
fd, 1000L, WAIT_EVENT_LOGICAL_SYNC_DATA);
-
- ClearInterrupt(INTERRUPT_GENERAL);
}
return bytesread;
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index bb566c1ee19..4c136e73a62 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -3654,11 +3654,8 @@ LogicalRepApplyLoop(XLogRecPtr last_received)
int c;
StringInfoData s;
- if (ConfigReloadPending)
- {
- ConfigReloadPending = false;
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
ProcessConfigFile(PGC_SIGHUP);
- }
/* Reset timeout. */
last_recv_timestamp = GetCurrentTimestamp();
@@ -3753,7 +3750,9 @@ LogicalRepApplyLoop(XLogRecPtr last_received)
else
wait_time = NAPTIME_PER_CYCLE;
- rc = WaitInterruptOrSocket(INTERRUPT_GENERAL,
+ rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK() |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_GENERAL,
WL_SOCKET_READABLE | WL_INTERRUPT |
WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
fd, wait_time,
@@ -3761,14 +3760,10 @@ LogicalRepApplyLoop(XLogRecPtr last_received)
if (rc & WL_INTERRUPT)
{
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
- }
-
- if (ConfigReloadPending)
- {
- ConfigReloadPending = false;
- ProcessConfigFile(PGC_SIGHUP);
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
+ ProcessConfigFile(PGC_SIGHUP);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
if (rc & WL_TIMEOUT)
diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index 719e531eb90..5ce7f88f216 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -224,6 +224,7 @@ ReplicationSlotsShmemInit(void)
ReplicationSlot *slot = &ReplicationSlotCtl->replication_slots[i];
/* everything else is zeroed by the memset above */
+ slot->active_proc = INVALID_PROC_NUMBER;
SpinLockInit(&slot->mutex);
LWLockInitialize(&slot->io_in_progress_lock,
LWTRANCHE_REPLICATION_SLOT_IO);
@@ -402,7 +403,7 @@ ReplicationSlotCreate(const char *name, bool db_specific,
* be doing that. So it's safe to initialize the slot.
*/
Assert(!slot->in_use);
- Assert(slot->active_pid == 0);
+ Assert(slot->active_proc == INVALID_PROC_NUMBER);
/* first initialize persistent data */
memset(&slot->data, 0, sizeof(ReplicationSlotPersistentData));
@@ -444,8 +445,8 @@ ReplicationSlotCreate(const char *name, bool db_specific,
/* We can now mark the slot active, and that makes it our slot. */
SpinLockAcquire(&slot->mutex);
- Assert(slot->active_pid == 0);
- slot->active_pid = MyProcPid;
+ Assert(slot->active_proc == INVALID_PROC_NUMBER);
+ slot->active_proc = MyProcNumber;
SpinLockRelease(&slot->mutex);
MyReplicationSlot = slot;
@@ -559,7 +560,7 @@ void
ReplicationSlotAcquire(const char *name, bool nowait, bool error_if_invalid)
{
ReplicationSlot *s;
- int active_pid;
+ ProcNumber active_proc;
Assert(name != NULL);
@@ -600,15 +601,15 @@ retry:
* to inactive_since in InvalidatePossiblyObsoleteSlot.
*/
SpinLockAcquire(&s->mutex);
- if (s->active_pid == 0)
- s->active_pid = MyProcPid;
- active_pid = s->active_pid;
+ if (s->active_proc == INVALID_PROC_NUMBER)
+ s->active_proc = MyProcNumber;
+ active_proc = s->active_proc;
ReplicationSlotSetInactiveSince(s, 0, false);
SpinLockRelease(&s->mutex);
}
else
{
- active_pid = MyProcPid;
+ active_proc = MyProcNumber;
ReplicationSlotSetInactiveSince(s, 0, true);
}
LWLockRelease(ReplicationSlotControlLock);
@@ -618,7 +619,7 @@ retry:
* wait until the owning process signals us that it's been released, or
* error out.
*/
- if (active_pid != MyProcPid)
+ if (active_proc != MyProcNumber)
{
if (!nowait)
{
@@ -632,7 +633,7 @@ retry:
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("replication slot \"%s\" is active for PID %d",
- NameStr(s->data.name), active_pid)));
+ NameStr(s->data.name), GetPGProcByNumber(active_proc)->pid)));
}
else if (!nowait)
ConditionVariableCancelSleep(); /* no sleep needed after all */
@@ -690,7 +691,7 @@ ReplicationSlotRelease(void)
bool is_logical = false; /* keep compiler quiet */
TimestampTz now = 0;
- Assert(slot != NULL && slot->active_pid != 0);
+ Assert(slot != NULL && slot->active_proc != INVALID_PROC_NUMBER);
if (am_walsender)
{
@@ -736,7 +737,7 @@ ReplicationSlotRelease(void)
* disconnecting, but wake up others that may be waiting for it.
*/
SpinLockAcquire(&slot->mutex);
- slot->active_pid = 0;
+ slot->active_proc = INVALID_PROC_NUMBER;
ReplicationSlotSetInactiveSince(slot, now, false);
SpinLockRelease(&slot->mutex);
ConditionVariableBroadcast(&slot->active_cv);
@@ -788,7 +789,7 @@ restart:
continue;
SpinLockAcquire(&s->mutex);
- if ((s->active_pid == MyProcPid &&
+ if ((s->active_proc == MyProcNumber &&
(!synced_only || s->data.synced)))
{
Assert(s->data.persistency == RS_TEMPORARY);
@@ -976,7 +977,7 @@ ReplicationSlotDropPtr(ReplicationSlot *slot)
bool fail_softly = slot->data.persistency != RS_PERSISTENT;
SpinLockAcquire(&slot->mutex);
- slot->active_pid = 0;
+ slot->active_proc = INVALID_PROC_NUMBER;
SpinLockRelease(&slot->mutex);
/* wake up anyone waiting on this slot */
@@ -998,7 +999,7 @@ ReplicationSlotDropPtr(ReplicationSlot *slot)
* Also wake up processes waiting for it.
*/
LWLockAcquire(ReplicationSlotControlLock, LW_EXCLUSIVE);
- slot->active_pid = 0;
+ slot->active_proc = INVALID_PROC_NUMBER;
slot->in_use = false;
LWLockRelease(ReplicationSlotControlLock);
ConditionVariableBroadcast(&slot->active_cv);
@@ -1293,7 +1294,7 @@ ReplicationSlotsCountDBSlots(Oid dboid, int *nslots, int *nactive)
/* count slots with spinlock held */
SpinLockAcquire(&s->mutex);
(*nslots)++;
- if (s->active_pid != 0)
+ if (s->active_proc != INVALID_PROC_NUMBER)
(*nactive)++;
SpinLockRelease(&s->mutex);
}
@@ -1331,7 +1332,7 @@ restart:
{
ReplicationSlot *s;
char *slotname;
- int active_pid;
+ ProcNumber active_proc;
s = &ReplicationSlotCtl->replication_slots[i];
@@ -1353,11 +1354,11 @@ restart:
SpinLockAcquire(&s->mutex);
/* can't change while ReplicationSlotControlLock is held */
slotname = NameStr(s->data.name);
- active_pid = s->active_pid;
- if (active_pid == 0)
+ active_proc = s->active_proc;
+ if (active_proc == INVALID_PROC_NUMBER)
{
MyReplicationSlot = s;
- s->active_pid = MyProcPid;
+ s->active_proc = MyProcNumber;
}
SpinLockRelease(&s->mutex);
@@ -1382,11 +1383,11 @@ restart:
* XXX: We can consider shutting down the slot sync worker before
* trying to drop synced temporary slots here.
*/
- if (active_pid)
+ if (active_proc != INVALID_PROC_NUMBER)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("replication slot \"%s\" is active for PID %d",
- slotname, active_pid)));
+ slotname, GetPGProcByNumber(active_proc)->pid)));
/*
* To avoid duplicating ReplicationSlotDropAcquired() and to avoid
@@ -1734,7 +1735,7 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
{
XLogRecPtr restart_lsn;
NameData slotname;
- int active_pid = 0;
+ ProcNumber active_proc = INVALID_PROC_NUMBER;
ReplicationSlotInvalidationCause invalidation_cause = RS_INVAL_NONE;
TimestampTz now = 0;
long slot_idle_secs = 0;
@@ -1817,17 +1818,17 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
}
slotname = s->data.name;
- active_pid = s->active_pid;
+ active_proc = s->active_proc;
/*
* If the slot can be acquired, do so and mark it invalidated
* immediately. Otherwise we'll signal the owning process, below, and
* retry.
*/
- if (active_pid == 0)
+ if (active_proc == INVALID_PROC_NUMBER)
{
MyReplicationSlot = s;
- s->active_pid = MyProcPid;
+ s->active_proc = MyProcNumber;
s->data.invalidated = invalidation_cause;
/*
@@ -1864,8 +1865,10 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
&slot_idle_usecs);
}
- if (active_pid != 0)
+ if (active_proc != INVALID_PROC_NUMBER)
{
+ int active_pid;
+
/*
* Prepare the sleep on the slot's condition variable before
* releasing the lock, to close a possible race condition if the
@@ -1873,7 +1876,6 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
*/
ConditionVariablePrepareToSleep(&s->active_cv);
- LWLockRelease(ReplicationSlotControlLock);
released_lock = true;
/*
@@ -1882,31 +1884,38 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
* signalling is the only reason for there to be a loop in this
* routine; otherwise we could rely on caller's restart loop.)
*
+ * Hold the lock while doing this, to prevent the PGPROC entry
+ * from being recycled while we do this; PGPROC slots do get
+ * reused quickly.
+ *
* There is the race condition that other process may own the slot
* after its current owner process is terminated and before this
* process owns it. To handle that, we signal only if the PID of
* the owning process has changed from the previous time. (This
* logic assumes that the same PID is not reused very quickly.)
*/
+ active_pid = GetPGProcByNumber(active_proc)->pid;
if (last_signaled_pid != active_pid)
{
- ReportSlotInvalidation(invalidation_cause, true, active_pid,
+ ReportSlotInvalidation(invalidation_cause, true,
+ active_pid,
slotname, restart_lsn,
oldestLSN, snapshotConflictHorizon,
slot_idle_secs);
if (MyBackendType == B_STARTUP)
- (void) SendProcSignal(active_pid,
- PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT,
- INVALID_PROC_NUMBER);
+ SendInterrupt(INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT,
+ active_proc);
else
- (void) kill(active_pid, SIGTERM);
+ SendInterrupt(INTERRUPT_DIE, active_proc);
last_signaled_pid = active_pid;
terminated = true;
invalidation_cause_prev = invalidation_cause;
}
+ LWLockRelease(ReplicationSlotControlLock);
+
/* Wait until the slot is released. */
ConditionVariableSleep(&s->active_cv,
WAIT_EVENT_REPLICATION_SLOT_DROP);
@@ -1937,7 +1946,8 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
ReplicationSlotSave();
ReplicationSlotRelease();
- ReportSlotInvalidation(invalidation_cause, false, active_pid,
+ ReportSlotInvalidation(invalidation_cause, false,
+ GetPGProcByNumber(active_proc)->pid,
slotname, restart_lsn,
oldestLSN, snapshotConflictHorizon,
slot_idle_secs);
@@ -2576,7 +2586,7 @@ RestoreSlotFromDisk(const char *name)
slot->candidate_restart_valid = InvalidXLogRecPtr;
slot->in_use = true;
- slot->active_pid = 0;
+ slot->active_proc = INVALID_PROC_NUMBER;
/*
* Set the time since the slot has become inactive after loading the
@@ -2884,7 +2894,7 @@ StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel)
SpinLockAcquire(&slot->mutex);
restart_lsn = slot->data.restart_lsn;
invalidated = slot->data.invalidated != RS_INVAL_NONE;
- inactive = slot->active_pid == 0;
+ inactive = slot->active_proc == INVALID_PROC_NUMBER;
SpinLockRelease(&slot->mutex);
if (invalidated)
@@ -2970,11 +2980,8 @@ WaitForStandbyConfirmation(XLogRecPtr wait_for_lsn)
{
CHECK_FOR_INTERRUPTS();
- if (ConfigReloadPending)
- {
- ConfigReloadPending = false;
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
ProcessConfigFile(PGC_SIGHUP);
- }
/* Exit if done waiting for every slot. */
if (StandbySlotsHaveCaughtup(wait_for_lsn, WARNING))
@@ -2984,6 +2991,8 @@ WaitForStandbyConfirmation(XLogRecPtr wait_for_lsn)
* Wait for the slots in the synchronized_standby_slots to catch up,
* but use a timeout (1s) so we can also check if the
* synchronized_standby_slots has been changed.
+ *
+ * FIXME: I think we need a wait to add a CV to WaitEventSet
*/
ConditionVariableTimedSleep(&WalSndCtl->wal_confirm_rcv_cv, 1000,
WAIT_EVENT_WAIT_FOR_STANDBY_CONFIRMATION);
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 146eef5871e..6820b131465 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -20,6 +20,7 @@
#include "replication/logical.h"
#include "replication/slot.h"
#include "replication/slotsync.h"
+#include "storage/proc.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/pg_lsn.h"
@@ -255,6 +256,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
{
ReplicationSlot *slot = &ReplicationSlotCtl->replication_slots[slotno];
ReplicationSlot slot_contents;
+ int active_pid;
Datum values[PG_GET_REPLICATION_SLOTS_COLS];
bool nulls[PG_GET_REPLICATION_SLOTS_COLS];
WALAvailability walstate;
@@ -267,6 +269,10 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
/* Copy slot contents while holding spinlock, then examine at leisure */
SpinLockAcquire(&slot->mutex);
slot_contents = *slot;
+ if (slot_contents.active_proc != INVALID_PROC_NUMBER)
+ active_pid = GetPGProcByNumber(slot_contents.active_proc)->pid;
+ else
+ active_pid = 0;
SpinLockRelease(&slot->mutex);
memset(values, 0, sizeof(values));
@@ -291,10 +297,10 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
values[i++] = ObjectIdGetDatum(slot_contents.data.database);
values[i++] = BoolGetDatum(slot_contents.data.persistency == RS_TEMPORARY);
- values[i++] = BoolGetDatum(slot_contents.active_pid != 0);
+ values[i++] = BoolGetDatum(slot_contents.active_proc != INVALID_PROC_NUMBER);
- if (slot_contents.active_pid != 0)
- values[i++] = Int32GetDatum(slot_contents.active_pid);
+ if (slot_contents.active_proc != INVALID_PROC_NUMBER)
+ values[i++] = Int32GetDatum(active_pid);
else
nulls[i++] = true;
@@ -359,13 +365,13 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
*/
if (!XLogRecPtrIsInvalid(slot_contents.data.restart_lsn))
{
- int pid;
+ ProcNumber procno;
SpinLockAcquire(&slot->mutex);
- pid = slot->active_pid;
+ procno = slot->active_proc;
slot_contents.data.restart_lsn = slot->data.restart_lsn;
SpinLockRelease(&slot->mutex);
- if (pid != 0)
+ if (procno != INVALID_PROC_NUMBER)
{
values[i++] = CStringGetTextDatum("unreserved");
walstate = WALAVAIL_UNRESERVED;
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 93d8b9d0605..d977c3a5b23 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -252,10 +252,10 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
* entitled to assume that an acknowledged commit is also replicated,
* which might not be true. So in this case we issue a WARNING (which
* some clients may be able to interpret) and shut off further output.
- * We do NOT reset ProcDiePending, so that the process will die after
+ * We do NOT clear the interrupt bit, so that the process will die after
* the commit is cleaned up.
*/
- if (ProcDiePending)
+ if (IsInterruptPending(INTERRUPT_DIE))
{
ereport(WARNING,
(errcode(ERRCODE_ADMIN_SHUTDOWN),
@@ -272,9 +272,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
* altogether is not helpful, so we just terminate the wait with a
* suitable warning.
*/
- if (QueryCancelPending)
+ if (ConsumeInterrupt(INTERRUPT_QUERY_CANCEL))
{
- QueryCancelPending = false;
ereport(WARNING,
(errmsg("canceling wait for synchronous replication due to user request"),
errdetail("The transaction has already committed locally, but might not have been replicated to the standby.")));
@@ -286,7 +285,9 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
* Wait on interrupt. Any condition that should wake us up will set
* the interrupt, so no need for timeout.
*/
- rc = WaitInterrupt(INTERRUPT_GENERAL,
+ rc = WaitInterrupt(INTERRUPT_GENERAL |
+ INTERRUPT_DIE |
+ INTERRUPT_QUERY_CANCEL,
WL_INTERRUPT | WL_POSTMASTER_DEATH,
-1,
WAIT_EVENT_SYNC_REP);
@@ -297,7 +298,7 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
*/
if (rc & WL_POSTMASTER_DEATH)
{
- ProcDiePending = true;
+ RaiseInterrupt(INTERRUPT_DIE);
whereToSendOutput = DestNone;
SyncRepCancelWait();
break;
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 87b1bad279f..63990360569 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -70,7 +70,6 @@
#include "storage/ipc.h"
#include "storage/proc.h"
#include "storage/procarray.h"
-#include "storage/procsignal.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/guc.h"
@@ -169,7 +168,7 @@ ProcessWalRcvInterrupts(void)
*/
CHECK_FOR_INTERRUPTS();
- if (ShutdownRequestPending)
+ if (IsInterruptPending(INTERRUPT_SHUTDOWN_AUX))
{
ereport(FATAL,
(errcode(ERRCODE_ADMIN_SHUTDOWN),
@@ -284,7 +283,7 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
/* SIGQUIT handler was already set up by InitPostmasterChild */
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN);
/* Reset some signals that are accepted by postmaster but not here */
@@ -461,9 +460,8 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
/* Process any requests or signals received recently */
ProcessWalRcvInterrupts();
- if (ConfigReloadPending)
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
{
- ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
/* recompute wakeup times */
now = GetCurrentTimestamp();
@@ -546,7 +544,10 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
* avoiding some system calls.
*/
Assert(wait_fd != PGINVALID_SOCKET);
- rc = WaitInterruptOrSocket(INTERRUPT_GENERAL,
+ rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK() |
+ INTERRUPT_SHUTDOWN_AUX |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_GENERAL,
WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE |
WL_TIMEOUT | WL_INTERRUPT,
wait_fd,
@@ -554,7 +555,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
WAIT_EVENT_WAL_RECEIVER_MAIN);
if (rc & WL_INTERRUPT)
{
- ClearInterrupt(INTERRUPT_GENERAL);
ProcessWalRcvInterrupts();
if (walrcv->force_reply)
@@ -569,6 +569,7 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
pg_memory_barrier();
XLogWalRcvSendReply(true, false);
}
+ ClearInterrupt(INTERRUPT_GENERAL);
}
if (rc & WL_TIMEOUT)
{
@@ -734,7 +735,9 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
}
SpinLockRelease(&walrcv->mutex);
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() |
+ INTERRUPT_SHUTDOWN_AUX |
+ INTERRUPT_GENERAL,
WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
0,
WAIT_EVENT_WAL_RECEIVER_WAIT_START);
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 0afa181550b..9c807620b7a 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -25,7 +25,7 @@
* the walsender will exit quickly without sending any more XLOG records.
*
* If the server is shut down, checkpointer sends us
- * PROCSIG_WALSND_INIT_STOPPING after all regular backends have exited. If
+ * INTERRUPT_WALSND_INIT_STOPPING after all regular backends have exited. If
* the backend is idle or runs an SQL query this causes the backend to
* shutdown, if logical replication is in progress all existing WAL records
* are processed followed by a shutdown. Otherwise this causes the walsender
@@ -199,9 +199,9 @@ static volatile sig_atomic_t got_STOPPING = false;
/*
* This is set while we are streaming. When not set
- * PROCSIG_WALSND_INIT_STOPPING signal will be handled like SIGTERM. When set,
- * the main loop is responsible for checking got_STOPPING and terminating when
- * it's set (after streaming any remaining WAL).
+ * INTERRUPT_WALSND_INIT_STOPPING interrupt will be handled like SIGTERM. When
+ * set, the main loop is responsible for checking got_STOPPING and terminating
+ * when it's set (after streaming any remaining WAL).
*/
static volatile sig_atomic_t replication_active = false;
@@ -259,7 +259,7 @@ static void WalSndKeepalive(bool requestReply, XLogRecPtr writePtr);
static void WalSndKeepaliveIfNecessary(void);
static void WalSndCheckTimeOut(void);
static long WalSndComputeSleeptime(TimestampTz now);
-static void WalSndWait(uint32 socket_events, long timeout, uint32 wait_event);
+static void WalSndWait(uint32 socket_events, long timeout, uint32 wait_event, uint32 interruptMask);
static void WalSndPrepareWrite(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid, bool last_write);
static void WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid, bool last_write);
static void WalSndUpdateProgress(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
@@ -1608,7 +1608,10 @@ ProcessPendingWrites(void)
/* Sleep until something happens or we time out */
WalSndWait(WL_SOCKET_WRITEABLE | WL_SOCKET_READABLE, sleeptime,
- WAIT_EVENT_WAL_SENDER_WRITE_DATA);
+ WAIT_EVENT_WAL_SENDER_WRITE_DATA,
+ INTERRUPT_CFI_MASK() |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_GENERAL);
/* Clear any already-pending wakeups */
ClearInterrupt(INTERRUPT_GENERAL);
@@ -1616,9 +1619,8 @@ ProcessPendingWrites(void)
CHECK_FOR_INTERRUPTS();
/* Process any requests or signals received recently */
- if (ConfigReloadPending)
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
{
- ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
SyncRepInitConfig();
}
@@ -1628,7 +1630,7 @@ ProcessPendingWrites(void)
WalSndShutdown();
}
- /* reactivate interrupt so WalSndLoop knows to continue */
+ /* reactivate interrupt flag so WalSndLoop knows to continue */
RaiseInterrupt(INTERRUPT_GENERAL);
}
@@ -1822,9 +1824,8 @@ WalSndWaitForWal(XLogRecPtr loc)
CHECK_FOR_INTERRUPTS();
/* Process any requests or signals received recently */
- if (ConfigReloadPending)
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
{
- ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
SyncRepInitConfig();
}
@@ -1934,7 +1935,10 @@ WalSndWaitForWal(XLogRecPtr loc)
Assert(wait_event != 0);
- WalSndWait(wakeEvents, sleeptime, wait_event);
+ WalSndWait(wakeEvents, sleeptime, wait_event,
+ INTERRUPT_CFI_MASK() |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_GENERAL);
}
/* reactivate interrupt flag so WalSndLoop knows to continue */
@@ -2759,9 +2763,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
CHECK_FOR_INTERRUPTS();
/* Process any requests or signals received recently */
- if (ConfigReloadPending)
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
{
- ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
SyncRepInitConfig();
}
@@ -2857,7 +2860,11 @@ WalSndLoop(WalSndSendDataCallback send_data)
wakeEvents |= WL_SOCKET_WRITEABLE;
/* Sleep until something happens or we time out */
- WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN);
+ WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN,
+ INTERRUPT_CFI_MASK() |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_GENERAL
+ );
}
}
}
@@ -2895,6 +2902,7 @@ InitWalSenderSlot(void)
/*
* Found a free slot. Reserve it for us.
*/
+ walsnd->pgprocno = MyProcNumber;
walsnd->pid = MyProcPid;
walsnd->state = WALSNDSTATE_STARTUP;
walsnd->sentPtr = InvalidXLogRecPtr;
@@ -2951,6 +2959,7 @@ WalSndKill(int code, Datum arg)
SpinLockAcquire(&walsnd->mutex);
/* Mark WalSnd struct as no longer being in use. */
walsnd->pid = 0;
+ walsnd->pgprocno = 0;
SpinLockRelease(&walsnd->mutex);
}
@@ -3525,10 +3534,10 @@ WalSndRqstFileReload(void)
}
/*
- * Handle PROCSIG_WALSND_INIT_STOPPING signal.
+ * Process INTERRUPT_WALSND_INIT_STOPPING interrupt
*/
void
-HandleWalSndInitStopping(void)
+ProcessWalSndInitStopping(void)
{
Assert(am_walsender);
@@ -3539,9 +3548,12 @@ HandleWalSndInitStopping(void)
* standby, and then exit gracefully.
*/
if (!replication_active)
- kill(MyProcPid, SIGTERM);
+ RaiseInterrupt(INTERRUPT_DIE);
else
+ {
got_STOPPING = true;
+ RaiseInterrupt(INTERRUPT_GENERAL);
+ }
}
/*
@@ -3567,7 +3579,7 @@ WalSndSignals(void)
/* SIGQUIT handler was already set up by InitPostmasterChild */
InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, WalSndLastCycleHandler); /* request a last cycle and
* shutdown */
@@ -3655,24 +3667,28 @@ WalSndWakeup(bool physical, bool logical)
* on postmaster death.
*/
static void
-WalSndWait(uint32 socket_events, long timeout, uint32 wait_event)
+WalSndWait(uint32 socket_events, long timeout, uint32 wait_event, uint32 interruptMask)
{
WaitEvent event;
+ /* for condition variables */
+ interruptMask |= INTERRUPT_GENERAL;
+
ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, socket_events, 0);
+ ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetInterruptPos, WL_INTERRUPT, interruptMask);
/*
* We use a condition variable to efficiently wake up walsenders in
* WalSndWakeup().
*
* Every walsender prepares to sleep on a shared memory CV. Note that it
- * just prepares to sleep on the CV (i.e., adds itself to the CV's
- * waitlist), but does not actually wait on the CV (IOW, it never calls
- * 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 SendInterrupt(),
- * helping walsenders come out of WaitEventSetWait().
+ * just prepares to sleep on the CV waitlist), but does not actually wait
+ * on the CV (IOW, it never calls 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 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
@@ -3720,16 +3736,16 @@ WalSndInitStopping(void)
for (i = 0; i < max_wal_senders; i++)
{
WalSnd *walsnd = &WalSndCtl->walsnds[i];
- pid_t pid;
+ ProcNumber procno;
SpinLockAcquire(&walsnd->mutex);
- pid = walsnd->pid;
+ procno = walsnd->pgprocno;
SpinLockRelease(&walsnd->mutex);
- if (pid == 0)
+ if (procno == INVALID_PROC_NUMBER)
continue;
- SendProcSignal(pid, PROCSIG_WALSND_INIT_STOPPING, INVALID_PROC_NUMBER);
+ SendInterrupt(INTERRUPT_WALSND_INIT_STOPPING, procno);
}
}
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index d7be4309d4e..4099e141231 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -55,6 +55,7 @@
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
+#include "storage/procsignal.h"
#include "storage/read_stream.h"
#include "storage/smgr.h"
#include "storage/standby.h"
@@ -2976,7 +2977,7 @@ BufferSync(int flags)
UnlockBufHdr(bufHdr, buf_state);
/* Check for barrier events in case NBuffers is large. */
- if (ProcSignalBarrierPending)
+ if (IsInterruptPending(INTERRUPT_BARRIER))
ProcessProcSignalBarrier();
}
@@ -3057,7 +3058,7 @@ BufferSync(int flags)
s->num_to_scan++;
/* Check for barrier events. */
- if (ProcSignalBarrierPending)
+ if (IsInterruptPending(INTERRUPT_BARRIER))
ProcessProcSignalBarrier();
}
@@ -5213,7 +5214,7 @@ LockBufferForCleanup(Buffer buffer)
* deadlock_timeout for it.
*/
if (logged_recovery_conflict)
- LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
+ LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN,
waitStart, GetCurrentTimestamp(),
NULL, false);
@@ -5263,7 +5264,7 @@ LockBufferForCleanup(Buffer buffer)
if (TimestampDifferenceExceeds(waitStart, now,
DeadlockTimeout))
{
- LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
+ LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN,
waitStart, now, NULL, true);
logged_recovery_conflict = true;
}
@@ -5285,16 +5286,17 @@ LockBufferForCleanup(Buffer buffer)
}
else
{
- WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
WAIT_EVENT_BUFFER_PIN);
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
}
/*
* Remove flag marking us as waiter. Normally this will not be set
- * anymore, but ProcWaitForSignal() can return for other signals as
- * well. We take care to only reset the flag if we're the waiter, as
+ * anymore, but WaitInterrupt can return for other reasons as well.
+ * We take care to only reset the flag if we're the waiter, as
* theoretically another backend could have started waiting. That's
* impossible with the current usages due to table level locking, but
* better be safe.
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..5b59f773ea4 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -27,6 +27,7 @@
#ifdef PROFILE_PID_DIR
#include "postmaster/autovacuum.h"
#endif
+#include "postmaster/interrupt.h"
#include "storage/dsm.h"
#include "storage/ipc.h"
#include "tcop/tcopprot.h"
@@ -175,9 +176,8 @@ proc_exit_prepare(int code)
* close up shop already. Note that the signal handlers will not set
* these flags again, now that proc_exit_inprogress is set.
*/
- InterruptPending = false;
- ProcDiePending = false;
- QueryCancelPending = false;
+ ClearInterrupt(INTERRUPT_DIE);
+ ClearInterrupt(INTERRUPT_QUERY_CANCEL);
InterruptHoldoffCount = 1;
CritSectionCount = 0;
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 2e54c11f880..e70a06e754b 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -58,6 +58,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "port/pg_lfind.h"
+#include "postmaster/interrupt.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
@@ -3488,13 +3489,13 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
* Returns pid of the process signaled, or 0 if not found.
*/
pid_t
-CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode)
+CancelVirtualTransaction(VirtualTransactionId vxid, InterruptType reason)
{
- return SignalVirtualTransaction(vxid, sigmode, true);
+ return SignalVirtualTransaction(vxid, reason, true);
}
pid_t
-SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode,
+SignalVirtualTransaction(VirtualTransactionId vxid, InterruptType reason,
bool conflictPending)
{
ProcArrayStruct *arrayP = procArray;
@@ -3522,7 +3523,7 @@ SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode,
* Kill the pid if it's still here. If not, that's what we
* wanted so ignore any errors.
*/
- (void) SendProcSignal(pid, sigmode, vxid.procNumber);
+ SendInterrupt(reason, vxid.procNumber);
}
break;
}
@@ -3656,7 +3657,7 @@ CountDBConnections(Oid databaseid)
* CancelDBBackends --- cancel backends that are using specified database
*/
void
-CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending)
+CancelDBBackends(Oid databaseid, InterruptType reason, bool conflictPending)
{
ProcArrayStruct *arrayP = procArray;
int index;
@@ -3671,20 +3672,17 @@ CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending)
if (databaseid == InvalidOid || proc->databaseId == databaseid)
{
- VirtualTransactionId procvxid;
pid_t pid;
- GET_VXID_FROM_PGPROC(procvxid, *proc);
-
proc->recoveryConflictPending = conflictPending;
pid = proc->pid;
if (pid != 0)
{
/*
- * Kill the pid if it's still here. If not, that's what we
- * wanted so ignore any errors.
+ * Cancel the backend if it's still here. If not, that's what
+ * we wanted anyway.
*/
- (void) SendProcSignal(pid, sigmode, procvxid.procNumber);
+ SendInterrupt(reason, pgprocno);
}
}
}
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index ddbb041ff9f..d3fdaafc3d6 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -27,6 +27,7 @@
#include "replication/walsender.h"
#include "storage/condition_variable.h"
#include "storage/ipc.h"
+#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "storage/sinval.h"
#include "storage/smgr.h"
@@ -34,38 +35,30 @@
#include "utils/memutils.h"
/*
- * The SIGUSR1 signal is multiplexed to support signaling multiple event
- * types. The specific reason is communicated via flags in shared memory.
- * We keep a boolean flag for each possible "reason", so that different
- * reasons can be signaled to a process concurrently. (However, if the same
- * reason is signaled more than once nearly simultaneously, the process may
- * observe it only once.)
+ * State for the ProcSignalBarrier mechanism and query cancellation.
*
- * Each process that wants to receive signals registers its process ID
- * in the ProcSignalSlots array. The array is indexed by ProcNumber to make
- * slot allocation simple, and to avoid having to search the array when you
- * know the ProcNumber of the process you're signaling. (We do support
- * signaling without ProcNumber, but it's a bit less efficient.)
+ * Each process that wants to participate in barriers or query cancellation
+ * registers its process ID in the ProcSignalSlots array. The array is indexed
+ * by ProcNumber to make slot allocation simple.
*
* The fields in each slot are protected by a spinlock, pss_mutex. pss_pid can
* also be read without holding the spinlock, as a quick preliminary check
* when searching for a particular PID in the array.
*
- * pss_signalFlags are intended to be set in cases where we don't need to
- * keep track of whether or not the target process has handled the signal,
- * but sometimes we need confirmation, as when making a global state change
- * that cannot be considered complete until all backends have taken notice
- * of it. For such use cases, we set a bit in pss_barrierCheckMask and then
+ * Plain interrupts are intended to be set in cases where we don't need to
+ * keep track of whether or not the target process has handled the signal, but
+ * sometimes we need confirmation, as when making a global state change that
+ * cannot be considered complete until all backends have taken notice of
+ * it. For such use cases, we set a bit in pss_barrierCheckMask and then
* increment the current "barrier generation"; when the new barrier generation
- * (or greater) appears in the pss_barrierGeneration flag of every process,
- * we know that the message has been received everywhere.
+ * (or greater) appears in the pss_barrierGeneration flag of every process, we
+ * know that the message has been received everywhere.
*/
typedef struct
{
pg_atomic_uint32 pss_pid;
bool pss_cancel_key_valid;
int32 pss_cancel_key;
- volatile sig_atomic_t pss_signalFlags[NUM_PROCSIGNALS];
slock_t pss_mutex; /* protects the above fields */
/* Barrier-related fields (not protected by pss_mutex) */
@@ -104,7 +97,6 @@ struct ProcSignalHeader
NON_EXEC_STATIC ProcSignalHeader *ProcSignal = NULL;
static ProcSignalSlot *MyProcSignalSlot = NULL;
-static bool CheckProcSignal(ProcSignalReason reason);
static void CleanupProcSignalState(int status, Datum arg);
static void ResetProcSignalBarrierBits(uint32 flags);
@@ -150,7 +142,6 @@ ProcSignalShmemInit(void)
pg_atomic_init_u32(&slot->pss_pid, 0);
slot->pss_cancel_key_valid = false;
slot->pss_cancel_key = 0;
- MemSet(slot->pss_signalFlags, 0, sizeof(slot->pss_signalFlags));
pg_atomic_init_u64(&slot->pss_barrierGeneration, PG_UINT64_MAX);
pg_atomic_init_u32(&slot->pss_barrierCheckMask, 0);
ConditionVariableInit(&slot->pss_barrierCV);
@@ -180,9 +171,6 @@ ProcSignalInit(bool cancel_key_valid, int32 cancel_key)
/* Value used for sanity check below */
old_pss_pid = pg_atomic_read_u32(&slot->pss_pid);
- /* Clear out any leftover signal reasons */
- MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));
-
/*
* Initialize barrier state. Since we're a brand-new process, there
* shouldn't be any leftover backend-private state that needs to be
@@ -230,9 +218,10 @@ CleanupProcSignalState(int status, Datum arg)
ProcSignalSlot *slot = MyProcSignalSlot;
/*
- * Clear MyProcSignalSlot, so that a SIGUSR1 received after this point
- * won't try to access it after it's no longer ours (and perhaps even
- * after we've unmapped the shared memory segment).
+ * Clear MyProcSignalSlot, so that if INTERRUPT_BARRIER is received after
+ * this point, ProcessProcSignalBarrier() won't try to access it after
+ * it's no longer ours (and perhaps even after we've unmapped the shared
+ * memory segment).
*/
Assert(MyProcSignalSlot != NULL);
MyProcSignalSlot = NULL;
@@ -268,73 +257,6 @@ CleanupProcSignalState(int status, Datum arg)
ConditionVariableBroadcast(&slot->pss_barrierCV);
}
-/*
- * SendProcSignal
- * Send a signal to a Postgres process
- *
- * Providing procNumber is optional, but it will speed up the operation.
- *
- * On success (a signal was sent), zero is returned.
- * On error, -1 is returned, and errno is set (typically to ESRCH or EPERM).
- *
- * Not to be confused with ProcSendSignal
- */
-int
-SendProcSignal(pid_t pid, ProcSignalReason reason, ProcNumber procNumber)
-{
- volatile ProcSignalSlot *slot;
-
- if (procNumber != INVALID_PROC_NUMBER)
- {
- Assert(procNumber < NumProcSignalSlots);
- slot = &ProcSignal->psh_slot[procNumber];
-
- SpinLockAcquire(&slot->pss_mutex);
- if (pg_atomic_read_u32(&slot->pss_pid) == pid)
- {
- /* Atomically set the proper flag */
- slot->pss_signalFlags[reason] = true;
- SpinLockRelease(&slot->pss_mutex);
- /* Send signal */
- return kill(pid, SIGUSR1);
- }
- SpinLockRelease(&slot->pss_mutex);
- }
- else
- {
- /*
- * procNumber not provided, so search the array using pid. We search
- * the array back to front so as to reduce search overhead. Passing
- * INVALID_PROC_NUMBER means that the target is most likely an
- * auxiliary process, which will have a slot near the end of the
- * array.
- */
- int i;
-
- for (i = NumProcSignalSlots - 1; i >= 0; i--)
- {
- slot = &ProcSignal->psh_slot[i];
-
- if (pg_atomic_read_u32(&slot->pss_pid) == pid)
- {
- SpinLockAcquire(&slot->pss_mutex);
- if (pg_atomic_read_u32(&slot->pss_pid) == pid)
- {
- /* Atomically set the proper flag */
- slot->pss_signalFlags[reason] = true;
- SpinLockRelease(&slot->pss_mutex);
- /* Send signal */
- return kill(pid, SIGUSR1);
- }
- SpinLockRelease(&slot->pss_mutex);
- }
- }
- }
-
- errno = ESRCH;
- return -1;
-}
-
/*
* EmitProcSignalBarrier
* Send a signal to every Postgres process
@@ -379,8 +301,8 @@ EmitProcSignalBarrier(ProcSignalBarrierType type)
pg_atomic_add_fetch_u64(&ProcSignal->psh_barrierGeneration, 1);
/*
- * Signal all the processes, so that they update their advertised barrier
- * generation.
+ * Send an interrupt to all the processes, so that they update their
+ * advertised barrier generation.
*
* Concurrency is not a problem here. Backends that have exited don't
* matter, and new backends that have joined since we entered this
@@ -391,26 +313,8 @@ EmitProcSignalBarrier(ProcSignalBarrierType type)
* backends that need to update state - but they won't actually need to
* change any state.
*/
- for (int i = NumProcSignalSlots - 1; i >= 0; i--)
- {
- volatile ProcSignalSlot *slot = &ProcSignal->psh_slot[i];
- pid_t pid = pg_atomic_read_u32(&slot->pss_pid);
-
- if (pid != 0)
- {
- SpinLockAcquire(&slot->pss_mutex);
- pid = pg_atomic_read_u32(&slot->pss_pid);
- if (pid != 0)
- {
- /* see SendProcSignal for details */
- slot->pss_signalFlags[PROCSIG_BARRIER] = true;
- SpinLockRelease(&slot->pss_mutex);
- kill(pid, SIGUSR1);
- }
- else
- SpinLockRelease(&slot->pss_mutex);
- }
- }
+ for (ProcNumber pgprocno = 0; pgprocno < NumProcSignalSlots; pgprocno++)
+ SendInterrupt(INTERRUPT_BARRIER, pgprocno);
return generation;
}
@@ -469,23 +373,6 @@ WaitForProcSignalBarrier(uint64 generation)
pg_memory_barrier();
}
-/*
- * Handle receipt of an interrupt indicating a global barrier event.
- *
- * All the actual work is deferred to ProcessProcSignalBarrier(), because we
- * cannot safely access the barrier generation inside the signal handler as
- * 64bit atomics might use spinlock based emulation, even for reads. As this
- * routine only gets called when PROCSIG_BARRIER is sent that won't cause a
- * lot of unnecessary work.
- */
-static void
-HandleProcSignalBarrierInterrupt(void)
-{
- InterruptPending = true;
- ProcSignalBarrierPending = true;
- /* interrupt will be raised by procsignal_sigusr1_handler */
-}
-
/*
* Perform global barrier related interrupt checking.
*
@@ -501,12 +388,12 @@ ProcessProcSignalBarrier(void)
uint64 shared_gen;
volatile uint32 flags;
- Assert(MyProcSignalSlot);
-
/* Exit quickly if there's no work to do. */
- if (!ProcSignalBarrierPending)
+ if (!ConsumeInterrupt(INTERRUPT_BARRIER))
+ return;
+
+ if (MyProcSignalSlot == NULL)
return;
- ProcSignalBarrierPending = false;
/*
* It's not unlikely to process multiple barriers at once, before the
@@ -635,86 +522,7 @@ static void
ResetProcSignalBarrierBits(uint32 flags)
{
pg_atomic_fetch_or_u32(&MyProcSignalSlot->pss_barrierCheckMask, flags);
- ProcSignalBarrierPending = true;
- InterruptPending = true;
-}
-
-/*
- * CheckProcSignal - check to see if a particular reason has been
- * signaled, and clear the signal flag. Should be called after receiving
- * SIGUSR1.
- */
-static bool
-CheckProcSignal(ProcSignalReason reason)
-{
- volatile ProcSignalSlot *slot = MyProcSignalSlot;
-
- if (slot != NULL)
- {
- /*
- * Careful here --- don't clear flag if we haven't seen it set.
- * pss_signalFlags is of type "volatile sig_atomic_t" to allow us to
- * read it here safely, without holding the spinlock.
- */
- if (slot->pss_signalFlags[reason])
- {
- slot->pss_signalFlags[reason] = false;
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * procsignal_sigusr1_handler - handle SIGUSR1 signal.
- */
-void
-procsignal_sigusr1_handler(SIGNAL_ARGS)
-{
- if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT))
- HandleCatchupInterrupt();
-
- if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
- HandleNotifyInterrupt();
-
- if (CheckProcSignal(PROCSIG_PARALLEL_MESSAGE))
- HandleParallelMessageInterrupt();
-
- if (CheckProcSignal(PROCSIG_WALSND_INIT_STOPPING))
- HandleWalSndInitStopping();
-
- if (CheckProcSignal(PROCSIG_BARRIER))
- HandleProcSignalBarrierInterrupt();
-
- if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT))
- HandleLogMemoryContextInterrupt();
-
- if (CheckProcSignal(PROCSIG_PARALLEL_APPLY_MESSAGE))
- HandleParallelApplyMessageInterrupt();
-
- if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
- HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
-
- if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE))
- HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
-
- if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK))
- HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);
-
- if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
- HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
-
- if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT))
- HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT);
-
- if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK))
- HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
-
- if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
- HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
-
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_BARRIER);
}
/*
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 0a9879d7024..29268b88e39 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -4,9 +4,10 @@
* single-reader, single-writer shared memory message queue
*
* Both the sender and the receiver must have a PGPROC; their respective
- * 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.
+ * INTERRUPT_GENERAL 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.
*
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -49,7 +50,6 @@
* 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.
@@ -115,6 +115,7 @@ struct shm_mq
* 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
* SendInterrupt() calls, which are quite expensive.
+ * XXX: It's not that expensive anymore, if the process isn't currently waiting
*
* mqh_partial_bytes, mqh_expected_bytes, and mqh_length_word_complete
* are used to track the state of non-blocking operations. When the caller
@@ -343,16 +344,15 @@ 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 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 = false, we'll wait when the ring buffer fills up, and then
+ * continue writing once the receiver has drained some data.
*
- * 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 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.)
+ * When nowait = true, 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 INTERRUPT_GENERAL 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.)
*
* When force_flush = true, we immediately update the shm_mq's mq_bytes_written
* and notify the receiver (if it is already attached). Otherwise, we don't
@@ -559,16 +559,15 @@ 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 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 = false, we'll when the ring buffer is empty and we have not
+ * yet received a full message. The sender will send us INTERRUPT_GENERAL
+ * 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
- * 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.
+ * When nowait = true, we do not wait when the buffer is empty and return
+ * SHM_MQ_WOULD_BLOCK instead. In this case, the caller should call this
+ * function again after INTERRUPT_GENERAL has been set.
*/
shm_mq_result
shm_mq_receive(shm_mq_handle *mqh, Size *nbytesp, void **datap, bool nowait)
@@ -1017,7 +1016,8 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
* interrupt at top of loop, because setting an already-set
* interrupt is much cheaper than setting one that has been reset.
*/
- (void) WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
WAIT_EVENT_MESSAGE_QUEUE_SEND);
/* Clear the interrupt so we don't spin. */
@@ -1163,14 +1163,15 @@ shm_mq_receive_bytes(shm_mq_handle *mqh, Size bytes_needed, bool nowait,
* loop, because setting an already-set interrupt is much cheaper than
* setting one that has been cleared.
*/
- (void) WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
WAIT_EVENT_MESSAGE_QUEUE_RECEIVE);
+ /* Handle standard interrupts that may have occurred while we were waiting. */
+ CHECK_FOR_INTERRUPTS();
+
/* Clear the interrupt so we don't spin. */
ClearInterrupt(INTERRUPT_GENERAL);
-
- /* An interrupt may have occurred while we were waiting. */
- CHECK_FOR_INTERRUPTS();
}
}
@@ -1252,14 +1253,15 @@ shm_mq_wait_internal(shm_mq *mq, PGPROC **ptr, BackgroundWorkerHandle *handle)
}
/* Wait to be signaled. */
- (void) WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
WAIT_EVENT_MESSAGE_QUEUE_INTERNAL);
+ /* Handle standard interrupts that may have occurred while we were waiting. */
+ CHECK_FOR_INTERRUPTS();
+
/* Clear the interrupt so we don't spin. */
ClearInterrupt(INTERRUPT_GENERAL);
-
- /* An interrupt may have occurred while we were waiting. */
- CHECK_FOR_INTERRUPTS();
}
return result;
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index ab22e0b981d..d3f2647c32d 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -205,7 +205,7 @@ pg_wait_until_termination(int pid, int64 timeout)
/* Process interrupts, if any, before waiting */
CHECK_FOR_INTERRUPTS();
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
waittime,
WAIT_EVENT_BACKEND_TERMINATION);
diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c
index 4a66e1b7f7c..a43541ed72d 100644
--- a/src/backend/storage/ipc/sinval.c
+++ b/src/backend/storage/ipc/sinval.c
@@ -20,25 +20,8 @@
#include "storage/sinvaladt.h"
#include "utils/inval.h"
-
uint64 SharedInvalidMessageCounter;
-
-/*
- * Because backends sitting idle will not be reading sinval events, we
- * need a way to give an idle backend a swift kick in the rear and make
- * it catch up before the sinval queue overflows and forces it to go
- * through a cache reset exercise. This is done by sending
- * PROCSIG_CATCHUP_INTERRUPT to any backend that gets too far behind.
- *
- * The signal handler will set an interrupt pending flag and raise the
- * INTERRUPT_GENERAL. Whenever starting to read from the client, or when
- * interrupted while doing so, ProcessClientReadInterrupt() will call
- * ProcessCatchupEvent().
- */
-volatile sig_atomic_t catchupInterruptPending = false;
-
-
/*
* SendSharedInvalidMessages
* Add shared-cache-invalidation message(s) to the global SI message queue.
@@ -132,38 +115,13 @@ ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *m
* catchup signal this way avoids creating spikes in system load for what
* should be just a background maintenance activity.
*/
- if (catchupInterruptPending)
+ if (ConsumeInterrupt(INTERRUPT_SINVAL_CATCHUP))
{
- catchupInterruptPending = false;
elog(DEBUG4, "sinval catchup complete, cleaning queue");
SICleanupQueue(false, 0);
}
}
-
-/*
- * HandleCatchupInterrupt
- *
- * This is called when PROCSIG_CATCHUP_INTERRUPT is received.
- *
- * We used to directly call ProcessCatchupEvent directly when idle. These days
- * we just set a flag to do it later and notify the process of that fact by
- * raising INTERRUPT_GENERAL.
- */
-void
-HandleCatchupInterrupt(void)
-{
- /*
- * Note: this is called by a SIGNAL HANDLER. You must be very wary what
- * you do here.
- */
-
- catchupInterruptPending = true;
-
- /* make sure the event is processed in due course */
- RaiseInterrupt(INTERRUPT_GENERAL);
-}
-
/*
* ProcessCatchupInterrupt
*
@@ -173,12 +131,12 @@ HandleCatchupInterrupt(void)
void
ProcessCatchupInterrupt(void)
{
- while (catchupInterruptPending)
+ while (IsInterruptPending(INTERRUPT_SINVAL_CATCHUP))
{
/*
* What we need to do here is cause ReceiveSharedInvalidMessages() to
* run, which will do the necessary work and also reset the
- * catchupInterruptPending flag. If we are inside a transaction we
+ * INTERRUPT_SINVAL_CATCHUP flag. If we are inside a transaction we
* can just call AcceptInvalidationMessages() to do this. If we
* aren't, we start and immediately end a transaction; the call to
* AcceptInvalidationMessages() happens down inside transaction start.
@@ -190,12 +148,12 @@ ProcessCatchupInterrupt(void)
*/
if (IsTransactionOrTransactionBlock())
{
- elog(DEBUG4, "ProcessCatchupEvent inside transaction");
+ elog(DEBUG4, "ProcessCatchupInterrupt inside transaction");
AcceptInvalidationMessages();
}
else
{
- elog(DEBUG4, "ProcessCatchupEvent outside transaction");
+ elog(DEBUG4, "ProcessCatchupInterrupt outside transaction");
StartTransactionCommand();
CommitTransactionCommand();
}
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index 2da91738c32..97ee90c89cf 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -18,10 +18,10 @@
#include <unistd.h>
#include "miscadmin.h"
+#include "postmaster/interrupt.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "storage/procnumber.h"
-#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "storage/sinvaladt.h"
#include "storage/spin.h"
@@ -118,7 +118,7 @@
* we exceed CLEANUP_MIN. Should be a power of 2 for speed.
*
* SIG_THRESHOLD: the minimum number of messages a backend must have fallen
- * behind before we'll send it PROCSIG_CATCHUP_INTERRUPT.
+ * behind before we'll send it INTERRUPT_SINVAL_CATCHUP.
*
* WRITE_QUANTUM: the max number of messages to push into the buffer per
* iteration of SIInsertDataEntries. Noncritical but should be less than
@@ -564,7 +564,7 @@ SIGetDataEntries(SharedInvalidationMessage *data, int datasize)
* minFree is the minimum number of message slots to make free.
*
* Possible side effects of this routine include marking one or more
- * backends as "reset" in the array, and sending PROCSIG_CATCHUP_INTERRUPT
+ * backends as "reset" in the array, and sending INTERRUPT_SINVAL_CATCHUP
* to some backend that seems to be getting too far behind. We signal at
* most one backend at a time, for reasons explained at the top of the file.
*
@@ -657,8 +657,8 @@ SICleanupQueue(bool callerHasWriteLock, int minFree)
segP->nextThreshold = (numMsgs / CLEANUP_QUANTUM + 1) * CLEANUP_QUANTUM;
/*
- * Lastly, signal anyone who needs a catchup interrupt. Since
- * SendProcSignal() might not be fast, we don't want to hold locks while
+ * Lastly, signal anyone who needs a catchup interrupt. SendInterrupt()
+ * is pretty fast, but we nevertheless don't want to hold locks while
* executing it.
*/
if (needSig)
@@ -669,8 +669,8 @@ SICleanupQueue(bool callerHasWriteLock, int minFree)
needSig->signaled = true;
LWLockRelease(SInvalReadLock);
LWLockRelease(SInvalWriteLock);
- elog(DEBUG4, "sending sinval catchup signal to PID %d", (int) his_pid);
- SendProcSignal(his_pid, PROCSIG_CATCHUP_INTERRUPT, his_procNumber);
+ elog(DEBUG4, "sending sinval catchup interrupt to backend PID %d", (int) his_pid);
+ SendInterrupt(INTERRUPT_SINVAL_CATCHUP, his_procNumber);
if (callerHasWriteLock)
LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE);
}
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 1ef7600522c..f96c1eeeac3 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -71,13 +71,13 @@ static volatile sig_atomic_t got_standby_delay_timeout = false;
static volatile sig_atomic_t got_standby_lock_timeout = false;
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
- ProcSignalReason reason,
+ InterruptType reason,
uint32 wait_event_info,
bool report_waiting);
-static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
+static void SendRecoveryConflictWithBufferPin(InterruptType reason);
static XLogRecPtr LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
-static const char *get_recovery_conflict_desc(ProcSignalReason reason);
+static const char *get_recovery_conflict_desc(InterruptType reason);
/*
* InitRecoveryTransactionEnvironment
@@ -271,7 +271,7 @@ WaitExceedsMaxStandbyDelay(uint32 wait_event_info)
* to be resolved or not.
*/
void
-LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start,
+LogRecoveryConflict(InterruptType reason, TimestampTz wait_start,
TimestampTz now, VirtualTransactionId *wait_list,
bool still_waiting)
{
@@ -358,7 +358,7 @@ LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start,
*/
static void
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
- ProcSignalReason reason, uint32 wait_event_info,
+ InterruptType reason, uint32 wait_event_info,
bool report_waiting)
{
TimestampTz waitStart = 0;
@@ -489,7 +489,7 @@ ResolveRecoveryConflictWithSnapshot(TransactionId snapshotConflictHorizon,
backends = GetConflictingVirtualXIDs(snapshotConflictHorizon,
locator.dbOid);
ResolveRecoveryConflictWithVirtualXIDs(backends,
- PROCSIG_RECOVERY_CONFLICT_SNAPSHOT,
+ INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT,
WAIT_EVENT_RECOVERY_CONFLICT_SNAPSHOT,
true);
@@ -560,7 +560,7 @@ ResolveRecoveryConflictWithTablespace(Oid tsid)
temp_file_users = GetConflictingVirtualXIDs(InvalidTransactionId,
InvalidOid);
ResolveRecoveryConflictWithVirtualXIDs(temp_file_users,
- PROCSIG_RECOVERY_CONFLICT_TABLESPACE,
+ INTERRUPT_RECOVERY_CONFLICT_TABLESPACE,
WAIT_EVENT_RECOVERY_CONFLICT_TABLESPACE,
true);
}
@@ -581,7 +581,7 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
*/
while (CountDBBackends(dbid) > 0)
{
- CancelDBBackends(dbid, PROCSIG_RECOVERY_CONFLICT_DATABASE, true);
+ CancelDBBackends(dbid, INTERRUPT_RECOVERY_CONFLICT_DATABASE, true);
/*
* Wait awhile for them to die so that we avoid flooding an
@@ -665,7 +665,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
* because the caller, WaitOnLock(), has already reported that.
*/
ResolveRecoveryConflictWithVirtualXIDs(backends,
- PROCSIG_RECOVERY_CONFLICT_LOCK,
+ INTERRUPT_RECOVERY_CONFLICT_LOCK,
PG_WAIT_LOCK | locktag.locktag_type,
false);
}
@@ -697,10 +697,11 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
}
/* Wait to be signaled by the release of the Relation Lock */
- WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
PG_WAIT_LOCK | locktag.locktag_type);
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
/*
* Exit if ltime is reached. Then all the backends holding conflicting
@@ -727,7 +728,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
while (VirtualTransactionIdIsValid(*backends))
{
SignalVirtualTransaction(*backends,
- PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
+ INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
false);
backends++;
}
@@ -749,10 +750,11 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
* until the relation locks are released or ltime is reached.
*/
got_standby_deadlock_timeout = false;
- WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
PG_WAIT_LOCK | locktag.locktag_type);
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
}
cleanup:
@@ -778,9 +780,10 @@ cleanup:
* We either resolve conflicts immediately or set a timeout to wake us at
* the limit of our patience.
*
- * Resolve conflicts by sending a PROCSIG signal to all backends to check if
- * they hold one of the buffer pins that is blocking Startup process. If so,
- * those backends will take an appropriate error action, ERROR or FATAL.
+ * Resolve conflicts by sending INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN to all
+ * backends to check if they hold one of the buffer pins that is blocking
+ * Startup process. If so, those backends will take an appropriate error
+ * action, ERROR or FATAL.
*
* We also must check for deadlocks. Deadlocks occur because if queries
* wait on a lock, that must be behind an AccessExclusiveLock, which can only
@@ -809,7 +812,7 @@ ResolveRecoveryConflictWithBufferPin(void)
/*
* We're already behind, so clear a path as quickly as possible.
*/
- SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+ SendRecoveryConflictWithBufferPin(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN);
}
else
{
@@ -844,17 +847,20 @@ 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
- * interrupt flag. FIXME: seems like a shaky assumption. WakeupRecovery()
- * indeed uses a different interrupt flag (different latch earlier), but
- * the signal handler??
+ * interrupt flag.
+ *
+ * FIXME: seems like a shaky assumption. WakeupRecovery() indeed uses a
+ * different interrupt flag (different latch earlier), but the signal
+ * handler??
*/
- WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
WAIT_EVENT_BUFFER_PIN);
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
if (got_standby_delay_timeout)
- SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+ SendRecoveryConflictWithBufferPin(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN);
else if (got_standby_deadlock_timeout)
{
/*
@@ -870,7 +876,7 @@ ResolveRecoveryConflictWithBufferPin(void)
* not be so harmful because the period that the buffer is kept pinned
* is basically no so long. But we should fix this?
*/
- SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+ SendRecoveryConflictWithBufferPin(INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
}
/*
@@ -885,10 +891,10 @@ ResolveRecoveryConflictWithBufferPin(void)
}
static void
-SendRecoveryConflictWithBufferPin(ProcSignalReason reason)
+SendRecoveryConflictWithBufferPin(InterruptType reason)
{
- Assert(reason == PROCSIG_RECOVERY_CONFLICT_BUFFERPIN ||
- reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+ Assert(reason == INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN ||
+ reason == INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
/*
* We send signal to all backends to ask them if they are holding the
@@ -947,6 +953,7 @@ void
StandbyDeadLockHandler(void)
{
got_standby_deadlock_timeout = true;
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -956,6 +963,7 @@ void
StandbyTimeoutHandler(void)
{
got_standby_delay_timeout = true;
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -965,6 +973,7 @@ void
StandbyLockTimeoutHandler(void)
{
got_standby_lock_timeout = true;
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -1489,31 +1498,31 @@ LogStandbyInvalidations(int nmsgs, SharedInvalidationMessage *msgs,
/* Return the description of recovery conflict */
static const char *
-get_recovery_conflict_desc(ProcSignalReason reason)
+get_recovery_conflict_desc(InterruptType reason)
{
const char *reasonDesc = _("unknown reason");
switch (reason)
{
- case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+ case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN:
reasonDesc = _("recovery conflict on buffer pin");
break;
- case PROCSIG_RECOVERY_CONFLICT_LOCK:
+ case INTERRUPT_RECOVERY_CONFLICT_LOCK:
reasonDesc = _("recovery conflict on lock");
break;
- case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+ case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE:
reasonDesc = _("recovery conflict on tablespace");
break;
- case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+ case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT:
reasonDesc = _("recovery conflict on snapshot");
break;
- case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT:
+ case INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT:
reasonDesc = _("recovery conflict on replication slot");
break;
- case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+ case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
reasonDesc = _("recovery conflict on buffer deadlock");
break;
- case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+ case INTERRUPT_RECOVERY_CONFLICT_DATABASE:
reasonDesc = _("recovery conflict on database");
break;
default:
diff --git a/src/backend/storage/ipc/waiteventset.c b/src/backend/storage/ipc/waiteventset.c
index 9380dd58d66..23a88316a66 100644
--- a/src/backend/storage/ipc/waiteventset.c
+++ b/src/backend/storage/ipc/waiteventset.c
@@ -1101,7 +1101,7 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
* synchronization so that if an interrupt bit is set after this,
* the setter will wake us up.
*/
- old_mask = pg_atomic_fetch_or_u32(MyPendingInterrupts, 1 << SLEEPING_ON_INTERRUPTS);
+ old_mask = pg_atomic_fetch_or_u32(MyPendingInterrupts, SLEEPING_ON_INTERRUPTS);
already_pending = ((old_mask & set->interrupt_mask) != 0);
/* remember to clear the SLEEPING_ON_INTERRUPTS flag later */
@@ -1176,7 +1176,7 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
/* If we set the SLEEPING_ON_INTERRUPTS flag, reset it again */
if (sleeping_flag_armed)
- pg_atomic_fetch_and_u32(MyPendingInterrupts, ~((uint32) 1 << SLEEPING_ON_INTERRUPTS));
+ pg_atomic_fetch_and_u32(MyPendingInterrupts, ~((uint32) SLEEPING_ON_INTERRUPTS));
#ifndef WIN32
waiting = false;
diff --git a/src/backend/storage/lmgr/condition_variable.c b/src/backend/storage/lmgr/condition_variable.c
index 295914cf032..4fc4440585b 100644
--- a/src/backend/storage/lmgr/condition_variable.c
+++ b/src/backend/storage/lmgr/condition_variable.c
@@ -161,7 +161,8 @@ ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
* Wait for interrupt. (If we're awakened for some other reason, the
* code below will cope anyway.)
*/
- (void) WaitInterrupt(INTERRUPT_GENERAL, wait_events, cur_timeout, wait_event_info);
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ wait_events, cur_timeout, wait_event_info);
/* Clear the flag before examining the state of the wait list. */
ClearInterrupt(INTERRUPT_GENERAL);
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index ab9b46b9cb9..54cd99a13ef 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -1577,10 +1577,11 @@ GetSafeSnapshot(Snapshot origSnapshot)
SxactIsROUnsafe(MySerializableXact)))
{
LWLockRelease(SerializableXactHashLock);
- WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
WAIT_EVENT_SAFE_SNAPSHOT);
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
}
MySerializableXact->flags &= ~SXACT_FLAG_DEFERRABLE_WAITING;
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 4ebff9a4b37..e4bbcf42261 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -1393,7 +1393,7 @@ ProcSleep(LOCALLOCK *locallock)
* because the startup process here has already waited
* longer than deadlock_timeout.
*/
- LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_LOCK,
+ LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_LOCK,
standbyWaitStart, now,
cnt > 0 ? vxids : NULL, true);
logged_recovery_conflict = true;
@@ -1402,9 +1402,9 @@ ProcSleep(LOCALLOCK *locallock)
}
else
{
- (void) WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
PG_WAIT_LOCK | locallock->tag.lock.locktag_type);
- ClearInterrupt(INTERRUPT_GENERAL);
/* check for deadlocks first, as that's probably log-worthy */
if (got_deadlock_timeout)
{
@@ -1412,6 +1412,7 @@ ProcSleep(LOCALLOCK *locallock)
got_deadlock_timeout = false;
}
CHECK_FOR_INTERRUPTS();
+ ClearInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -1658,7 +1659,7 @@ ProcSleep(LOCALLOCK *locallock)
/*
* Disable the timers, if they are still running. As in LockErrorCleanup,
* we must preserve the LOCK_TIMEOUT indicator flag: if a lock timeout has
- * already caused QueryCancelPending to become set, we want the cancel to
+ * already raised INTERRUPT_QUERY_CANCEL, we want the cancel to
* be reported as a lock timeout, not a user cancel.
*/
if (!InHotStandby)
@@ -1682,7 +1683,7 @@ ProcSleep(LOCALLOCK *locallock)
* startup process waited longer than deadlock_timeout for it.
*/
if (InHotStandby && logged_recovery_conflict)
- LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_LOCK,
+ LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_LOCK,
standbyWaitStart, GetCurrentTimestamp(),
NULL, false);
@@ -1881,10 +1882,6 @@ CheckDeadLockAlert(void)
* 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 raises the interrupt again after the interrupt is
- * raised here.
*/
RaiseInterrupt(INTERRUPT_GENERAL);
errno = save_errno;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 8784ea78dcd..cc19c0157ec 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -66,6 +66,7 @@
#include "storage/proc.h"
#include "storage/procsignal.h"
#include "storage/sinval.h"
+#include "storage/standby.h"
#include "tcop/fastpath.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
@@ -153,10 +154,6 @@ static const char *userDoption = NULL; /* -D switch */
static bool EchoQuery = false; /* -E switch */
static bool UseSemiNewlineNewline = false; /* -j switch */
-/* whether or not, and why, we were canceled by conflict with recovery */
-static volatile sig_atomic_t RecoveryConflictPending = false;
-static volatile sig_atomic_t RecoveryConflictPendingReasons[NUM_PROCSIGNALS];
-
/* reused buffer to pass to SendRowDescriptionMessage() */
static MemoryContext row_description_context = NULL;
static StringInfoData row_description_buf;
@@ -495,42 +492,71 @@ ReadCommand(StringInfo inBuf)
* false if about to read or done reading.
*
* Must preserve errno!
+ *
+ * Returns an interrupt mask with the bits set that a new call to this
+ * function with blocked==true would handle and clear in the current
+ * state. The caller can use the returned mask in a WaitInterrupt() call, to
+ * wait for interrupts that this would handle, and
+ * ProcessClientReadInterrupt() promises to clear those bits if called again.
*/
-void
+uint32
ProcessClientReadInterrupt(bool blocked)
{
int save_errno = errno;
+ uint32 interruptMask = 0;
if (DoingCommandRead)
{
- /* Check for general interrupts that arrived before/while reading */
+ /* Check for standard interrupts that arrived before/while reading */
+ interruptMask |= INTERRUPT_CFI_MASK();
CHECK_FOR_INTERRUPTS();
/* Process sinval catchup interrupts, if any */
- if (catchupInterruptPending)
+ interruptMask |= INTERRUPT_SINVAL_CATCHUP;
+ if (IsInterruptPending(INTERRUPT_SINVAL_CATCHUP))
ProcessCatchupInterrupt();
- /* Process notify interrupts, if any */
- if (notifyInterruptPending)
- ProcessNotifyInterrupt(true);
- }
- else if (ProcDiePending)
- {
/*
- * 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 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 interrupt flag is set, as we might've undesirably
- * cleared it while reading.
+ * If we are truly idle, ie. *not* inside a transaction block matching
+ * the conditions in PostgresMain(), there are a few additional
+ * interrupts that we can process immediately.
*/
- if (blocked)
- CHECK_FOR_INTERRUPTS();
- else
- RaiseInterrupt(INTERRUPT_GENERAL);
+ if (!IsTransactionOrTransactionBlock())
+ {
+ interruptMask |= INTERRUPT_ASYNC_NOTIFY;
+ if (IsInterruptPending(INTERRUPT_ASYNC_NOTIFY))
+ ProcessNotifyInterrupt(true);
+
+ interruptMask |= INTERRUPT_IDLE_STATS_TIMEOUT;
+ if (IsInterruptPending(INTERRUPT_IDLE_STATS_TIMEOUT))
+ {
+ ClearInterrupt(INTERRUPT_IDLE_STATS_TIMEOUT);
+ pgstat_report_stat(true);
+ }
+ }
+ }
+ else if ((INTERRUPT_CFI_MASK() & INTERRUPT_DIE) != 0)
+ {
+ interruptMask |= INTERRUPT_DIE;
+ if (IsInterruptPending(INTERRUPT_DIE))
+ {
+ /*
+ * 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,
+ * don't clear the interrupt flag is set, so that if there is no data
+ * then we'll come back here and die.
+ */
+ if (blocked)
+ {
+ CHECK_FOR_INTERRUPTS();
+ Assert(false); /* CHECK_FOR_INTERRUPTS should've bailed out */
+ }
+ }
}
errno = save_errno;
+
+ return interruptMask;
}
/*
@@ -541,30 +567,30 @@ ProcessClientReadInterrupt(bool blocked)
* false if about to write or done writing.
*
* Must preserve errno!
+ *
+ * Like ProcessClientReadInterrupt, returns an interrupt mask with the bits
+ * set that a new call to this function with blocked==true would handle and
+ * clear in the current state.
*/
-void
+uint32
ProcessClientWriteInterrupt(bool blocked)
{
int save_errno = errno;
+ uint32 interruptMask = 0;
- if (ProcDiePending)
+ if ((INTERRUPT_CFI_MASK() & INTERRUPT_DIE) != 0)
{
- /*
- * 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 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 interrupt flag is set, as we might've undesirably
- * cleared it while writing.
- */
- if (blocked)
+ interruptMask |= INTERRUPT_DIE;
+ if (IsInterruptPending(INTERRUPT_DIE))
{
/*
- * Don't mess with whereToSendOutput if ProcessInterrupts wouldn't
- * service ProcDiePending.
+ * 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 to not clear the interrupt flag set, so that if
+ * the write would block then we'll come back here and die.
*/
- if (InterruptHoldoffCount == 0 && CritSectionCount == 0)
+ if (blocked)
{
/*
* We don't want to send the client the error message, as a)
@@ -576,13 +602,14 @@ ProcessClientWriteInterrupt(bool blocked)
whereToSendOutput = DestNone;
CHECK_FOR_INTERRUPTS();
+ Assert(false); /* CHECK_FOR_INTERRUPTS should've bailed out */
}
}
- else
- RaiseInterrupt(INTERRUPT_GENERAL);
}
errno = save_errno;
+
+ return interruptMask;
}
/*
@@ -2527,29 +2554,29 @@ errdetail_abort(void)
* Add an errdetail() line showing conflict source.
*/
static int
-errdetail_recovery_conflict(ProcSignalReason reason)
+errdetail_recovery_conflict(InterruptType reason)
{
switch (reason)
{
- case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+ case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN:
errdetail("User was holding shared buffer pin for too long.");
break;
- case PROCSIG_RECOVERY_CONFLICT_LOCK:
+ case INTERRUPT_RECOVERY_CONFLICT_LOCK:
errdetail("User was holding a relation lock for too long.");
break;
- case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+ case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE:
errdetail("User was or might have been using tablespace that must be dropped.");
break;
- case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+ case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT:
errdetail("User query might have needed to see row versions that must be removed.");
break;
- case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT:
+ case INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT:
errdetail("User was using a logical replication slot that must be invalidated.");
break;
- case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+ case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
errdetail("User transaction caused buffer deadlock with recovery.");
break;
- case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+ case INTERRUPT_RECOVERY_CONFLICT_DATABASE:
errdetail("User was connected to a database that must be dropped.");
break;
default:
@@ -3004,17 +3031,11 @@ die(SIGNAL_ARGS)
{
/* Don't joggle the elbow of proc_exit */
if (!proc_exit_inprogress)
- {
- InterruptPending = true;
- ProcDiePending = true;
- }
+ RaiseInterrupt(INTERRUPT_DIE);
/* for the cumulative stats system */
pgStatSessionEndCause = DISCONNECT_KILLED;
- /* If we're still here, waken anything waiting on the interrupt */
- RaiseInterrupt(INTERRUPT_GENERAL);
-
/*
* If we're in single user mode, we want to quit immediately - we can't
* rely on interrupts as they wouldn't work when stdin/stdout is a file.
@@ -3032,17 +3053,7 @@ die(SIGNAL_ARGS)
void
StatementCancelHandler(SIGNAL_ARGS)
{
- /*
- * Don't joggle the elbow of proc_exit
- */
- if (!proc_exit_inprogress)
- {
- InterruptPending = true;
- QueryCancelPending = true;
- }
-
- /* If we're still here, waken anything waiting on the interrupt */
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_QUERY_CANCEL);
}
/* signal handler for floating point exception */
@@ -3059,27 +3070,14 @@ FloatExceptionHandler(SIGNAL_ARGS)
}
/*
- * Tell the next CHECK_FOR_INTERRUPTS() to check for a particular type of
- * recovery conflict. Runs in a SIGUSR1 handler.
- */
-void
-HandleRecoveryConflictInterrupt(ProcSignalReason reason)
-{
- RecoveryConflictPendingReasons[reason] = true;
- RecoveryConflictPending = true;
- InterruptPending = true;
- /* INTERRUPT_GENERAL will be raised by procsignal_sigusr1_handler */
-}
-
-/*
- * Check one individual conflict reason.
+ * Process one individual conflict reason.
*/
static void
-ProcessRecoveryConflictInterrupt(ProcSignalReason reason)
+ProcessRecoveryConflictInterrupt(InterruptType reason)
{
switch (reason)
{
- case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+ case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
/*
* If we aren't waiting for a lock we can never deadlock.
@@ -3090,21 +3088,21 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason)
/* Intentional fall through to check wait for pin */
/* FALLTHROUGH */
- case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+ case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN:
/*
- * If PROCSIG_RECOVERY_CONFLICT_BUFFERPIN is requested but we
+ * If INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN is requested but we
* aren't blocking the Startup process there is nothing more to
* do.
*
- * When PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK is requested,
+ * When INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK is requested,
* if we're waiting for locks and the startup process is not
* waiting for buffer pin (i.e., also waiting for locks), we set
* the flag so that ProcSleep() will check for deadlocks.
*/
if (!HoldingBufferPinThatDelaysRecovery())
{
- if (reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK &&
+ if (reason == INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK &&
GetStartupBufferPinWaitBufId() < 0)
CheckDeadLockAlert();
return;
@@ -3115,9 +3113,9 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason)
/* Intentional fall through to error handling */
/* FALLTHROUGH */
- case PROCSIG_RECOVERY_CONFLICT_LOCK:
- case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
- case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+ case INTERRUPT_RECOVERY_CONFLICT_LOCK:
+ case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE:
+ case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT:
/*
* If we aren't in a transaction any longer then ignore.
@@ -3127,14 +3125,14 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason)
/* FALLTHROUGH */
- case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT:
+ case INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT:
/*
* If we're not in a subtransaction then we are OK to throw an
* ERROR to resolve the conflict. Otherwise drop through to the
* FATAL case.
*
- * PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT is a special case that
+ * RECOVERY_CONFLICT_LOGICALSLOT is a special case that
* always throws an ERROR (ie never promotes to FATAL), though it
* still has to respect QueryCancelHoldoffCount, so it shares this
* code path. Logical decoding slots are only acquired while
@@ -3144,17 +3142,17 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason)
* intercept an error before the replication slot is released.
*
* XXX other times that we can throw just an ERROR *may* be
- * PROCSIG_RECOVERY_CONFLICT_LOCK if no locks are held in parent
+ * RECOVERY_CONFLICT_LOCK if no locks are held in parent
* transactions
*
- * PROCSIG_RECOVERY_CONFLICT_SNAPSHOT if no snapshots are held by
+ * RECOVERY_CONFLICT_SNAPSHOT if no snapshots are held by
* parent transactions and the transaction is not
* transaction-snapshot mode
*
- * PROCSIG_RECOVERY_CONFLICT_TABLESPACE if no temp files or
+ * RECOVERY_CONFLICT_TABLESPACE if no temp files or
* cursors open in parent transactions
*/
- if (reason == PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT ||
+ if (reason == INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT ||
!IsSubTransaction())
{
/*
@@ -3178,12 +3176,10 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason)
if (QueryCancelHoldoffCount != 0)
{
/*
- * Re-arm and defer this interrupt until later. See
- * similar code in ProcessInterrupts().
+ * Leave the interrupt set to defer this interrupt
+ * until later. See similar code in
+ * ProcessInterrupts().
*/
- RecoveryConflictPendingReasons[reason] = true;
- RecoveryConflictPending = true;
- InterruptPending = true;
return;
}
@@ -3206,7 +3202,7 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason)
/* Intentional fall through to session cancel */
/* FALLTHROUGH */
- case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+ case INTERRUPT_RECOVERY_CONFLICT_DATABASE:
/*
* Retrying is not possible because the database is dropped, or we
@@ -3215,7 +3211,7 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason)
*/
pgstat_report_recovery_conflict(reason);
ereport(FATAL,
- (errcode(reason == PROCSIG_RECOVERY_CONFLICT_DATABASE ?
+ (errcode(reason == INTERRUPT_RECOVERY_CONFLICT_DATABASE ?
ERRCODE_DATABASE_DROPPED :
ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("terminating connection due to conflict with recovery"),
@@ -3229,47 +3225,17 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason)
}
}
-/*
- * Check each possible recovery conflict reason.
- */
-static void
-ProcessRecoveryConflictInterrupts(void)
-{
- /*
- * We don't need to worry about joggling the elbow of proc_exit, because
- * proc_exit_prepare() holds interrupts, so ProcessInterrupts() won't call
- * us.
- */
- Assert(!proc_exit_inprogress);
- Assert(InterruptHoldoffCount == 0);
- Assert(RecoveryConflictPending);
-
- RecoveryConflictPending = false;
-
- for (ProcSignalReason reason = PROCSIG_RECOVERY_CONFLICT_FIRST;
- reason <= PROCSIG_RECOVERY_CONFLICT_LAST;
- reason++)
- {
- if (RecoveryConflictPendingReasons[reason])
- {
- RecoveryConflictPendingReasons[reason] = false;
- ProcessRecoveryConflictInterrupt(reason);
- }
- }
-}
-
/*
* ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro
*
* If an interrupt condition is pending, and it's safe to service it,
- * then clear the flag and accept the interrupt. Called only when
- * InterruptPending is true.
+ * then clear the flag and accept the interrupt.
*
- * Note: if INTERRUPTS_CAN_BE_PROCESSED() is true, then ProcessInterrupts
- * is guaranteed to clear the InterruptPending flag before returning.
- * (This is not the same as guaranteeing that it's still clear when we
- * return; another interrupt could have arrived. But we promise that
- * any pre-existing one will have been serviced.)
+ * Note: ProcessInterrupts is guaranteed to clear the interrupt bits in
+ * INTERRUPT_CFI_MASK() before returning. (This is not the same as
+ * guaranteeing that it's still clear when we return; another interrupt could
+ * have arrived. But we promise that any pre-existing one will have been
+ * serviced.)
*/
void
ProcessInterrupts(void)
@@ -3277,12 +3243,18 @@ ProcessInterrupts(void)
/* OK to accept any interrupts now? */
if (InterruptHoldoffCount != 0 || CritSectionCount != 0)
return;
- InterruptPending = false;
- if (ProcDiePending)
+ /*
+ * TODO: it'd be good for performance to read the atomic
+ * MyPendingInterrupts variable just once in this function
+ */
+
+ if (ConsumeInterrupt(INTERRUPT_WALSND_INIT_STOPPING))
+ ProcessWalSndInitStopping();
+
+ if (ConsumeInterrupt(INTERRUPT_DIE))
{
- ProcDiePending = false;
- QueryCancelPending = false; /* ProcDie trumps QueryCancel */
+ ClearInterrupt(INTERRUPT_QUERY_CANCEL); /* ProcDie trumps QueryCancel */
LockErrorCleanup();
/* As in quickdie, don't risk sending to client during auth */
if (ClientAuthInProgress && whereToSendOutput == DestRemote)
@@ -3321,10 +3293,8 @@ ProcessInterrupts(void)
errmsg("terminating connection due to administrator command")));
}
- if (CheckClientConnectionPending)
+ if (ConsumeInterrupt(INTERRUPT_CLIENT_CHECK_TIMEOUT))
{
- CheckClientConnectionPending = false;
-
/*
* Check for lost connection and re-arm, if still configured, but not
* if we've arrived back at DoingCommandRead state. We don't want to
@@ -3334,16 +3304,16 @@ ProcessInterrupts(void)
if (!DoingCommandRead && client_connection_check_interval > 0)
{
if (!pq_check_connection())
- ClientConnectionLost = true;
+ RaiseInterrupt(INTERRUPT_CLIENT_CONNECTION_LOST);
else
enable_timeout_after(CLIENT_CONNECTION_CHECK_TIMEOUT,
client_connection_check_interval);
}
}
- if (ClientConnectionLost)
+ if (IsInterruptPending(INTERRUPT_CLIENT_CONNECTION_LOST))
{
- QueryCancelPending = false; /* lost connection trumps QueryCancel */
+ ClearInterrupt(INTERRUPT_QUERY_CANCEL); /* lost connection trumps QueryCancel */
LockErrorCleanup();
/* don't send to client, we already know the connection to be dead. */
whereToSendOutput = DestNone;
@@ -3360,25 +3330,11 @@ ProcessInterrupts(void)
*
* See similar logic in ProcessRecoveryConflictInterrupts().
*/
- if (QueryCancelPending && QueryCancelHoldoffCount != 0)
- {
- /*
- * Re-arm InterruptPending so that we process the cancel request as
- * soon as we're done reading the message. (XXX this is seriously
- * ugly: it complicates INTERRUPTS_CAN_BE_PROCESSED(), and it means we
- * can't use that macro directly as the initial test in this function,
- * meaning that this code also creates opportunities for other bugs to
- * appear.)
- */
- InterruptPending = true;
- }
- else if (QueryCancelPending)
+ if (QueryCancelHoldoffCount == 0 && ConsumeInterrupt(INTERRUPT_QUERY_CANCEL))
{
bool lock_timeout_occurred;
bool stmt_timeout_occurred;
- QueryCancelPending = false;
-
/*
* If LOCK_TIMEOUT and STATEMENT_TIMEOUT indicators are both set, we
* need to clear both, so always fetch both.
@@ -3432,10 +3388,28 @@ ProcessInterrupts(void)
}
}
- if (RecoveryConflictPending)
- ProcessRecoveryConflictInterrupts();
+ if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_DATABASE))
+ ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_DATABASE);
+
+ if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_TABLESPACE))
+ ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_TABLESPACE);
+
+ if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_LOCK))
+ ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_LOCK);
- if (IdleInTransactionSessionTimeoutPending)
+ if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT))
+ ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT);
+
+ if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT))
+ ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT);
+
+ if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN))
+ ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN);
+
+ if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK))
+ ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+
+ if (ConsumeInterrupt(INTERRUPT_IDLE_IN_TRANSACTION_SESSION_TIMEOUT))
{
/*
* If the GUC has been reset to zero, ignore the signal. This is
@@ -3443,7 +3417,6 @@ ProcessInterrupts(void)
* interrupt. We need to unset the flag before the injection point,
* otherwise we could loop in interrupts checking.
*/
- IdleInTransactionSessionTimeoutPending = false;
if (IdleInTransactionSessionTimeout > 0)
{
INJECTION_POINT("idle-in-transaction-session-timeout");
@@ -3453,10 +3426,9 @@ ProcessInterrupts(void)
}
}
- if (TransactionTimeoutPending)
+ if (ConsumeInterrupt(INTERRUPT_TRANSACTION_TIMEOUT))
{
/* As above, ignore the signal if the GUC has been reset to zero. */
- TransactionTimeoutPending = false;
if (TransactionTimeout > 0)
{
INJECTION_POINT("transaction-timeout");
@@ -3466,10 +3438,9 @@ ProcessInterrupts(void)
}
}
- if (IdleSessionTimeoutPending)
+ if (ConsumeInterrupt(INTERRUPT_IDLE_SESSION_TIMEOUT))
{
/* As above, ignore the signal if the GUC has been reset to zero. */
- IdleSessionTimeoutPending = false;
if (IdleSessionTimeout > 0)
{
INJECTION_POINT("idle-session-timeout");
@@ -3479,28 +3450,17 @@ ProcessInterrupts(void)
}
}
- /*
- * If there are pending stats updates and we currently are truly idle
- * (matching the conditions in PostgresMain(), report stats now.
- */
- if (IdleStatsUpdateTimeoutPending &&
- DoingCommandRead && !IsTransactionOrTransactionBlock())
- {
- IdleStatsUpdateTimeoutPending = false;
- pgstat_report_stat(true);
- }
-
- if (ProcSignalBarrierPending)
+ if (IsInterruptPending(INTERRUPT_BARRIER))
ProcessProcSignalBarrier();
- if (ParallelMessagePending)
+ if (ConsumeInterrupt(INTERRUPT_PARALLEL_MESSAGE))
ProcessParallelMessages();
- if (LogMemoryContextPending)
- ProcessLogMemoryContextInterrupt();
-
- if (ParallelApplyMessagePending)
+ if (ConsumeInterrupt(INTERRUPT_PARALLEL_APPLY_MESSAGE))
ProcessParallelApplyMessages();
+
+ if (ConsumeInterrupt(INTERRUPT_LOG_MEMORY_CONTEXT))
+ ProcessLogMemoryContextInterrupt();
}
/*
@@ -4203,7 +4163,7 @@ PostgresMain(const char *dbname, const char *username)
* midst of output during who-knows-what operation...
*/
pqsignal(SIGPIPE, SIG_IGN);
- pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+ pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN);
pqsignal(SIGFPE, FloatExceptionHandler);
@@ -4377,7 +4337,7 @@ PostgresMain(const char *dbname, const char *username)
* forgetting a timeout cancel.
*/
disable_all_timeouts(false); /* do first to avoid race condition */
- QueryCancelPending = false;
+ ClearInterrupt(INTERRUPT_QUERY_CANCEL);
idle_in_transaction_timeout_enabled = false;
idle_session_timeout_enabled = false;
@@ -4562,7 +4522,7 @@ PostgresMain(const char *dbname, const char *username)
* were received during the just-finished transaction, they'll
* be seen by the client before ReadyForQuery is.
*/
- if (notifyInterruptPending)
+ if (IsInterruptPending(INTERRUPT_ASYNC_NOTIFY))
ProcessNotifyInterrupt(false);
/*
@@ -4648,7 +4608,7 @@ PostgresMain(const char *dbname, const char *username)
*
* Query cancel is supposed to be a no-op when there is no query in
* progress, so if a query cancel arrived while we were idle, just
- * reset QueryCancelPending. ProcessInterrupts() has that effect when
+ * reset INTERRUPT_QUERY_CANCEL. ProcessInterrupts() has that effect when
* it's called when DoingCommandRead is set, so check for interrupts
* before resetting DoingCommandRead.
*/
@@ -4659,11 +4619,8 @@ PostgresMain(const char *dbname, const char *username)
* (6) check for any other interesting events that happened while we
* slept.
*/
- if (ConfigReloadPending)
- {
- ConfigReloadPending = false;
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
ProcessConfigFile(PGC_SIGHUP);
- }
/*
* (7) process the command. But ignore it if we're skipping till
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 05a8ccfdb75..762ded643b9 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -17,7 +17,7 @@
#include "postgres.h"
-#include "storage/procsignal.h"
+#include "postmaster/interrupt.h"
#include "utils/pgstat_internal.h"
#include "utils/timestamp.h"
@@ -78,7 +78,7 @@ pgstat_report_autovac(Oid dboid)
* Report a Hot Standby recovery conflict.
*/
void
-pgstat_report_recovery_conflict(int reason)
+pgstat_report_recovery_conflict(InterruptType reason)
{
PgStat_StatDBEntry *dbentry;
@@ -90,31 +90,33 @@ pgstat_report_recovery_conflict(int reason)
switch (reason)
{
- case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+ case INTERRUPT_RECOVERY_CONFLICT_DATABASE:
/*
* Since we drop the information about the database as soon as it
* replicates, there is no point in counting these conflicts.
*/
break;
- case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+ case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE:
dbentry->conflict_tablespace++;
break;
- case PROCSIG_RECOVERY_CONFLICT_LOCK:
+ case INTERRUPT_RECOVERY_CONFLICT_LOCK:
dbentry->conflict_lock++;
break;
- case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+ case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT:
dbentry->conflict_snapshot++;
break;
- case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+ case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN:
dbentry->conflict_bufferpin++;
break;
- case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT:
+ case INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT:
dbentry->conflict_logicalslot++;
break;
- case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+ case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
dbentry->conflict_startup_deadlock++;
break;
+ default:
+ elog(LOG, "unexpected recovery conflict reason %u", (unsigned int) reason);
}
}
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 396c2f223b4..a08fcffbc0f 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -265,7 +265,6 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
{
int pid = PG_GETARG_INT32(0);
PGPROC *proc;
- ProcNumber procNumber = INVALID_PROC_NUMBER;
/*
* See if the process with given pid is a backend or an auxiliary process.
@@ -294,14 +293,13 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(false);
}
- procNumber = GetNumberFromPGProc(proc);
- if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, procNumber) < 0)
- {
- /* Again, just a warning to allow loops */
- ereport(WARNING,
- (errmsg("could not send signal to process %d: %m", pid)));
- PG_RETURN_BOOL(false);
- }
+ /*
+ * XXX: We're not holding any locks here, so the PGPROC could get recycled
+ * for another backend. If that happens, we'll print the memory dump of
+ * the newly started process instead, which is confusing but otherwise
+ * harmless.
+ */
+ SendInterrupt(INTERRUPT_LOG_MEMORY_CONTEXT, GetNumberFromPGProc(proc));
PG_RETURN_BOOL(true);
}
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index d7e9da57bd1..6b410fbbc9f 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -403,11 +403,10 @@ pg_sleep(PG_FUNCTION_ARGS)
else
break;
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK(),
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
delay_ms,
WAIT_EVENT_PG_SLEEP);
- ClearInterrupt(INTERRUPT_GENERAL);
}
PG_RETURN_VOID();
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 79bc999cf26..100a9e119f1 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -28,21 +28,6 @@
ProtocolVersion FrontendProtocol;
-volatile sig_atomic_t InterruptPending = false;
-volatile sig_atomic_t QueryCancelPending = false;
-volatile sig_atomic_t ProcDiePending = false;
-volatile sig_atomic_t CheckClientConnectionPending = false;
-volatile sig_atomic_t ClientConnectionLost = false;
-volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
-volatile sig_atomic_t TransactionTimeoutPending = false;
-volatile sig_atomic_t IdleSessionTimeoutPending = false;
-volatile sig_atomic_t ProcSignalBarrierPending = false;
-volatile sig_atomic_t LogMemoryContextPending = false;
-volatile sig_atomic_t IdleStatsUpdateTimeoutPending = false;
-volatile uint32 InterruptHoldoffCount = 0;
-volatile uint32 QueryCancelHoldoffCount = 0;
-volatile uint32 CritSectionCount = 0;
-
int MyProcPid;
pg_time_t MyStartTime;
TimestampTz MyStartTimestamp;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index e11101a36b0..5f539157fea 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1382,41 +1382,31 @@ LockTimeoutHandler(void)
static void
TransactionTimeoutHandler(void)
{
- TransactionTimeoutPending = true;
- InterruptPending = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_TRANSACTION_TIMEOUT);
}
static void
IdleInTransactionSessionTimeoutHandler(void)
{
- IdleInTransactionSessionTimeoutPending = true;
- InterruptPending = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_IDLE_IN_TRANSACTION_SESSION_TIMEOUT);
}
static void
IdleSessionTimeoutHandler(void)
{
- IdleSessionTimeoutPending = true;
- InterruptPending = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_IDLE_SESSION_TIMEOUT);
}
static void
IdleStatsUpdateTimeoutHandler(void)
{
- IdleStatsUpdateTimeoutPending = true;
- InterruptPending = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_IDLE_STATS_TIMEOUT);
}
static void
ClientCheckTimeoutHandler(void)
{
- CheckClientConnectionPending = true;
- InterruptPending = true;
- RaiseInterrupt(INTERRUPT_GENERAL);
+ RaiseInterrupt(INTERRUPT_CLIENT_CHECK_TIMEOUT);
}
/*
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index e90d82c7ddc..b63613118b7 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -370,12 +370,6 @@ handle_sig_alarm(SIGNAL_ARGS)
*/
HOLD_INTERRUPTS();
- /*
- * SIGALRM is always cause for waking anything waiting on
- * INTERRUPT_GENERAL.
- */
- RaiseInterrupt(INTERRUPT_GENERAL);
-
/*
* Always reset signal_pending, even if !alarm_enabled, since indeed no
* signal is now pending.
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 5233a65aaa0..63cee06a12a 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -1260,27 +1260,11 @@ MemoryContextAllocExtended(MemoryContext context, Size size, int flags)
return ret;
}
-/*
- * HandleLogMemoryContextInterrupt
- * Handle receipt of an interrupt indicating logging of memory
- * contexts.
- *
- * All the actual work is deferred to ProcessLogMemoryContextInterrupt(),
- * because we cannot safely emit a log message inside the signal handler.
- */
-void
-HandleLogMemoryContextInterrupt(void)
-{
- InterruptPending = true;
- LogMemoryContextPending = true;
- /* INTERRUPT_GENERAL will be raised by procsignal_sigusr1_handler */
-}
-
/*
* ProcessLogMemoryContextInterrupt
* Perform logging of memory contexts of this backend process.
*
- * Any backend that participates in ProcSignal signaling must arrange
+ * Any backend that participates in standard interrupt handling must arrange
* to call this function if we see LogMemoryContextPending set.
* It is called from CHECK_FOR_INTERRUPTS(), which is enough because
* the target process for logging of memory contexts is a backend.
@@ -1288,7 +1272,7 @@ HandleLogMemoryContextInterrupt(void)
void
ProcessLogMemoryContextInterrupt(void)
{
- LogMemoryContextPending = false;
+ ClearInterrupt(INTERRUPT_LOG_MEMORY_CONTEXT);
/*
* Use LOG_SERVER_ONLY to prevent this message from being sent to the
diff --git a/src/include/access/parallel.h b/src/include/access/parallel.h
index a257d0a8ee6..b7bdada3646 100644
--- a/src/include/access/parallel.h
+++ b/src/include/access/parallel.h
@@ -14,8 +14,6 @@
#ifndef PARALLEL_H
#define PARALLEL_H
-#include <signal.h>
-
#include "access/xlogdefs.h"
#include "lib/ilist.h"
#include "postmaster/bgworker.h"
@@ -55,7 +53,6 @@ typedef struct ParallelWorkerContext
shm_toc *toc;
} ParallelWorkerContext;
-extern PGDLLIMPORT volatile sig_atomic_t ParallelMessagePending;
extern PGDLLIMPORT int ParallelWorkerNumber;
extern PGDLLIMPORT bool InitializingParallelWorker;
@@ -72,7 +69,6 @@ extern void WaitForParallelWorkersToFinish(ParallelContext *pcxt);
extern void DestroyParallelContext(ParallelContext *pcxt);
extern bool ParallelContextActive(void);
-extern void HandleParallelMessageInterrupt(void);
extern void ProcessParallelMessages(void);
extern void AtEOXact_Parallel(bool isCommit);
extern void AtEOSubXact_Parallel(bool isCommit, SubTransactionId mySubId);
diff --git a/src/include/commands/async.h b/src/include/commands/async.h
index f75c3df9556..50773ddb0e2 100644
--- a/src/include/commands/async.h
+++ b/src/include/commands/async.h
@@ -13,11 +13,8 @@
#ifndef ASYNC_H
#define ASYNC_H
-#include <signal.h>
-
extern PGDLLIMPORT bool Trace_notify;
extern PGDLLIMPORT int max_notify_queue_pages;
-extern PGDLLIMPORT volatile sig_atomic_t notifyInterruptPending;
extern Size AsyncShmemSize(void);
extern void AsyncShmemInit(void);
@@ -40,9 +37,6 @@ extern void AtSubCommit_Notify(void);
extern void AtSubAbort_Notify(void);
extern void AtPrepare_Notify(void);
-/* signal handler for inbound notifies (PROCSIG_NOTIFY_INTERRUPT) */
-extern void HandleNotifyInterrupt(void);
-
/* process interrupts */
extern void ProcessNotifyInterrupt(bool flush);
diff --git a/src/include/libpq/libpq-be-fe-helpers.h b/src/include/libpq/libpq-be-fe-helpers.h
index dd361be4afd..310c45f689c 100644
--- a/src/include/libpq/libpq-be-fe-helpers.h
+++ b/src/include/libpq/libpq-be-fe-helpers.h
@@ -209,7 +209,7 @@ libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
else
io_flag = WL_SOCKET_WRITEABLE;
- rc = WaitInterruptOrSocket(INTERRUPT_GENERAL,
+ rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK(),
WL_EXIT_ON_PM_DEATH | WL_INTERRUPT | io_flag,
PQsocket(conn),
0,
@@ -217,10 +217,7 @@ libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
/* Interrupted? */
if (rc & WL_INTERRUPT)
- {
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
- }
/* If socket is ready, advance the libpq state machine */
if (rc & io_flag)
@@ -341,7 +338,7 @@ libpqsrv_get_result(PGconn *conn, uint32 wait_event_info)
{
int rc;
- rc = WaitInterruptOrSocket(INTERRUPT_GENERAL,
+ rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK(),
WL_EXIT_ON_PM_DEATH | WL_INTERRUPT |
WL_SOCKET_READABLE,
PQsocket(conn),
@@ -350,10 +347,7 @@ libpqsrv_get_result(PGconn *conn, uint32 wait_event_info)
/* Interrupted? */
if (rc & WL_INTERRUPT)
- {
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
- }
/* Consume whatever data is available from the socket */
if (PQconsumeInput(conn) == 0)
@@ -436,12 +430,10 @@ libpqsrv_cancel(PGconn *conn, TimestampTz endtime)
}
/* Sleep until there's something to do */
- WaitInterruptOrSocket(INTERRUPT_GENERAL, waitEvents,
+ WaitInterruptOrSocket(INTERRUPT_CFI_MASK(), waitEvents,
PQcancelSocket(cancel_conn),
cur_timeout, PG_WAIT_CLIENT);
- ClearInterrupt(INTERRUPT_GENERAL);
-
CHECK_FOR_INTERRUPTS();
}
exit: ;
diff --git a/src/include/libpq/pqmq.h b/src/include/libpq/pqmq.h
index 348b4494333..ae02aacfaa1 100644
--- a/src/include/libpq/pqmq.h
+++ b/src/include/libpq/pqmq.h
@@ -17,7 +17,7 @@
#include "storage/shm_mq.h"
extern void pq_redirect_to_shm_mq(dsm_segment *seg, shm_mq_handle *mqh);
-extern void pq_set_parallel_leader(pid_t pid, ProcNumber procNumber);
+extern void pq_set_parallel_leader(ProcNumber procNumber);
extern void pq_parse_errornotice(StringInfo msg, ErrorData *edata);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f5b773b79e5..08de6146800 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -28,124 +28,28 @@
#include "datatype/timestamp.h" /* for TimestampTz */
#include "pgtime.h" /* for pg_time_t */
+/*
+ * for backwards-compatibility: CHECK_FOR_INTERRUPTS() used to be here, and it's
+ * used all over the place
+ */
+#ifndef FRONTEND
+#include "postmaster/interrupt.h"
+#endif
#define InvalidPid (-1)
/*****************************************************************************
- * System interrupt and critical section handling
- *
- * There are two types of interrupts that a running backend needs to accept
- * without messing up its state: QueryCancel (SIGINT) and ProcDie (SIGTERM).
- * In both cases, we need to be able to clean up the current transaction
- * gracefully, so we can't respond to the interrupt instantaneously ---
- * there's no guarantee that internal data structures would be self-consistent
- * if the code is interrupted at an arbitrary instant. Instead, the signal
- * handlers set flags that are checked periodically during execution.
- *
- * The CHECK_FOR_INTERRUPTS() macro is called at strategically located spots
- * where it is normally safe to accept a cancel or die interrupt. In some
- * cases, we invoke CHECK_FOR_INTERRUPTS() inside low-level subroutines that
- * might sometimes be called in contexts that do *not* want to allow a cancel
- * or die interrupt. The HOLD_INTERRUPTS() and RESUME_INTERRUPTS() macros
- * allow code to ensure that no cancel or die interrupt will be accepted,
- * even if CHECK_FOR_INTERRUPTS() gets called in a subroutine. The interrupt
- * will be held off until CHECK_FOR_INTERRUPTS() is done outside any
- * HOLD_INTERRUPTS() ... RESUME_INTERRUPTS() section.
- *
- * There is also a mechanism to prevent query cancel interrupts, while still
- * allowing die interrupts: HOLD_CANCEL_INTERRUPTS() and
- * RESUME_CANCEL_INTERRUPTS().
+ * Critical section handling
*
- * Note that ProcessInterrupts() has also acquired a number of tasks that
- * do not necessarily cause a query-cancel-or-die response. Hence, it's
- * possible that it will just clear InterruptPending and return.
- *
- * INTERRUPTS_PENDING_CONDITION() can be checked to see whether an
- * interrupt needs to be serviced, without trying to do so immediately.
- * Some callers are also interested in INTERRUPTS_CAN_BE_PROCESSED(),
- * which tells whether ProcessInterrupts is sure to clear the interrupt.
- *
- * Special mechanisms are used to let an interrupt be accepted when we are
- * waiting for a lock or when we are waiting for command input (but, of
- * course, only if the interrupt holdoff counter is zero). See the
- * related code for details.
- *
- * A lost connection is handled similarly, although the loss of connection
- * does not raise a signal, but is detected when we fail to write to the
- * socket. If there was a signal for a broken connection, we could make use of
- * it by setting ClientConnectionLost in the signal handler.
- *
- * A related, but conceptually distinct, mechanism is the "critical section"
- * mechanism. A critical section not only holds off cancel/die interrupts,
- * but causes any ereport(ERROR) or ereport(FATAL) to become ereport(PANIC)
- * --- that is, a system-wide reset is forced. Needless to say, only really
- * *critical* code should be marked as a critical section! Currently, this
- * mechanism is only used for XLOG-related code.
+ * A critical section holds off cancel/die interrupts like HOLD_INTERRUPTS()
+ * does, and also causes any ereport(ERROR) or ereport(FATAL) to become
+ * ereport(PANIC) --- that is, a system-wide reset is forced. Needless to
+ * say, only really *critical* code should be marked as a critical section!
+ * Currently, this mechanism is only used for XLOG-related code.
*
*****************************************************************************/
-/* in globals.c */
-/* these are marked volatile because they are set by signal handlers: */
-extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
-extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
-extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
-extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
-extern PGDLLIMPORT volatile sig_atomic_t TransactionTimeoutPending;
-extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
-extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
-extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
-extern PGDLLIMPORT volatile sig_atomic_t IdleStatsUpdateTimeoutPending;
-
-extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
-extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
-
-/* these are marked volatile because they are examined by signal handlers: */
-extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
-extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
-extern PGDLLIMPORT volatile uint32 CritSectionCount;
-
-/* in tcop/postgres.c */
-extern void ProcessInterrupts(void);
-
-/* Test whether an interrupt is pending */
-#ifndef WIN32
-#define INTERRUPTS_PENDING_CONDITION() \
- (unlikely(InterruptPending))
-#else
-#define INTERRUPTS_PENDING_CONDITION() \
- (unlikely(UNBLOCKED_SIGNAL_QUEUE()) ? pgwin32_dispatch_queued_signals() : 0, \
- unlikely(InterruptPending))
-#endif
-
-/* Service interrupt, if one is pending and it's safe to service it now */
-#define CHECK_FOR_INTERRUPTS() \
-do { \
- if (INTERRUPTS_PENDING_CONDITION()) \
- ProcessInterrupts(); \
-} while(0)
-
-/* Is ProcessInterrupts() guaranteed to clear InterruptPending? */
-#define INTERRUPTS_CAN_BE_PROCESSED() \
- (InterruptHoldoffCount == 0 && CritSectionCount == 0 && \
- QueryCancelHoldoffCount == 0)
-
-#define HOLD_INTERRUPTS() (InterruptHoldoffCount++)
-
-#define RESUME_INTERRUPTS() \
-do { \
- Assert(InterruptHoldoffCount > 0); \
- InterruptHoldoffCount--; \
-} while(0)
-
-#define HOLD_CANCEL_INTERRUPTS() (QueryCancelHoldoffCount++)
-
-#define RESUME_CANCEL_INTERRUPTS() \
-do { \
- Assert(QueryCancelHoldoffCount > 0); \
- QueryCancelHoldoffCount--; \
-} while(0)
-
#define START_CRIT_SECTION() (CritSectionCount++)
#define END_CRIT_SECTION() \
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 4aad10b0b6d..be523d1ab3e 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -605,7 +605,7 @@ extern bool pgstat_tracks_io_op(BackendType bktype, IOObject io_object,
extern void pgstat_drop_database(Oid databaseid);
extern void pgstat_report_autovac(Oid dboid);
-extern void pgstat_report_recovery_conflict(int reason);
+extern void pgstat_report_recovery_conflict(InterruptType reason);
extern void pgstat_report_deadlock(void);
extern void pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount);
extern void pgstat_report_checksum_failure(void);
diff --git a/src/include/postmaster/interrupt.h b/src/include/postmaster/interrupt.h
index 09dc6f0330d..5dcb35cf896 100644
--- a/src/include/postmaster/interrupt.h
+++ b/src/include/postmaster/interrupt.h
@@ -10,42 +10,110 @@
*
* 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.
+ * handlers, or "sent" by other backends setting them directly.
*
- * Most code currently deals with the INTERRUPT_GENERAL 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.
+ * Standard interrupts
+ * -------------------
*
- * 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().
+ * Some interrupts need to be processed fairly quickly even when the backend
+ * is busy, like QueryCancel (SIGINT) and ProcDie (SIGTERM), but that requires
+ * cleaning up the current transaction gracefully, and there's no guarantee
+ * that internal data structures would be self-consistent if the code is
+ * interrupted at an arbitrary instant.
*
+ * The CHECK_FOR_INTERRUPTS() macro is called at strategically located spots
+ * where it is normally safe to accept a cancel or die interrupt.
+ * 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 correct pattern to wait for event(s) using INTERRUPT_GENERAL is:
+ * When you need to wait, pass INTERRUPT_CFI_MASK() to WaitInterrupt() and
+ * call CHECK_FOR_INTERRUPTS() every time WaitInterrupt() returns.
+ *
+ * In some cases, we invoke CHECK_FOR_INTERRUPTS() inside low-level
+ * subroutines that might sometimes be called in contexts that do *not* want
+ * to allow a cancel or die interrupt. The HOLD_INTERRUPTS() and
+ * RESUME_INTERRUPTS() macros allow code to ensure that no cancel or die
+ * interrupt will be accepted, even if CHECK_FOR_INTERRUPTS() gets called in a
+ * subroutine. The interrupt will be held off until CHECK_FOR_INTERRUPTS() is
+ * done outside any HOLD_INTERRUPTS() ... RESUME_INTERRUPTS() section. There
+ * is also a mechanism to prevent query cancel interrupts, while still
+ * allowing die interrupts: HOLD_CANCEL_INTERRUPTS() and
+ * RESUME_CANCEL_INTERRUPTS().
+ *
+ * Note that ProcessInterrupts() has also acquired a number of tasks that do
+ * not necessarily cause a query-cancel-or-die response. Hence, it's possible
+ * that it will just clear some interrupt bits and return.
+ *
+ * INTERRUPTS_PENDING_CONDITION() can be checked to see whether an
+ * interrupt needs to be serviced, without trying to do so immediately.
+ * Some callers are also interested in INTERRUPTS_CAN_BE_PROCESSED(),
+ * which tells whether ProcessInterrupts is sure to clear the interrupt.
+ *
+ * Special mechanisms are used to let an interrupt be accepted when we are
+ * waiting for a lock or when we are waiting for command input (but, of
+ * course, only if the interrupt holdoff counter is zero). See the related
+ * code for details.
+ *
+ * A lost connection is handled similarly to a ProcDie request, although the
+ * loss of connection is detected and the interrupt is raied when we fail to
+ * write to the socket. If there was a signal for a broken connection, we
+ * could make use of it by setting ClientConnectionLost in the signal handler.
+ *
+ * Standard interrupts in AUX processes
+ * ------------------------------------
+ *
+ * In background processes that are not regular backends, responses to signals
+ * that are translated to interrupts are fairly varied and many types of
+ * backends have their own implementations. Some use CHECK_FOR_INTERRUPTS()
+ * but have additional interrupts that are also processed in the main
+ * loop. Others don't use CHECK_FOR_INTERRUPTS() at all, but have their own
+ * Process*Interrupts() function that is called at strategic spots.
+ *
+ * We nevertheless provide a few generic signal handlers and interrupt checks
+ * to facilitate code reuse, see ProcessMainLoopInterrupts() and the standard
+ * signal handlers SignalHandlerForConfigReload(), SignalHandlerForCrashExit(),
+ * and SignalHandlerForShutdownRequest().
+ *
+ * INTERRUPT_GENERAL: The multiplexed general-purpose wakeup
+ * ---------------------------------------------------------
+ *
+ * The INTERRUPT_GENERAL interrupt is multiplexed for many different purposes
+ * that don't warrant a dedicated interrupt bit. Because it's reused for
+ * different purposes, waiters must tolerate receiving spurious interrupt
+ * wakeups.
+ *
+ * Waiting on an interrupt
+ * -----------------------
+ *
+ * The correct pattern to wait for event(s) using INTERRUPT_GENERAL (or any
+ * bespoken interrupt flag) is:
*
* for (;;)
* {
+ * CHECK_FOR_INTERRUPTS();
+ *
* ClearInterrupt(INTERRUPT_GENERAL);
* if (work to do)
* Do Stuff();
- * WaitInterrupt(INTERRUPT_GENERAL, ...);
+ * WaitInterrupt(INTERRUPT_CFI_MASAK() | INTERRUPT_GENERAL, ...);
* }
*
* 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
+ * 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 (;;)
* {
+ * CHECK_FOR_INTERRUPTS();
+ *
* if (work to do)
* Do Stuff(); // in particular, exit loop if some condition satisfied
- * WaitInterrupt(INTERRUPT_GENERAL, ...);
+ * WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL, ...);
* ClearInterrupt(INTERRUPT_GENERAL);
* }
*
@@ -55,12 +123,23 @@
* 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) *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.
+ * To wake up a process waiting on INTERRUPT_GENERAL, 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) *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.
+ *
+ * Standard Signal handlers
+ * ------------------------
+ *
+ * Responses to signals that are translated to interrupts are fairly varied
+ * and many types of backends have their own implementations, but we provide a
+ * few generic signal handlers and interrupt checks to facilitate code reuse.
+ *
*
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -76,73 +155,272 @@
#include "port/atomics.h"
#include "storage/procnumber.h"
-#include "storage/waiteventset.h"
-
-#include <signal.h>
+#include "storage/waiteventset.h" /* WL_* are defined in waiteventset.h */
extern PGDLLIMPORT pg_atomic_uint32 *MyPendingInterrupts;
-extern PGDLLIMPORT volatile sig_atomic_t ConfigReloadPending;
-extern PGDLLIMPORT volatile sig_atomic_t ShutdownRequestPending;
+/* these are marked volatile because they are examined by signal handlers: */
+/* FIXME: is that still true, do these still need to be volatile? */
+extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
+extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
+extern PGDLLIMPORT volatile uint32 CritSectionCount;
+
+/*
+ * If you called ProcessInterrupts() now, it would process and clear these
+ * interrupts.
+ */
+#define INTERRUPT_CFI_MASK() \
+ ( \
+ (InterruptHoldoffCount > 0 || CritSectionCount > 0) ? 0 : \
+ ((QueryCancelHoldoffCount > 0) ? (INTERRUPT_CFI_ALL_MASK & (~INTERRUPT_QUERY_CANCEL)) : \
+ INTERRUPT_CFI_ALL_MASK) \
+ )
+
+/*
+ * Service standard interrupts, if one is pending and it's safe to service it now.
+ *
+ * This could check INTERRUPT_CFI_MASK(), but we prefer to keep his short and
+ * fast in the common case that no interrupts are pending. That's why this
+ * checks the constant INTERRUPT_CFI_ALL_MASK instead and check the holdoffs
+ * are out-of-line in ProcessInterrupts().
+ */
+#define CHECK_FOR_INTERRUPTS() \
+do { \
+ if (IsInterruptPending(INTERRUPT_CFI_ALL_MASK)) \
+ ProcessInterrupts(); \
+} while(0)
+
+/* Would ProcessInterrupts() clear all the bits in 'mask'? */
+#define INTERRUPTS_CAN_BE_PROCESSED(mask) \
+ (((mask) & ~INTERRUPT_CFI_MASK()) == 0)
+
+#define HOLD_INTERRUPTS() \
+do { \
+ InterruptHoldoffCount++; \
+} while(0)
+
+#define RESUME_INTERRUPTS() \
+do { \
+ Assert(InterruptHoldoffCount > 0); \
+ InterruptHoldoffCount--; \
+} while(0)
+
+#define HOLD_CANCEL_INTERRUPTS() (QueryCancelHoldoffCount++)
+
+#define RESUME_CANCEL_INTERRUPTS() \
+do { \
+ Assert(QueryCancelHoldoffCount > 0); \
+ QueryCancelHoldoffCount--; \
+} while(0)
+
/*
* Flags in the pending interrupts bitmask. Each value represents one bit in
* the bitmask.
*/
-typedef enum
+typedef enum InterruptType
{
/*
- * SLEEPING_ON_INTERRUPTS indicates that the backend is currently blocked
- * waiting for an interrupt. If it's set, the backend needs to be woken up
- * when a bit in the pending interrupts mask is set. It's used internally
- * by the interrupt machinery, and cannot be used directly in the public
- * functions. It's named differently to distinguish it from the actual
- * interrupt flags.
+ * INTERRUPT_GENERAL is used as a general-purpose wakeup, multiplexed for
+ * many reasons.
*/
- SLEEPING_ON_INTERRUPTS = 1 << 0,
+ INTERRUPT_GENERAL = 1 << 0,
/*
- * INTERRUPT_GENERAL is multiplexed for many reasons, like query
- * cancellation termination requests, recovery conflicts, and config
- * reload requests. Upon receiving INTERRUPT_GENERAL, 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.
+ * Because backends sitting idle will not be reading sinval events, we
+ * need a way to give an idle backend a swift kick in the rear and make it
+ * catch up before the sinval queue overflows and forces it to go through
+ * a cache reset exercise. This is done by sending
+ * INTERRUPT_SINVAL_CATCHUP to any backend that gets too far behind.
+ *
+ * The interrupt is processed whenever starting to read from the client,
+ * or when interrupted while doing so, ProcessClientReadInterrupt() will
+ * call ProcessCatchupInterrupt().
*/
- INTERRUPT_GENERAL = 1 << 1,
+ INTERRUPT_SINVAL_CATCHUP = 1 << 1,
/*
- * INTERRUPT_RECOVERY_CONTINUE 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_ASYNC_NOTIFY is sent to notify backends that have registered
+ * to LISTEN on any channels that they might have messages they need to
+ * deliver to the frontent. It is also processed whenever starting to read
+ * from the client or while doing so, but only when there is no
+ * transaction in progress.
*/
- INTERRUPT_RECOVERY_CONTINUE = 1 << 2,
+ INTERRUPT_ASYNC_NOTIFY = 1 << 2,
+
+ /* Raised by timer while idle, to send a stats update */
+ INTERRUPT_IDLE_STATS_TIMEOUT = 1 << 3,
+
+ /* Config file reload is requested */
+ INTERRUPT_CONFIG_RELOAD = 1 << 4,
+
+ /*
+ * INTERRUPT_RECOVERY_CONTINUE is used to wake up the 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. We don't reuse
+ * INTERRUPT_GENERAL for this, so that more WAL arriving doesn't wake up
+ * the startup process excessively when we're waiting in other places,
+ * like for recovery conflicts.
+ */
+ INTERRUPT_RECOVERY_CONTINUE = 1 << 5,
/* sent to logical replication launcher, when a subscription changes */
- INTERRUPT_SUBSCRIPTION_CHANGE = 1 << 3,
+ INTERRUPT_SUBSCRIPTION_CHANGE = 1 << 6,
+
+ /*
+ * Many aux processes don't want to react to INTERRUPT_DIE in
+ * CHECK_FOR_INTERRUPTS(), so they use a separate flag when shutdown is
+ * requested.
+ *
+ * TODO: perhaps use INTERRUPT_DIE, but teach CHECK_FOR_INTERRUPTS() to
+ * ignore it in aux processes, and remove it from CheckForInterruptsMask.
+ * That would save one interrupt bit, and would make things more
+ * consistent.
+ */
+ INTERRUPT_SHUTDOWN_AUX = 1 << 7,
+
+ /*
+ * Perform one last checkpoint, then shut down. Only used in the checkpointer
+ * process.
+ */
+ INTERRUPT_SHUTDOWN_XLOG = 1 << 8,
+
+ /*---- Interrupts handled by CHECK_FOR_INTERRUPTS() ----*/
+
+ /*
+ * Backend has been requested to terminate
+ *
+ * This is raised by the SIGTERM signal handler, or can be sent directly
+ * by another backend e.g. with pg_terminate_backend().
+ */
+ INTERRUPT_DIE = 1 << 9,
+
+ /*
+ * Cancel current query, if any.
+ *
+ * This is raised by the SIGTERM signal handler, or can be sent directly
+ * by another backend e.g. with pg_cancel_backend(), or in response to a
+ * query cancellation packet.
+ */
+ INTERRUPT_QUERY_CANCEL = 1 << 10,
+
+ /* ask walsenders to prepare for shutdown */
+ INTERRUPT_WALSND_INIT_STOPPING = 1 << 11,
+
+ /*
+ * Recovery conflict reasons. These are sent by the startup process in hot
+ * standby mode when a backend holds back the WAL replay for too long.
+ */
+ INTERRUPT_RECOVERY_CONFLICT_DATABASE = 1 << 12,
+ INTERRUPT_RECOVERY_CONFLICT_TABLESPACE = 1 << 13,
+ INTERRUPT_RECOVERY_CONFLICT_LOCK = 1 << 14,
+ INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT = 1 << 15,
+ INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN = 1 << 16,
+ INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK = 1 << 17,
+ INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT = 1 << 18,
+
+ /* Raised by timers */
+ INTERRUPT_TRANSACTION_TIMEOUT = 1 << 19,
+ INTERRUPT_IDLE_SESSION_TIMEOUT = 1 << 20,
+ INTERRUPT_IDLE_IN_TRANSACTION_SESSION_TIMEOUT = 1 << 21,
+ INTERRUPT_CLIENT_CHECK_TIMEOUT = 1 << 22,
+
+ /* Raised synchronously when the client connection is lost */
+ INTERRUPT_CLIENT_CONNECTION_LOST = 1 << 23,
+
+ /* Ask backend to log the memory contexts */
+ INTERRUPT_LOG_MEMORY_CONTEXT = 1 << 24,
+
+ /* Message from a cooperating parallel backend */
+ INTERRUPT_PARALLEL_MESSAGE = 1 << 25,
+
+ /* Message from a parallel apply worker */
+ INTERRUPT_PARALLEL_APPLY_MESSAGE = 1 << 26,
+
+ /* procsignal global barrier interrupt */
+ INTERRUPT_BARRIER = 1 << 27,
+
+ /*---- end of interrupts handled by CHECK_FOR_INTERRUPTS() ----*/
+
+ /*
+ * NOTE: InterruptTypes must fit in a 32-bit bitmask. (If we had efficient
+ * 64-bit atomics on all platforms, we could easily go up to 64 bits)
+ */
+
+ /*
+ * SLEEPING_ON_INTERRUPTS indicates that the backend is currently blocked
+ * waiting for an interrupt. If it's set, the backend needs to be woken up
+ * when a bit in the pending interrupts mask is set. It's used internally
+ * by the interrupt machinery, and cannot be used directly in the public
+ * functions. It's named differently to distinguish it from the actual
+ * interrupt flags.
+ */
+ SLEEPING_ON_INTERRUPTS = 1 << 31,
+
} InterruptType;
+/* The set of interrupts that are (ever) processed by CHECK_FOR_INTERRUPTS */
+#define INTERRUPT_CFI_ALL_MASK ( \
+ INTERRUPT_DIE | \
+ INTERRUPT_QUERY_CANCEL | \
+ INTERRUPT_WALSND_INIT_STOPPING | \
+ INTERRUPT_RECOVERY_CONFLICT_DATABASE | \
+ INTERRUPT_RECOVERY_CONFLICT_TABLESPACE | \
+ INTERRUPT_RECOVERY_CONFLICT_LOCK | \
+ INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT | \
+ INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN | \
+ INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK | \
+ INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT | \
+ INTERRUPT_TRANSACTION_TIMEOUT | \
+ INTERRUPT_IDLE_SESSION_TIMEOUT | \
+ INTERRUPT_IDLE_IN_TRANSACTION_SESSION_TIMEOUT | \
+ INTERRUPT_CLIENT_CHECK_TIMEOUT | \
+ INTERRUPT_CLIENT_CONNECTION_LOST | \
+ INTERRUPT_LOG_MEMORY_CONTEXT | \
+ INTERRUPT_PARALLEL_MESSAGE | \
+ INTERRUPT_PARALLEL_APPLY_MESSAGE | \
+ INTERRUPT_BARRIER \
+ )
+
+/* The set of interrupts that are processed by ProcessMainLoopInterrupts */
+#define INTERRUPT_MAIN_LOOP_MASK ( \
+ INTERRUPT_BARRIER | \
+ INTERRUPT_SHUTDOWN_AUX | \
+ INTERRUPT_LOG_MEMORY_CONTEXT | \
+ INTERRUPT_CONFIG_RELOAD \
+ )
+
/*
- * Test an interrupt flag (of flags).
+ * Test an interrupt flag (or flags).
*/
static inline bool
IsInterruptPending(uint32 interruptMask)
{
- return (pg_atomic_read_u32(MyPendingInterrupts) & interruptMask) != 0;
+ pg_read_barrier();
+
+#ifdef WIN32
+ if (unlikely(UNBLOCKED_SIGNAL_QUEUE()))
+ pgwin32_dispatch_queued_signals();
+#endif
+
+ if (unlikely((pg_atomic_read_u32(MyPendingInterrupts) & interruptMask) != 0))
+ return true;
+ else
+ return false;
}
/*
- * Clear an interrupt flag.
+ * Clear an interrupt flag (or flags).
*/
static inline void
ClearInterrupt(uint32 interruptMask)
{
pg_atomic_fetch_and_u32(MyPendingInterrupts, ~interruptMask);
+ pg_write_barrier();
}
/*
- * Test and clear an interrupt flag.
+ * Test and clear an interrupt flag (or flags).
*/
static inline bool
ConsumeInterrupt(uint32 interruptMask)
@@ -155,9 +433,6 @@ ConsumeInterrupt(uint32 interruptMask)
return true;
}
-extern PGDLLIMPORT volatile sig_atomic_t ConfigReloadPending;
-extern PGDLLIMPORT volatile sig_atomic_t ShutdownRequestPending;
-
extern void RaiseInterrupt(uint32 interruptMask);
extern void SendInterrupt(uint32 interruptMask, ProcNumber pgprocno);
extern int WaitInterrupt(uint32 interruptMask, int wakeEvents, long timeout,
@@ -173,4 +448,7 @@ extern void SignalHandlerForConfigReload(SIGNAL_ARGS);
extern void SignalHandlerForCrashExit(SIGNAL_ARGS);
extern void SignalHandlerForShutdownRequest(SIGNAL_ARGS);
+/* in tcop/postgres.c */
+extern void ProcessInterrupts(void);
+
#endif
diff --git a/src/include/postmaster/startup.h b/src/include/postmaster/startup.h
index 158f52255a6..a0316202b95 100644
--- a/src/include/postmaster/startup.h
+++ b/src/include/postmaster/startup.h
@@ -25,6 +25,14 @@
extern PGDLLIMPORT int log_startup_progress_interval;
+/* The set of interrupts that are processed by ProcessStartupProcInterrupts */
+#define INTERRUPT_STARTUP_PROC_MASK ( \
+ INTERRUPT_BARRIER | \
+ INTERRUPT_DIE | \
+ INTERRUPT_LOG_MEMORY_CONTEXT | \
+ INTERRUPT_CONFIG_RELOAD \
+ )
+
extern void ProcessStartupProcInterrupts(void);
extern void StartupProcessMain(const void *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void PreRestoreCommand(void);
diff --git a/src/include/replication/logicalworker.h b/src/include/replication/logicalworker.h
index 88912606e4d..8900d1ed92f 100644
--- a/src/include/replication/logicalworker.h
+++ b/src/include/replication/logicalworker.h
@@ -12,10 +12,6 @@
#ifndef LOGICALWORKER_H
#define LOGICALWORKER_H
-#include <signal.h>
-
-extern PGDLLIMPORT volatile sig_atomic_t ParallelApplyMessagePending;
-
extern void ApplyWorkerMain(Datum main_arg);
extern void ParallelApplyWorkerMain(Datum main_arg);
extern void TablesyncWorkerMain(Datum main_arg);
@@ -23,7 +19,6 @@ extern void TablesyncWorkerMain(Datum main_arg);
extern bool IsLogicalWorker(void);
extern bool IsLogicalParallelApplyWorker(void);
-extern void HandleParallelApplyMessageInterrupt(void);
extern void ProcessParallelApplyMessages(void);
extern void LogicalRepWorkersWakeupAtCommit(Oid subid);
diff --git a/src/include/replication/slot.h b/src/include/replication/slot.h
index f5a24ccfbf2..b0ac9f70ae2 100644
--- a/src/include/replication/slot.h
+++ b/src/include/replication/slot.h
@@ -160,8 +160,8 @@ typedef struct ReplicationSlot
/* is this slot defined */
bool in_use;
- /* Who is streaming out changes for this slot? 0 in unused slots. */
- pid_t active_pid;
+ /* Who is streaming out changes for this slot? INVALID_PROC_NUMBER in unused slots. */
+ ProcNumber active_proc;
/* any outstanding modifications? */
bool just_dirtied;
@@ -187,7 +187,7 @@ typedef struct ReplicationSlot
/* is somebody performing io on this slot? */
LWLock io_in_progress_lock;
- /* Condition variable signaled when active_pid changes */
+ /* Condition variable signaled when active_proc changes */
ConditionVariable active_cv;
/* all the remaining data is only used for logical slots */
diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h
index c3e8e191339..24026fcfe5c 100644
--- a/src/include/replication/walsender.h
+++ b/src/include/replication/walsender.h
@@ -46,7 +46,7 @@ extern void WalSndShmemInit(void);
extern void WalSndWakeup(bool physical, bool logical);
extern void WalSndInitStopping(void);
extern void WalSndWaitStopping(void);
-extern void HandleWalSndInitStopping(void);
+extern void ProcessWalSndInitStopping(void);
extern void WalSndRqstFileReload(void);
/*
diff --git a/src/include/replication/walsender_private.h b/src/include/replication/walsender_private.h
index 814b812432a..3f07fed7a35 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -40,6 +40,7 @@ typedef enum WalSndState
*/
typedef struct WalSnd
{
+ ProcNumber pgprocno; /* this walsender's ProcNumber, or invalid if not active */
pid_t pid; /* this walsender's PID, or 0 if not active */
WalSndState state; /* this walsender's state */
diff --git a/src/include/replication/worker_internal.h b/src/include/replication/worker_internal.h
index 30b2775952c..6781e9f6a6a 100644
--- a/src/include/replication/worker_internal.h
+++ b/src/include/replication/worker_internal.h
@@ -78,9 +78,10 @@ typedef struct LogicalRepWorker
FileSet *stream_fileset;
/*
- * PID of leader apply worker if this slot is used for a parallel apply
- * worker, InvalidPid otherwise.
+ * Leader apply worker if this slot is used for a parallel apply worker,
+ * INVALID_PROC_NUMBER/InvalidPid otherwise.
*/
+ ProcNumber leader_pgprocno;
pid_t leader_pid;
/* Indicates whether apply can be performed in parallel. */
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index ef0b733ebe8..59a264059bc 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -14,6 +14,7 @@
#ifndef PROCARRAY_H
#define PROCARRAY_H
+#include "postmaster/interrupt.h"
#include "storage/lock.h"
#include "storage/standby.h"
#include "utils/relcache.h"
@@ -77,14 +78,14 @@ extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
bool excludeXmin0, bool allDbs, int excludeVacuum,
int *nvxids);
extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid);
-extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode);
-extern pid_t SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode,
+extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, InterruptType reason);
+extern pid_t SignalVirtualTransaction(VirtualTransactionId vxid, InterruptType reason,
bool conflictPending);
extern bool MinimumActiveBackends(int min);
extern int CountDBBackends(Oid databaseid);
extern int CountDBConnections(Oid databaseid);
-extern void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending);
+extern void CancelDBBackends(Oid databaseid, InterruptType reason, bool conflictPending);
extern int CountUserBackends(Oid roleid);
extern bool CountOtherDBBackends(Oid databaseId,
int *nbackends, int *nprepared);
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 022fd8ed933..8d917f1bec4 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -14,43 +14,6 @@
#ifndef PROCSIGNAL_H
#define PROCSIGNAL_H
-#include "storage/procnumber.h"
-
-
-/*
- * Reasons for signaling a Postgres child process (a backend or an auxiliary
- * process, like checkpointer). We can cope with concurrent signals for different
- * reasons. However, if the same reason is signaled multiple times in quick
- * succession, the process is likely to observe only one notification of it.
- * This is okay for the present uses.
- *
- * Also, because of race conditions, it's important that all the signals be
- * defined so that no harm is done if a process mistakenly receives one.
- */
-typedef enum
-{
- PROCSIG_CATCHUP_INTERRUPT, /* sinval catchup interrupt */
- PROCSIG_NOTIFY_INTERRUPT, /* listen/notify interrupt */
- PROCSIG_PARALLEL_MESSAGE, /* message from cooperating parallel backend */
- PROCSIG_WALSND_INIT_STOPPING, /* ask walsenders to prepare for shutdown */
- PROCSIG_BARRIER, /* global barrier interrupt */
- PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
- PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */
-
- /* Recovery conflict reasons */
- PROCSIG_RECOVERY_CONFLICT_FIRST,
- PROCSIG_RECOVERY_CONFLICT_DATABASE = PROCSIG_RECOVERY_CONFLICT_FIRST,
- PROCSIG_RECOVERY_CONFLICT_TABLESPACE,
- PROCSIG_RECOVERY_CONFLICT_LOCK,
- PROCSIG_RECOVERY_CONFLICT_SNAPSHOT,
- PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT,
- PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
- PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
- PROCSIG_RECOVERY_CONFLICT_LAST = PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
-} ProcSignalReason;
-
-#define NUM_PROCSIGNALS (PROCSIG_RECOVERY_CONFLICT_LAST + 1)
-
typedef enum
{
PROCSIGNAL_BARRIER_SMGRRELEASE, /* ask smgr to close files */
@@ -63,16 +26,12 @@ extern Size ProcSignalShmemSize(void);
extern void ProcSignalShmemInit(void);
extern void ProcSignalInit(bool cancel_key_valid, int32 cancel_key);
-extern int SendProcSignal(pid_t pid, ProcSignalReason reason,
- ProcNumber procNumber);
extern void SendCancelRequest(int backendPID, int32 cancelAuthCode);
extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
extern void WaitForProcSignalBarrier(uint64 generation);
extern void ProcessProcSignalBarrier(void);
-extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
-
/* ProcSignalHeader is an opaque struct, details known only within procsignal.c */
typedef struct ProcSignalHeader ProcSignalHeader;
diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h
index 2463c0f9fac..cc627ae4853 100644
--- a/src/include/storage/sinval.h
+++ b/src/include/storage/sinval.h
@@ -125,16 +125,11 @@ typedef union
/* Counter of messages processed; don't worry about overflow. */
extern PGDLLIMPORT uint64 SharedInvalidMessageCounter;
-extern PGDLLIMPORT volatile sig_atomic_t catchupInterruptPending;
-
extern void SendSharedInvalidMessages(const SharedInvalidationMessage *msgs,
int n);
extern void ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *msg),
void (*resetFunction) (void));
-/* signal handler for catchup events (PROCSIG_CATCHUP_INTERRUPT) */
-extern void HandleCatchupInterrupt(void);
-
/*
* enable/disable processing of catchup events directly from signal handler.
* The enable routine first performs processing of any catchup events that
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index 24e2f5082bc..881506d609f 100644
--- a/src/include/storage/standby.h
+++ b/src/include/storage/standby.h
@@ -15,8 +15,8 @@
#define STANDBY_H
#include "datatype/timestamp.h"
+#include "postmaster/interrupt.h"
#include "storage/lock.h"
-#include "storage/procsignal.h"
#include "storage/relfilelocator.h"
#include "storage/standbydefs.h"
@@ -43,7 +43,7 @@ extern void CheckRecoveryConflictDeadlock(void);
extern void StandbyDeadLockHandler(void);
extern void StandbyTimeoutHandler(void);
extern void StandbyLockTimeoutHandler(void);
-extern void LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start,
+extern void LogRecoveryConflict(InterruptType reason, TimestampTz wait_start,
TimestampTz now, VirtualTransactionId *wait_list,
bool still_waiting);
diff --git a/src/include/storage/waiteventset.h b/src/include/storage/waiteventset.h
index 60ececb785c..c9ab8136b53 100644
--- a/src/include/storage/waiteventset.h
+++ b/src/include/storage/waiteventset.h
@@ -27,7 +27,6 @@
#include <signal.h>
#include "storage/procnumber.h"
-#include "utils/resowner.h"
/*
* Bitmasks for events that may wake-up WaitInterrupt(),
@@ -82,7 +81,9 @@ extern void InitializeWaitEventSupport(void);
extern HANDLE CreateSharedWakeupHandle(void);
#endif
-extern WaitEventSet *CreateWaitEventSet(ResourceOwner resowner, int nevents);
+struct ResourceOwnerData;
+
+extern WaitEventSet *CreateWaitEventSet(struct ResourceOwnerData *resowner, int nevents);
extern void FreeWaitEventSet(WaitEventSet *set);
extern void FreeWaitEventSetAfterFork(WaitEventSet *set);
extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index a62367f7793..908f39217b0 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -16,7 +16,6 @@
#include "nodes/params.h"
#include "nodes/plannodes.h"
-#include "storage/procsignal.h"
#include "utils/guc.h"
#include "utils/queryenvironment.h"
@@ -72,9 +71,8 @@ extern void die(SIGNAL_ARGS);
extern void quickdie(SIGNAL_ARGS) pg_attribute_noreturn();
extern void StatementCancelHandler(SIGNAL_ARGS);
extern void FloatExceptionHandler(SIGNAL_ARGS) pg_attribute_noreturn();
-extern void HandleRecoveryConflictInterrupt(ProcSignalReason reason);
-extern void ProcessClientReadInterrupt(bool blocked);
-extern void ProcessClientWriteInterrupt(bool blocked);
+extern uint32 ProcessClientReadInterrupt(bool blocked);
+extern uint32 ProcessClientWriteInterrupt(bool blocked);
extern void process_postgres_switches(int argc, char *argv[],
GucContext ctx, const char **dbname);
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 8abc26abce2..5dce202fe90 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -101,7 +101,6 @@ extern void MemoryContextCheck(MemoryContext context);
#define MemoryContextCopyAndSetIdentifier(cxt, id) \
MemoryContextSetIdentifier(cxt, MemoryContextStrdup(cxt, id))
-extern void HandleLogMemoryContextInterrupt(void);
extern void ProcessLogMemoryContextInterrupt(void);
/*
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 02aa420360c..ebf0bffb624 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -263,6 +263,9 @@ wait_for_workers_to_become_ready(worker_state *wstate,
{
int workers_ready;
+ /* Clear the interrupt flag so we don't spin. */
+ ClearInterrupt(INTERRUPT_GENERAL);
+
/* If all the workers are ready, we have succeeded. */
SpinLockAcquire(&hdr->mutex);
workers_ready = hdr->workers_ready;
@@ -285,12 +288,10 @@ wait_for_workers_to_become_ready(worker_state *wstate,
we_bgworker_startup = WaitEventExtensionNew("TestShmMqBgWorkerStartup");
/* Wait to be signaled. */
- (void) WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
we_bgworker_startup);
- /* Clear the interrupt flag so we don't spin. */
- ClearInterrupt(INTERRUPT_GENERAL);
-
/* 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 240fe315d47..101f6b7741c 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -171,6 +171,8 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
{
bool wait = true;
+ ClearInterrupt(INTERRUPT_GENERAL);
+
/*
* If we haven't yet sent the message the requisite number of times,
* try again to send it now. Note that when shm_mq_send() returns
@@ -239,9 +241,9 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
* they have read or written data and therefore there may now be
* work for us to do.
*/
- (void) WaitInterrupt(INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() | INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0,
we_message_queue);
- ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
}
}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 28a2798037a..6dcd4141c0b 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -216,7 +216,9 @@ worker_spi_main(Datum main_arg)
* is awakened if postmaster dies. That way the background process
* goes away immediately in an emergency.
*/
- (void) WaitInterrupt(INTERRUPT_GENERAL,
+ (void) WaitInterrupt(INTERRUPT_CFI_MASK() |
+ INTERRUPT_CONFIG_RELOAD |
+ INTERRUPT_GENERAL,
WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
worker_spi_naptime * 1000L,
worker_spi_wait_event_main);
@@ -227,11 +229,8 @@ worker_spi_main(Datum main_arg)
/*
* In case of a SIGHUP, just reload the configuration.
*/
- if (ConfigReloadPending)
- {
- ConfigReloadPending = false;
+ if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD))
ProcessConfigFile(PGC_SIGHUP);
- }
/*
* Start a transaction on which we can run queries. Note that each
--
2.39.5
view thread (22+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: Interrupts vs signals
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox