diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index fac4051e1aa4..7b2926e66d88 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -37,8 +37,8 @@
#include "storage/dsm.h"
#include "storage/dsm_registry.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/lwlock.h"
#include "storage/procsignal.h"
#include "storage/smgr.h"
@@ -216,10 +216,10 @@ autoprewarm_main(Datum main_arg)
if (autoprewarm_interval <= 0)
{
/* We're only dumping at shutdown, so just wait forever. */
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_EXIT_ON_PM_DEATH,
- -1L,
- PG_WAIT_EXTENSION);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+ -1L,
+ PG_WAIT_EXTENSION);
}
else
{
@@ -243,14 +243,14 @@ autoprewarm_main(Datum main_arg)
}
/* Sleep until the next dump time. */
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- delay_in_ms,
- PG_WAIT_EXTENSION);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ delay_in_ms,
+ PG_WAIT_EXTENSION);
}
/* Reset the latch, loop. */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -817,9 +817,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),
@@ -853,9 +850,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/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index dbee33b37dbd..358ae8efd24f 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -26,7 +26,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "postgres_fdw.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/inval.h"
@@ -740,8 +740,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);
@@ -1384,7 +1384,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))
@@ -1479,7 +1479,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))
@@ -1592,12 +1592,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(1 << 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 cf5643411842..0edbdcf0cfa5 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -38,7 +38,7 @@
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "postgres_fdw.h"
-#include "storage/latch.h"
+#include "storage/waiteventset.h"
#include "utils/builtins.h"
#include "utils/float.h"
#include "utils/guc.h"
@@ -7354,7 +7354,7 @@ postgresForeignAsyncConfigureWait(AsyncRequest *areq)
Assert(pendingAreq == areq);
AddWaitEventToSet(set, WL_SOCKET_READABLE, PQsocket(fsstate->conn),
- NULL, areq);
+ 0, areq);
}
/*
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91f..871f6869d6ac 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;
@@ -108,6 +108,18 @@ typedef struct BackgroundWorker
+
+ BGWORKER_NO_NOTIFY
+
+
+ BGWORKER_NO_NOTIFY
+ Normally, the backend that registers a dynamic worker will be notified
+ with INTERRUPT_GENERAL 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.
+
+
+
@@ -181,12 +193,8 @@ typedef struct BackgroundWorker
- bgw_notify_pid is the PID of a PostgreSQL
- backend process to which the postmaster should send SIGUSR1
- 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 MyProcPid.
+ bgw_notify_pid is not used and may be removed
+ in a future release.
Once running, the process can connect to a database by calling
@@ -227,7 +235,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
- WaitLatch(). Make sure the
+ WaitInterrupt(). Make sure the
WL_POSTMASTER_DEATH flag is set when calling that function, and
verify the return code for a prompt exit in the emergency case that
postgres itself has terminated.
@@ -258,10 +266,7 @@ typedef struct BackgroundWorker
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
- bgw_notify_pid to MyProcPid and
- then passing the BackgroundWorkerHandle * obtained at
- registration time to
+ wait for the worker to start up. This can be accomplished with the
WaitForBackgroundWorkerStartup(BackgroundWorkerHandle
*handle, pid_t *) function.
This function will block until the postmaster has attempted to start the
diff --git a/doc/src/sgml/sources.sgml b/doc/src/sgml/sources.sgml
index fa68d4d024a9..fc642f5f7134 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 volatile sig_atomic_t. A few
functions in postgres are also deemed signal safe, importantly
- SetLatch().
+ RaiseInterrupt().
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:
static void
handle_sighup(SIGNAL_ARGS)
{
got_SIGHUP = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index f2ca94305817..38c5bb6e7efd 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -53,6 +53,7 @@
#include "postmaster/autovacuum.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
+#include "storage/interrupt.h"
#include "storage/lmgr.h"
#include "utils/lsyscache.h"
#include "utils/pg_rusage.h"
@@ -2706,11 +2707,11 @@ lazy_truncate_heap(LVRelState *vacrel)
return;
}
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL,
- WAIT_EVENT_VACUUM_TRUNCATE);
- ResetLatch(MyLatch);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 6e4711dace70..b12d8be647c3 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 60d95037b365..f870072db937 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -33,6 +33,7 @@
#include "miscadmin.h"
#include "optimizer/optimizer.h"
#include "pgstat.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/predicate.h"
#include "storage/spin.h"
@@ -599,7 +600,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.
@@ -755,16 +755,16 @@ WaitForParallelWorkersToAttach(ParallelContext *pcxt)
{
/*
* Worker not yet started, so we must wait. The postmaster
- * will notify us if the worker's state changes. Our latch
- * might also get set for some other reason, but if so we'll
- * just end up waiting for the same worker again.
+ * will notify us if the worker's state changes. The
+ * interrupt might also get set for some other reason, but if
+ * so we'll just end up waiting for the same worker again.
*/
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_EXIT_ON_PM_DEATH,
- -1, WAIT_EVENT_BGWORKER_STARTUP);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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);
}
}
@@ -873,15 +873,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(1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, -1,
+ WAIT_EVENT_PARALLEL_FINISH);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
if (pcxt->toc != NULL)
@@ -1034,7 +1035,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 6f58412bcabe..5fe0bfc524f6 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -84,9 +84,9 @@
#include "replication/walsender.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/large_object.h"
-#include "storage/latch.h"
#include "storage/predicate.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -2676,7 +2676,7 @@ XLogSetAsyncXactLSN(XLogRecPtr asyncXactLSN)
ProcNumber walwriterProc = procglobal->walwriterProc;
if (walwriterProc != INVALID_PROC_NUMBER)
- SetLatch(&GetPGProcByNumber(walwriterProc)->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, ProcGlobal->walwriterProc);
}
}
@@ -9347,11 +9347,11 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive)
reported_waiting = true;
}
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- 1000L,
- WAIT_EVENT_BACKUP_WAIT_WAL_ARCHIVE);
- ResetLatch(MyLatch);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 b0c6d7c6875c..3e1d6074cf11 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -28,7 +28,7 @@
#include "pgstat.h"
#include "replication/walreceiver.h"
#include "storage/fd.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
#include "storage/standby.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
@@ -718,17 +718,17 @@ pg_promote(PG_FUNCTION_ARGS)
{
int rc;
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
if (!RecoveryInProgress())
PG_RETURN_BOOL(true);
CHECK_FOR_INTERRUPTS();
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
- 1000L / WAITS_PER_SECOND,
- WAIT_EVENT_PROMOTE);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 c6994b78282a..676d6710abb8 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -52,9 +52,10 @@
#include "replication/slotsync.h"
#include "replication/walreceiver.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/pmsignal.h"
+#include "storage/proc.h"
#include "storage/procarray.h"
#include "storage/spin.h"
#include "utils/datetime.h"
@@ -315,23 +316,6 @@ typedef struct XLogRecoveryCtlData
*/
bool SharedPromoteIsTriggered;
- /*
- * recoveryWakeupLatch is used to wake up the startup process to continue
- * WAL replay, if it is waiting for WAL to arrive or promotion to be
- * requested.
- *
- * Note that the startup process also uses another latch, its procLatch,
- * to wait for recovery conflict. If we get rid of recoveryWakeupLatch for
- * signaling the startup process in favor of using its procLatch, which
- * comports better with possible generic signal handlers using that latch.
- * But we should not do that because the startup process doesn't assume
- * that it's waken up by walreceiver process or SIGHUP signal handler
- * while it's waiting for recovery conflict. The separate latches,
- * recoveryWakeupLatch and procLatch, should be used for inter-process
- * communication for WAL replay and recovery conflict, respectively.
- */
- Latch recoveryWakeupLatch;
-
/*
* Last record successfully replayed.
*/
@@ -466,7 +450,6 @@ XLogRecoveryShmemInit(void)
memset(XLogRecoveryCtl, 0, sizeof(XLogRecoveryCtlData));
SpinLockInit(&XLogRecoveryCtl->info_lck);
- InitSharedLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
ConditionVariableInit(&XLogRecoveryCtl->recoveryNotPausedCV);
}
@@ -540,13 +523,6 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
readRecoverySignalFile();
validateRecoveryParameters();
- /*
- * Take ownership of the wakeup latch if we're going to sleep during
- * recovery, if required.
- */
- if (ArchiveRecoveryRequested)
- OwnLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
-
/*
* Set the WAL reading processor now, as it will be needed when reading
* the checkpoint record required (backup_label or not).
@@ -1634,13 +1610,6 @@ ShutdownWalRecovery(void)
snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYHISTORY");
unlink(recoveryPath); /* ignore any error */
}
-
- /*
- * We don't need the latch anymore. It's not strictly necessary to disown
- * it, but let's do it for the sake of tidiness.
- */
- if (ArchiveRecoveryRequested)
- DisownLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
}
/*
@@ -1800,7 +1769,7 @@ PerformWalRecovery(void)
}
/*
- * If we've been asked to lag the primary, wait on latch until
+ * If we've been asked to lag the primary, wait on interrupt until
* enough time has passed.
*/
if (recoveryApplyDelay(xlogreader))
@@ -3023,8 +2992,8 @@ recoveryApplyDelay(XLogReaderState *record)
delayUntil = TimestampTzPlusMilliseconds(xtime, recovery_min_apply_delay);
/*
- * Exit without arming the latch if it's already past time to apply this
- * record
+ * Exit without clearing the interrupt if it's already past time to apply
+ * this record
*/
msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(), delayUntil);
if (msecs <= 0)
@@ -3032,11 +3001,19 @@ recoveryApplyDelay(XLogReaderState *record)
while (true)
{
- ResetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
+ /*
+ * INTERRUPT_GENERAL 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. */
HandleStartupProcInterrupts();
+ ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE);
if (CheckForStandbyTrigger())
break;
@@ -3057,10 +3034,11 @@ recoveryApplyDelay(XLogReaderState *record)
elog(DEBUG2, "recovery apply delay %ld milliseconds", msecs);
- (void) WaitLatch(&XLogRecoveryCtl->recoveryWakeupLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- msecs,
- WAIT_EVENT_RECOVERY_APPLY_DELAY);
+ (void) WaitInterrupt(1 << INTERRUPT_RECOVERY_CONTINUE |
+ 1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ msecs,
+ WAIT_EVENT_RECOVERY_APPLY_DELAY);
}
return true;
}
@@ -3703,15 +3681,17 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
/* Do background tasks that might benefit us later. */
KnownAssignedTransactionIdsIdleMaintenance();
- (void) WaitLatch(&XLogRecoveryCtl->recoveryWakeupLatch,
- WL_LATCH_SET | WL_TIMEOUT |
- WL_EXIT_ON_PM_DEATH,
- wait_time,
- WAIT_EVENT_RECOVERY_RETRIEVE_RETRY_INTERVAL);
- ResetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
+ (void) WaitInterrupt(1 << INTERRUPT_RECOVERY_CONTINUE |
+ 1 << INTERRUPT_GENERAL,
+ 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);
HandleStartupProcInterrupts();
}
last_fail_time = now;
@@ -3978,11 +3958,12 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
* Wait for more WAL to arrive, when we will be woken
* immediately by the WAL receiver.
*/
- (void) WaitLatch(&XLogRecoveryCtl->recoveryWakeupLatch,
- WL_LATCH_SET | WL_EXIT_ON_PM_DEATH,
- -1L,
- WAIT_EVENT_RECOVERY_WAL_STREAM);
- ResetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
+ (void) WaitInterrupt(1 << INTERRUPT_RECOVERY_CONTINUE |
+ 1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+ -1L,
+ WAIT_EVENT_RECOVERY_WAL_STREAM);
+ ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE);
break;
}
@@ -4002,6 +3983,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
* This possibly-long loop needs to handle interrupts of startup
* process.
*/
+ ClearInterrupt(INTERRUPT_GENERAL);
HandleStartupProcInterrupts();
}
@@ -4476,7 +4458,10 @@ CheckPromoteSignal(void)
void
WakeupRecovery(void)
{
- SetLatch(&XLogRecoveryCtl->recoveryWakeupLatch);
+ ProcNumber procno = ((volatile PROC_HDR *) ProcGlobal)->startupProc;
+
+ if (procno != INVALID_PROC_NUMBER)
+ SendInterrupt(INTERRUPT_RECOVERY_CONTINUE, procno);
}
/*
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 5295b85fe078..41f0cde62724 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -853,9 +853,9 @@ wal_segment_close(XLogReaderState *state)
* output method outside walsender, e.g. in a bgworker.
*
* TODO: The walsender has its own version of this, but it relies on the
- * walsender's latch being set whenever WAL is flushed. No such infrastructure
- * exists for normal backends, so we have to do a check/sleep/repeat style of
- * loop for now.
+ * walsender's interrupt being set whenever WAL is flushed. No such
+ * infrastructure exists for normal backends, so we have to do a
+ * check/sleep/repeat style of loop for now.
*/
int
read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
diff --git a/src/backend/backup/basebackup_throttle.c b/src/backend/backup/basebackup_throttle.c
index 4477945e6133..3d865fa621fb 100644
--- a/src/backend/backup/basebackup_throttle.c
+++ b/src/backend/backup/basebackup_throttle.c
@@ -17,7 +17,7 @@
#include "backup/basebackup_sink.h"
#include "miscadmin.h"
#include "pgstat.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
#include "utils/timestamp.h"
typedef struct bbsink_throttle
@@ -146,7 +146,7 @@ throttle(bbsink_throttle *sink, size_t increment)
(sink->throttling_counter / sink->throttling_sample);
/*
- * Since the latch could be set repeatedly because of concurrently WAL
+ * Since the interrupt could be set repeatedly because of concurrently WAL
* activity, sleep in a loop to ensure enough time has passed.
*/
for (;;)
@@ -163,21 +163,21 @@ throttle(bbsink_throttle *sink, size_t increment)
if (sleep <= 0)
break;
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
- /* We're eating a potentially set latch, so check for interrupts */
+ /* We're eating a wakeup, so check for interrupts */
CHECK_FOR_INTERRUPTS();
/*
* (TAR_SEND_SIZE / throttling_sample * elapsed_min_unit) should be
* the maximum time to sleep. Thus the cast to long is safe.
*/
- wait_result = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- (long) (sleep / 1000),
- WAIT_EVENT_BASE_BACKUP_THROTTLE);
+ wait_result = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 8ed503e1c1bd..bc954e905638 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 "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/procsignal.h"
@@ -406,9 +407,9 @@ static NotificationList *pendingNotifies = NULL;
/*
* Inbound notifications are initially processed by HandleNotifyInterrupt(),
* called from inside a signal handler. That just sets the
- * notifyInterruptPending flag and sets the process
- * latch. ProcessNotifyInterrupt() will then be called whenever it's safe to
- * actually deal with the interrupt.
+ * notifyInterruptPending flag and raises the INTERRUPT_GENERAL 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 bb639ef51fb2..539e4660f15a 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2412,8 +2412,8 @@ vacuum_delay_point(void)
/*
* We don't want to ignore postmaster death during very long vacuums
* with vacuum_cost_delay configured. We can't use the usual
- * WaitLatch() approach here because we want microsecond-based sleep
- * durations above.
+ * WaitInterrupt() approach here because we want microsecond-based
+ * sleep durations above.
*/
if (IsUnderPostmaster && !PostmasterIsAlive())
exit(1);
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index b5d56569f7f1..995ffa6fc9c6 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
@@ -1042,7 +1041,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 7f7edc7f9fc8..453970e7da72 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -36,6 +36,7 @@
#include "executor/tqueue.h"
#include "miscadmin.h"
#include "optimizer/optimizer.h"
+#include "storage/interrupt.h"
#include "utils/wait_event.h"
@@ -382,9 +383,10 @@ gather_readnext(GatherState *gatherstate)
return NULL;
/* Nothing to do except wait for developments. */
- (void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
- WAIT_EVENT_EXECUTE_GATHER);
- ResetLatch(MyLatch);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 47e8c9160600..ef7cba65e42f 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -1663,8 +1663,9 @@ interpret_ident_response(const char *ident_response,
* owns the tcp connection to "local_addr"
* If the username is successfully retrieved, check the usermap.
*
- * XXX: Using WaitLatchOrSocket() and doing a CHECK_FOR_INTERRUPTS() if the
- * latch was set would improve the responsiveness to timeouts/cancellations.
+ * XXX: Using WaitInterruptOrSocket() and doing a CHECK_FOR_INTERRUPTS()
+ * if the interrupt was pending would improve the responsiveness to
+ * timeouts/cancellations.
*/
static int
ident_inet(hbaPort *port)
@@ -3098,8 +3099,8 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
* packets to our port thus causing us to retry in a loop and never time
* out.
*
- * XXX: Using WaitLatchOrSocket() and doing a CHECK_FOR_INTERRUPTS() if
- * the latch was set would improve the responsiveness to
+ * XXX: Using WaitInterruptOrSocket() and doing a CHECK_FOR_INTERRUPTS()
+ * if the interrupt was pending would improve the responsiveness to
* timeouts/cancellations.
*/
gettimeofday(&endtime, NULL);
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 5a009776d121..2382ea08a8fc 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -21,6 +21,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "port/pg_bswap.h"
+#include "storage/interrupt.h"
#include "utils/injection_point.h"
#include "utils/memutils.h"
@@ -414,7 +415,7 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
/*
* Read the specified number of bytes off the wire, waiting using
- * WaitLatchOrSocket if we would block.
+ * WaitInterruptOrSocket if we would block.
*
* Results are read into PqGSSRecvBuffer.
*
@@ -450,9 +451,8 @@ read_or_wait(Port *port, ssize_t len)
*/
if (ret <= 0)
{
- WaitLatchOrSocket(NULL,
- WL_SOCKET_READABLE | WL_EXIT_ON_PM_DEATH,
- port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
+ WaitInterruptOrSocket(0, WL_SOCKET_READABLE | WL_EXIT_ON_PM_DEATH,
+ port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
/*
* If we got back zero bytes, and then waited on the socket to be
@@ -489,7 +489,7 @@ read_or_wait(Port *port, ssize_t len)
*
* Note that unlike the be_gssapi_read/be_gssapi_write functions, this
* function WILL block on the socket to be ready for read/write (using
- * WaitLatchOrSocket) as appropriate while establishing the GSSAPI
+ * WaitInterruptOrSocket) as appropriate while establishing the GSSAPI
* session.
*/
ssize_t
@@ -668,9 +668,8 @@ secure_open_gssapi(Port *port)
/* Wait and retry if we couldn't write yet */
if (ret <= 0)
{
- WaitLatchOrSocket(NULL,
- WL_SOCKET_WRITEABLE | WL_EXIT_ON_PM_DEATH,
- port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
+ WaitInterruptOrSocket(0, WL_SOCKET_WRITEABLE | WL_EXIT_ON_PM_DEATH,
+ port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
continue;
}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 91a86d62a35b..cdab6b0f595d 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -32,7 +32,8 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/fd.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
+#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/memutils.h"
@@ -522,8 +523,8 @@ be_tls_open_server(Port *port)
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 2139f81f2414..91346027d2bd 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -28,7 +28,7 @@
#include
#include "libpq/libpq.h"
-#include "miscadmin.h"
+#include "storage/interrupt.h"
#include "tcop/tcopprot.h"
#include "utils/injection_point.h"
#include "utils/wait_event.h"
@@ -213,7 +213,7 @@ secure_read(Port *port, void *ptr, size_t len)
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 @@ secure_read(Port *port, void *ptr, size_t len)
* 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 @@ secure_read(Port *port, void *ptr, size_t len)
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 @@ secure_write(Port *port, void *ptr, size_t len)
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 @@ secure_write(Port *port, void *ptr, size_t len)
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 896e1476b50d..c4c32c450a62 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -77,6 +77,7 @@
#include "miscadmin.h"
#include "port/pg_bswap.h"
#include "postmaster/postmaster.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "utils/guc_hooks.h"
#include "utils/memutils.h"
@@ -175,7 +176,7 @@ pq_init(ClientSocket *client_sock)
{
Port *port;
int socket_pos PG_USED_FOR_ASSERTS_ONLY;
- int latch_pos PG_USED_FOR_ASSERTS_ONLY;
+ int interrupt_pos PG_USED_FOR_ASSERTS_ONLY;
/* allocate the Port struct and copy the ClientSocket contents to it */
port = palloc0(sizeof(Port));
@@ -287,8 +288,8 @@ pq_init(ClientSocket *client_sock)
/*
* In backends (as soon as forked) we operate the underlying socket in
- * nonblocking mode and use latches to implement blocking semantics if
- * needed. That allows us to provide safely interruptible reads and
+ * nonblocking mode and use WaitEventSet to implement blocking semantics
+ * if needed. That allows us to provide safely interruptible reads and
* writes.
*/
#ifndef WIN32
@@ -306,18 +307,18 @@ pq_init(ClientSocket *client_sock)
FeBeWaitSet = CreateWaitEventSet(NULL, FeBeWaitSetNEvents);
socket_pos = AddWaitEventToSet(FeBeWaitSet, WL_SOCKET_WRITEABLE,
- port->sock, NULL, NULL);
- latch_pos = AddWaitEventToSet(FeBeWaitSet, WL_LATCH_SET, PGINVALID_SOCKET,
- MyLatch, NULL);
+ port->sock, 0, NULL);
+ interrupt_pos = AddWaitEventToSet(FeBeWaitSet, WL_INTERRUPT, PGINVALID_SOCKET,
+ 1 << INTERRUPT_GENERAL, NULL);
AddWaitEventToSet(FeBeWaitSet, WL_POSTMASTER_DEATH, PGINVALID_SOCKET,
- NULL, NULL);
+ 0, NULL);
/*
* The event positions match the order we added them, but let's sanity
* check them to be sure.
*/
Assert(socket_pos == FeBeWaitSetSocketPos);
- Assert(latch_pos == FeBeWaitSetLatchPos);
+ Assert(interrupt_pos == FeBeWaitSetInterruptPos);
return port;
}
@@ -2060,7 +2061,7 @@ pq_check_connection(void)
* It's OK to modify the socket event filter without restoring, because
* all FeBeWaitSet socket wait sites do the same.
*/
- ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, WL_SOCKET_CLOSED, NULL);
+ ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, WL_SOCKET_CLOSED, 0);
retry:
rc = WaitEventSetWait(FeBeWaitSet, 0, events, lengthof(events), 0);
@@ -2068,15 +2069,15 @@ pq_check_connection(void)
{
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 fd735e2fea9d..6f6f6288e6e5 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -20,6 +20,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "replication/logicalworker.h"
+#include "storage/interrupt.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
@@ -181,9 +182,10 @@ mq_putmessage(char msgtype, const char *s, size_t len)
if (result != SHM_MQ_WOULD_BLOCK)
break;
- (void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
- WAIT_EVENT_MESSAGE_QUEUE_PUT_MESSAGE);
- ResetLatch(MyLatch);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c
index 22a16c50b215..2f5eebdfec4b 100644
--- a/src/backend/libpq/pqsignal.c
+++ b/src/backend/libpq/pqsignal.c
@@ -42,7 +42,7 @@ pqinitmask(void)
{
sigemptyset(&UnBlockSig);
- /* Note: InitializeLatchSupport() modifies UnBlockSig. */
+ /* Note: InitializeWaitEventSupport() modifies UnBlockSig. */
/* First set all signals, then clear some. */
sigfillset(&BlockSig);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 6d324af7b0e5..718389906845 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -89,8 +89,8 @@
#include "postmaster/interrupt.h"
#include "postmaster/postmaster.h"
#include "storage/bufmgr.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/lmgr.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
@@ -571,10 +571,10 @@ AutoVacLauncherMain(char *startup_data, size_t startup_data_len)
bool can_launch;
/*
- * This loop is a bit different from the normal use of WaitLatch,
+ * This loop is a bit different from the normal use of WaitInterrupt,
* because we'd like to sleep before the first launch of a child
- * process. So it's WaitLatch, then ResetLatch, then check for
- * wakening conditions.
+ * process. So it's WaitInterrupt, then ClearInterrupt, then check
+ * for wakening conditions.
*/
launcher_determine_sleep(!dlist_is_empty(&AutoVacuumShmem->av_freeWorkers),
@@ -582,14 +582,14 @@ AutoVacLauncherMain(char *startup_data, size_t startup_data_len)
/*
* Wait until naptime expires or we get some type of signal (all the
- * signal handlers will wake us by calling SetLatch).
+ * signal handlers will wake us by calling RaiseInterrupt).
*/
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
- WAIT_EVENT_AUTOVACUUM_MAIN);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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);
HandleAutoVacLauncherInterrupts();
@@ -1346,7 +1346,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 d19174bda392..5cc056cd5aa2 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 7afe56885cb6..23b13b36a05d 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -21,8 +21,8 @@
#include "postmaster/postmaster.h"
#include "replication/logicallauncher.h"
#include "replication/logicalworker.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
@@ -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)
@@ -1226,9 +1233,9 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
if (status != BGWH_NOT_YET_STARTED)
break;
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
- WAIT_EVENT_BGWORKER_STARTUP);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_POSTMASTER_DEATH, 0,
+ WAIT_EVENT_BGWORKER_STARTUP);
if (rc & WL_POSTMASTER_DEATH)
{
@@ -1236,7 +1243,7 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
break;
}
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
return status;
@@ -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)
@@ -1269,9 +1277,9 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
if (status == BGWH_STOPPED)
break;
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
- WAIT_EVENT_BGWORKER_SHUTDOWN);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_POSTMASTER_DEATH, 0,
+ WAIT_EVENT_BGWORKER_SHUTDOWN);
if (rc & WL_POSTMASTER_DEATH)
{
@@ -1279,7 +1287,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 0f75548759a3..4200158a6260 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -42,6 +42,7 @@
#include "storage/bufmgr.h"
#include "storage/condition_variable.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/lwlock.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
@@ -224,7 +225,7 @@ BackgroundWriterMain(char *startup_data, size_t startup_data_len)
int rc;
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
HandleMainLoopInterrupts();
@@ -299,22 +300,22 @@ BackgroundWriterMain(char *startup_data, size_t startup_data_len)
* will call it every BgWriterDelay msec. While it's not critical for
* correctness that that be exact, the feedback loop might misbehave
* if we stray too far from that. Hence, avoid loading this process
- * down with latch events that are likely to happen frequently during
- * normal operation.
+ * down with interrupt events that are likely to happen frequently
+ * during normal operation.
*/
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- BgWriterDelay /* ms */ , WAIT_EVENT_BGWRITER_MAIN);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ BgWriterDelay /* ms */ , WAIT_EVENT_BGWRITER_MAIN);
/*
- * If no latch event and BgBufferSync says nothing's happening, extend
- * the sleep in "hibernation" mode, where we sleep for much longer
- * than bgwriter_delay says. Fewer wakeups save electricity. When a
- * backend starts using buffers again, it will wake us up by setting
- * our latch. Because the extra sleep will persist only as long as no
- * buffer allocations happen, this should not distort the behavior of
- * BgBufferSync's control loop too badly; essentially, it will think
- * that the system-wide idle interval didn't exist.
+ * If no interrupt event and BgBufferSync says nothing's happening,
+ * extend the sleep in "hibernation" mode, where we sleep for much
+ * longer than bgwriter_delay says. Fewer wakeups save electricity.
+ * When a backend starts using buffers again, it will wake us up by
+ * sending us an interrupt. Because the extra sleep will persist only
+ * as long as no buffer allocations happen, this should not distort
+ * the behavior of BgBufferSync's control loop too badly; essentially,
+ * it will think that the system-wide idle interval didn't exist.
*
* There is a race condition here, in that a backend might allocate a
* buffer between the time BgBufferSync saw the alloc count as zero
@@ -329,10 +330,10 @@ BackgroundWriterMain(char *startup_data, size_t startup_data_len)
/* Ask for notification at next buffer allocation */
StrategyNotifyBgWriter(MyProcNumber);
/* Sleep ... */
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- BgWriterDelay * HIBERNATE_FACTOR,
- WAIT_EVENT_BGWRITER_HIBERNATE);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 982572a75dbd..97abec31386a 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -49,6 +49,7 @@
#include "storage/bufmgr.h"
#include "storage/condition_variable.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/lwlock.h"
#include "storage/proc.h"
@@ -343,7 +344,7 @@ CheckpointerMain(char *startup_data, size_t startup_data_len)
bool chkpt_or_rstpt_timed = false;
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
/*
* Process any requests or signals received recently.
@@ -555,10 +556,10 @@ CheckpointerMain(char *startup_data, size_t startup_data_len)
cur_timeout = Min(cur_timeout, XLogArchiveTimeout - elapsed_secs);
}
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- cur_timeout * 1000L /* convert to ms */ ,
- WAIT_EVENT_CHECKPOINTER_MAIN);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ cur_timeout * 1000L /* convert to ms */ ,
+ WAIT_EVENT_CHECKPOINTER_MAIN);
}
}
@@ -758,10 +759,11 @@ CheckpointWriteDelay(int flags, double progress)
* Checkpointer and bgwriter are no longer related so take the Big
* Sleep.
*/
- WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH | WL_TIMEOUT,
- 100,
- WAIT_EVENT_CHECKPOINT_WRITE_DELAY);
- ResetLatch(MyLatch);
+ WaitInterrupt(1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH | WL_TIMEOUT,
+ 100,
+ WAIT_EVENT_CHECKPOINT_WRITE_DELAY);
+ ClearInterrupt(INTERRUPT_GENERAL);
}
else if (--absorb_counter <= 0)
{
@@ -873,7 +875,7 @@ ReqCheckpointHandler(SIGNAL_ARGS)
* The signaling process should have set ckpt_flags nonzero, so all we
* need do is ensure that our main loop gets kicked out of any wait.
*/
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
@@ -1145,7 +1147,7 @@ ForwardSyncRequest(const FileTag *ftag, SyncRequestType type)
ProcNumber checkpointerProc = procglobal->checkpointerProc;
if (checkpointerProc != INVALID_PROC_NUMBER)
- SetLatch(&GetPGProcByNumber(checkpointerProc)->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, ProcGlobal->checkpointerProc);
}
return true;
diff --git a/src/backend/postmaster/interrupt.c b/src/backend/postmaster/interrupt.c
index eedc0980cf11..af3443efc374 100644
--- a/src/backend/postmaster/interrupt.c
+++ b/src/backend/postmaster/interrupt.c
@@ -18,8 +18,8 @@
#include "miscadmin.h"
#include "postmaster/interrupt.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/procsignal.h"
#include "utils/guc.h"
#include "utils/memutils.h"
@@ -61,7 +61,7 @@ void
SignalHandlerForConfigReload(SIGNAL_ARGS)
{
ConfigReloadPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -105,5 +105,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 02f91431f5f3..b32cf788eaad 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -41,8 +41,8 @@
#include "postmaster/pgarch.h"
#include "storage/condition_variable.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
@@ -247,8 +247,8 @@ PgArchiverMain(char *startup_data, size_t startup_data_len)
on_shmem_exit(pgarch_die, 0);
/*
- * Advertise our proc number so that backends can use our latch to wake us
- * up while we're sleeping.
+ * Advertise our proc number so that backends can wake us up while we're
+ * sleeping.
*/
PgArch->pgprocno = MyProcNumber;
@@ -282,13 +282,12 @@ PgArchWakeup(void)
int arch_pgprocno = PgArch->pgprocno;
/*
- * We don't acquire ProcArrayLock here. It's actually fine because
- * procLatch isn't ever freed, so we just can potentially set the wrong
- * process' (or no process') latch. Even in that case the archiver will
- * be relaunched shortly and will start archiving.
+ * We don't acquire ProcArrayLock here, so we may send the interrupt to
+ * wrong process, but that's harmless. Even in that case the archiver
+ * will be relaunched shortly and will start archiving.
*/
if (arch_pgprocno != INVALID_PROC_NUMBER)
- SetLatch(&ProcGlobal->allProcs[arch_pgprocno].procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, arch_pgprocno);
}
@@ -298,7 +297,7 @@ pgarch_waken_stop(SIGNAL_ARGS)
{
/* set flag to do a final cycle and shut down afterwards */
ready_to_stop = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -318,7 +317,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 +354,10 @@ pgarch_MainLoop(void)
{
int rc;
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
- PGARCH_AUTOWAKE_INTERVAL * 1000L,
- WAIT_EVENT_ARCHIVER_MAIN);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 6f849ffbcb5d..2824ec084f59 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -109,6 +109,7 @@
#include "replication/slotsync.h"
#include "replication/walsender.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/pmsignal.h"
#include "tcop/backend_startup.h"
@@ -535,8 +536,7 @@ PostmasterMain(int argc, char *argv[])
pqsignal(SIGCHLD, handle_pm_child_exit_signal);
/* This may configure SIGURG, depending on platform. */
- InitializeLatchSupport();
- InitProcessLocalLatch();
+ InitializeWaitEventSupport();
/*
* No other place in Postgres should touch SIGTTIN/SIGTTOU handling. We
@@ -1605,14 +1605,14 @@ ConfigurePostmasterWaitSet(bool accept_connections)
pm_wait_set = CreateWaitEventSet(NULL,
accept_connections ? (1 + NumListenSockets) : 1);
- AddWaitEventToSet(pm_wait_set, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch,
- NULL);
+ AddWaitEventToSet(pm_wait_set, WL_INTERRUPT, PGINVALID_SOCKET,
+ 1 << INTERRUPT_GENERAL, NULL);
if (accept_connections)
{
for (int i = 0; i < NumListenSockets; i++)
AddWaitEventToSet(pm_wait_set, WL_SOCKET_ACCEPT, ListenSockets[i],
- NULL, NULL);
+ 0, NULL);
}
}
@@ -1641,19 +1641,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)
@@ -1946,7 +1947,7 @@ static void
handle_pm_pmsignal_signal(SIGNAL_ARGS)
{
pending_pm_pmsignal = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -1956,7 +1957,7 @@ static void
handle_pm_reload_request_signal(SIGNAL_ARGS)
{
pending_pm_reload_request = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -2033,7 +2034,7 @@ handle_pm_shutdown_request_signal(SIGNAL_ARGS)
pending_pm_shutdown_request = true;
break;
}
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -2194,7 +2195,7 @@ static void
handle_pm_child_exit_signal(SIGNAL_ARGS)
{
pending_pm_child_exit = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -4007,15 +4008,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/postmaster/startup.c b/src/backend/postmaster/startup.c
index ef6f98ebcd79..2020f170d000 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -28,6 +28,7 @@
#include "postmaster/startup.h"
#include "storage/ipc.h"
#include "storage/pmsignal.h"
+#include "storage/proc.h"
#include "storage/procsignal.h"
#include "storage/standby.h"
#include "utils/guc.h"
@@ -40,7 +41,7 @@
* On systems that need to make a system call to find out if the postmaster has
* gone away, we'll do so only every Nth call to HandleStartupProcInterrupts().
* This only affects how long it takes us to detect the condition while we're
- * busy replaying WAL. Latch waits and similar which should react immediately
+ * busy replaying WAL. Interrupt waits and similar should react immediately
* through the usual techniques.
*/
#define POSTMASTER_POLL_RATE_LIMIT 1024
@@ -205,6 +206,8 @@ StartupProcExit(int code, Datum arg)
/* Shutdown the recovery environment */
if (standbyState != STANDBY_DISABLED)
ShutdownRecoveryTransactionEnvironment();
+
+ ProcGlobal->startupProc = INVALID_PROC_NUMBER;
}
@@ -220,6 +223,12 @@ StartupProcessMain(char *startup_data, size_t startup_data_len)
MyBackendType = B_STARTUP;
AuxiliaryProcessMainCommon();
+ /*
+ * Advertise our proc number so that backends can wake us up, when the
+ * server is promoted or recovery is paused/resumed.
+ */
+ ProcGlobal->startupProc = MyProcNumber;
+
/* Arrange to clean up at startup process exit */
on_shmem_exit(StartupProcExit, 0);
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index f12639056f2b..7da491656792 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -44,8 +44,8 @@
#include "postmaster/syslogger.h"
#include "storage/dsm.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/pg_shmem.h"
#include "tcop/tcopprot.h"
#include "utils/guc.h"
@@ -328,7 +328,7 @@ SysLoggerMain(char *startup_data, size_t startup_data_len)
whereToSendOutput = DestNone;
/*
- * Set up a reusable WaitEventSet object we'll use to wait for our latch,
+ * Set up a reusable WaitEventSet object we'll use to wait for interrupts,
* and (except on Windows) our socket.
*
* Unlike all other postmaster child processes, we'll ignore postmaster
@@ -338,9 +338,9 @@ SysLoggerMain(char *startup_data, size_t startup_data_len)
* (including the postmaster).
*/
wes = CreateWaitEventSet(NULL, 2);
- AddWaitEventToSet(wes, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch, NULL);
+ AddWaitEventToSet(wes, WL_INTERRUPT, PGINVALID_SOCKET, 1 << INTERRUPT_GENERAL, NULL);
#ifndef WIN32
- AddWaitEventToSet(wes, WL_SOCKET_READABLE, syslogPipe[0], NULL, NULL);
+ AddWaitEventToSet(wes, WL_SOCKET_READABLE, syslogPipe[0], 0, NULL);
#endif
/* main worker loop */
@@ -356,7 +356,7 @@ SysLoggerMain(char *startup_data, size_t startup_data_len)
#endif
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
/*
* Process any requests or signals received recently.
@@ -1186,7 +1186,7 @@ pipeThread(void *arg)
if (ftell(syslogFile) >= Log_RotationSize * 1024L ||
(csvlogFile != NULL && ftell(csvlogFile) >= Log_RotationSize * 1024L) ||
(jsonlogFile != NULL && ftell(jsonlogFile) >= Log_RotationSize * 1024L))
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
LeaveCriticalSection(&sysloggerSection);
}
@@ -1197,8 +1197,8 @@ pipeThread(void *arg)
/* if there's any data left then force it out now */
flush_pipe_input(logbuffer, &bytes_in_logbuffer);
- /* set the latch to waken the main thread, which will quit */
- SetLatch(MyLatch);
+ /* raise the interrupt to waken the main thread, which will quit */
+ RaiseInterrupt(INTERRUPT_GENERAL);
LeaveCriticalSection(&sysloggerSection);
_endthread();
@@ -1593,5 +1593,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 48350bec524f..ad646fb3c0ad 100644
--- a/src/backend/postmaster/walsummarizer.c
+++ b/src/backend/postmaster/walsummarizer.c
@@ -38,8 +38,8 @@
#include "postmaster/walsummarizer.h"
#include "replication/walreceiver.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/lwlock.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
@@ -315,10 +315,8 @@ WalSummarizerMain(char *startup_data, size_t startup_data_len)
* So a really fast retry time doesn't seem to be especially
* beneficial, and it will clutter the logs.
*/
- (void) WaitLatch(NULL,
- WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- 10000,
- WAIT_EVENT_WAL_SUMMARIZER_ERROR);
+ (void) WaitInterrupt(0, WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10000,
+ WAIT_EVENT_WAL_SUMMARIZER_ERROR);
}
/* We can now handle ereport(ERROR) */
@@ -630,8 +628,8 @@ GetOldestUnsummarizedLSN(TimeLineID *tli, bool *lsn_is_exact)
*
* This might not work, because there's no guarantee that the WAL summarizer
* process was successfully started, and it also might have started but
- * subsequently terminated. So, under normal circumstances, this will get the
- * latch set, but there's no guarantee.
+ * subsequently terminated. So, under normal circumstances, this will send
+ * the interrupt, but there's no guarantee.
*/
void
WakeupWalSummarizer(void)
@@ -646,7 +644,7 @@ WakeupWalSummarizer(void)
LWLockRelease(WALSummarizerLock);
if (pgprocno != INVALID_PROC_NUMBER)
- SetLatch(&ProcGlobal->allProcs[pgprocno].procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, pgprocno);
}
/*
@@ -1637,11 +1635,11 @@ summarizer_wait_for_wal(void)
}
/* OK, now sleep. */
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- sleep_quanta * MS_PER_SLEEP_QUANTUM,
- WAIT_EVENT_WAL_SUMMARIZER_WAL);
- ResetLatch(MyLatch);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 5a3cb8946526..18073590e9a0 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -54,6 +54,7 @@
#include "storage/bufmgr.h"
#include "storage/condition_variable.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/lwlock.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
@@ -222,12 +223,12 @@ WalWriterMain(char *startup_data, size_t startup_data_len)
/*
* Advertise whether we might hibernate in this cycle. We do this
- * before resetting the latch to ensure that any async commits will
- * see the flag set if they might possibly need to wake us up, and
- * that we won't miss any signal they send us. (If we discover work
- * to do in the last cycle before we would hibernate, the global flag
- * will be set unnecessarily, but little harm is done.) But avoid
- * touching the global flag if it doesn't need to change.
+ * before clearing the interrupt flag to ensure that any async commits
+ * will see the flag set if they might possibly need to wake us up,
+ * and that we won't miss any signal they send us. (If we discover
+ * work to do in the last cycle before we would hibernate, the global
+ * flag will be set unnecessarily, but little harm is done.) But
+ * avoid touching the global flag if it doesn't need to change.
*/
if (hibernating != (left_till_hibernate <= 1))
{
@@ -236,7 +237,7 @@ WalWriterMain(char *startup_data, size_t startup_data_len)
}
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
/* Process any signals received recently */
HandleMainLoopInterrupts();
@@ -263,9 +264,9 @@ WalWriterMain(char *startup_data, size_t startup_data_len)
else
cur_timeout = WalWriterDelay * HIBERNATE_FACTOR;
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- cur_timeout,
- WAIT_EVENT_WAL_WRITER_MAIN);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 c74369953f81..f8bd2b67d6a9 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -30,7 +30,7 @@
#include "pgstat.h"
#include "pqexpbuffer.h"
#include "replication/walreceiver.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/pg_lsn.h"
@@ -237,16 +237,16 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
else
io_flag = WL_SOCKET_WRITEABLE;
- rc = WaitLatchOrSocket(MyLatch,
- WL_EXIT_ON_PM_DEATH | WL_LATCH_SET | io_flag,
- PQsocket(conn->streamConn),
- 0,
- WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
+ rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL,
+ 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(1 << 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 e7f7d4c5e4bd..eb5dc3116e05 100644
--- a/src/backend/replication/logical/applyparallelworker.c
+++ b/src/backend/replication/logical/applyparallelworker.c
@@ -165,6 +165,7 @@
#include "replication/logicalworker.h"
#include "replication/origin.h"
#include "replication/worker_internal.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "tcop/tcopprot.h"
@@ -804,13 +805,13 @@ LogicalParallelApplyLoop(shm_mq_handle *mqh)
int rc;
/* Wait for more work. */
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- 1000L,
- WAIT_EVENT_LOGICAL_PARALLEL_APPLY_MAIN);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 +991,7 @@ HandleParallelApplyMessageInterrupt(void)
{
InterruptPending = true;
ParallelApplyMessagePending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -1182,14 +1183,14 @@ pa_send_data(ParallelApplyWorkerInfo *winfo, Size nbytes, const void *data)
Assert(result == SHM_MQ_WOULD_BLOCK);
/* Wait before retrying. */
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- SHM_SEND_RETRY_INTERVAL_MS,
- WAIT_EVENT_LOGICAL_APPLY_SEND_DATA);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 +1255,13 @@ pa_wait_for_xact_state(ParallelApplyWorkerInfo *winfo,
break;
/* Wait to be signalled. */
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- 10L,
- WAIT_EVENT_LOGICAL_PARALLEL_APPLY_STATE_CHANGE);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 8b1964204453..c4c16cdd72ce 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -34,6 +34,7 @@
#include "replication/slot.h"
#include "replication/walreceiver.h"
#include "replication/worker_internal.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -56,7 +57,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;
@@ -209,16 +210,17 @@ WaitForReplicationWorkerAttach(LogicalRepWorker *worker,
}
/*
- * We need timeout because we generally don't get notified via latch
- * about the worker attach. But we don't expect to have to wait long.
+ * We need timeout because we generally don't get notified via an
+ * interrupt about the worker attach. But we don't expect to have to
+ * wait long.
*/
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- 10L, WAIT_EVENT_BGWORKER_STARTUP);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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();
}
}
@@ -494,7 +496,6 @@ logicalrep_worker_launch(LogicalRepWorkerType wtype,
}
bgw.bgw_restart_time = BGW_NEVER_RESTART;
- bgw.bgw_notify_pid = MyProcPid;
bgw.bgw_main_arg = Int32GetDatum(slot);
if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
@@ -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(1 << 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(1 << 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));
}
/*
@@ -803,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;
}
/*
@@ -937,7 +938,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);
@@ -963,6 +963,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;
@@ -1108,8 +1109,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);
}
/*
@@ -1123,8 +1128,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);
@@ -1156,6 +1161,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)
{
@@ -1212,14 +1218,15 @@ ApplyLauncherMain(Datum main_arg)
MemoryContextDelete(subctx);
/* Wait for more work. */
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- wait_time,
- WAIT_EVENT_LOGICAL_LAUNCHER_MAIN);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL |
+ 1 << INTERRUPT_SUBSCRIPTION_CHANGE,
+ 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();
}
@@ -1239,7 +1246,7 @@ ApplyLauncherMain(Datum main_arg)
bool
IsLogicalLauncher(void)
{
- return LogicalRepCtx->launcher_pid == MyProcPid;
+ return LogicalRepCtx->launcher_procno == MyProcNumber;
}
/*
diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index f4f80b23129e..9b5f5bb15803 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -59,6 +59,7 @@
#include "replication/logical.h"
#include "replication/slotsync.h"
#include "replication/snapbuild.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
@@ -1252,13 +1253,13 @@ wait_for_slot_activity(bool some_slot_updated)
sleep_ms = MIN_SLOTSYNC_WORKER_NAPTIME_MS;
}
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- sleep_ms,
- WAIT_EVENT_REPLICATION_SLOTSYNC_MAIN);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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);
}
/*
@@ -1590,13 +1591,13 @@ ShutDownSlotSync(void)
int rc;
/* Wait a bit, we don't expect to have to wait long */
- rc = WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- 10L, WAIT_EVENT_REPLICATION_SLOTSYNC_SHUTDOWN);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 7c8a0e9cfebc..dff4f5cbc45b 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -111,6 +111,7 @@
#include "replication/slot.h"
#include "replication/walreceiver.h"
#include "replication/worker_internal.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
@@ -210,11 +211,11 @@ wait_for_relation_state_change(Oid relid, char expected_state)
if (!worker)
break;
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- 1000L, WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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(1 << 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(1 << 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 9e50c880f818..5ffdbd745f01 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -177,6 +177,7 @@
#include "replication/worker_internal.h"
#include "rewrite/rewriteHandler.h"
#include "storage/buffile.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "tcop/tcopprot.h"
@@ -3729,26 +3730,26 @@ LogicalRepApplyLoop(XLogRecPtr last_received)
break;
/*
- * Wait for more data or latch. If we have unflushed transactions,
- * wake up after WalWriterDelay to see if they've been flushed yet (in
- * which case we should send a feedback message). Otherwise, there's
- * no particular urgency about waking up unless we get data or a
- * signal.
+ * Wait for more data or interrupt. If we have unflushed
+ * transactions, wake up after WalWriterDelay to see if they've been
+ * flushed yet (in which case we should send a feedback message).
+ * Otherwise, there's no particular urgency about waking up unless we
+ * get data or a signal.
*/
if (!dlist_is_empty(&lsn_mapping))
wait_time = WalWriterDelay;
else
wait_time = NAPTIME_PER_CYCLE;
- rc = WaitLatchOrSocket(MyLatch,
- WL_SOCKET_READABLE | WL_LATCH_SET |
- WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- fd, wait_time,
- WAIT_EVENT_LOGICAL_APPLY_MAIN);
+ rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL,
+ 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 22a2c7fc4095..6bf11db1839b 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -81,6 +81,7 @@
#include "replication/syncrep.h"
#include "replication/walsender.h"
#include "replication/walsender_private.h"
+#include "storage/interrupt.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
#include "utils/guc_hooks.h"
@@ -222,22 +223,22 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
/*
* Wait for specified LSN to be confirmed.
*
- * Each proc has its own wait latch, so we perform a normal latch
+ * Each proc has its own wait interrupt vector, so we perform a normal
* check/wait loop here.
*/
for (;;)
{
int rc;
- /* Must reset the latch before testing state. */
- ResetLatch(MyLatch);
+ /* Must clear the interrupt flag before testing state. */
+ ClearInterrupt(INTERRUPT_GENERAL);
/*
- * Acquiring the lock is not needed, the latch ensures proper
- * barriers. If it looks like we're done, we must really be done,
- * because once walsender changes the state to SYNC_REP_WAIT_COMPLETE,
- * it will never update it again, so we can't be seeing a stale value
- * in that case.
+ * Acquiring the lock is not needed, the interrupt ensures proper
+ * barriers (FIXME: is that so?). If it looks like we're done, we must
+ * really be done, because once walsender changes the state to
+ * SYNC_REP_WAIT_COMPLETE, it will never update it again, so we can't
+ * be seeing a stale value in that case.
*/
if (MyProc->syncRepState == SYNC_REP_WAIT_COMPLETE)
break;
@@ -282,11 +283,13 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
}
/*
- * Wait on latch. Any condition that should wake us up will set the
- * latch, so no need for timeout.
+ * Wait on interrupt. Any condition that should wake us up will set
+ * the interrupt, so no need for timeout.
*/
- rc = WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
- WAIT_EVENT_SYNC_REP);
+ rc = WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 5f641d279057..3b7cd6290dfc 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -67,6 +67,7 @@
#include "postmaster/interrupt.h"
#include "replication/walreceiver.h"
#include "replication/walsender.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -146,17 +147,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 +537,25 @@ WalReceiverMain(char *startup_data, size_t startup_data_len)
/*
* Ideally we would reuse a WaitEventSet object repeatedly
- * here to avoid the overheads of WaitLatchOrSocket on epoll
- * systems, but we can't be sure that libpq (or any other
- * walreceiver implementation) has the same socket (even if
- * the fd is the same number, it may have been closed and
+ * here to avoid the overheads of WaitInterruptOrSocket on
+ * epoll systems, but we can't be sure that libpq (or any
+ * other walreceiver implementation) has the same socket (even
+ * if the fd is the same number, it may have been closed and
* reopened since the last time). In future, if there is a
* function for removing sockets from WaitEventSet, then we
* could add and remove just the socket each time, potentially
* avoiding some system calls.
*/
Assert(wait_fd != PGINVALID_SOCKET);
- rc = WaitLatchOrSocket(MyLatch,
- WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE |
- WL_TIMEOUT | WL_LATCH_SET,
- wait_fd,
- nap,
- WAIT_EVENT_WAL_RECEIVER_MAIN);
- if (rc & WL_LATCH_SET)
+ rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL,
+ 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)
@@ -692,7 +693,7 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
WakeupRecovery();
for (;;)
{
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
ProcessWalRcvInterrupts();
@@ -724,8 +725,10 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
}
SpinLockRelease(&walrcv->mutex);
- (void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
- WAIT_EVENT_WAL_RECEIVER_WAIT_START);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_EXIT_ON_PM_DEATH,
+ 0,
+ WAIT_EVENT_WAL_RECEIVER_WAIT_START);
}
if (update_process_title)
@@ -1366,7 +1369,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 8557d10cf9d2..cbe73b9dc7ca 100644
--- a/src/backend/replication/walreceiverfuncs.c
+++ b/src/backend/replication/walreceiverfuncs.c
@@ -26,6 +26,7 @@
#include "access/xlogrecovery.h"
#include "pgstat.h"
#include "replication/walreceiver.h"
+#include "storage/interrupt.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/shmem.h"
@@ -317,7 +318,7 @@ RequestXLogStreaming(TimeLineID tli, XLogRecPtr recptr, const char *conninfo,
if (launch)
SendPostmasterSignal(PMSIGNAL_START_WALRECEIVER);
else if (walrcv_proc != INVALID_PROC_NUMBER)
- SetLatch(&GetPGProcByNumber(walrcv_proc)->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, walrcv_proc);
}
/*
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index dc25dd6af917..33f10104e6e6 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -80,6 +80,7 @@
#include "replication/walsender_private.h"
#include "storage/condition_variable.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
@@ -1013,8 +1014,8 @@ StartReplication(StartReplicationCmd *cmd)
* XLogReaderRoutine->page_read callback for logical decoding contexts, as a
* walsender process.
*
- * Inside the walsender we can do better than read_local_xlog_page,
- * which has to do a plain sleep/busy loop, because the walsender's latch gets
+ * Inside the walsender we can do better than read_local_xlog_page, which
+ * has to do a plain sleep/busy loop, because the walsender's interrupt gets
* set every time WAL is flushed.
*/
static int
@@ -1611,7 +1612,7 @@ ProcessPendingWrites(void)
WAIT_EVENT_WAL_SENDER_WRITE_DATA);
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
@@ -1628,8 +1629,8 @@ ProcessPendingWrites(void)
WalSndShutdown();
}
- /* reactivate latch so WalSndLoop knows to continue */
- SetLatch(MyLatch);
+ /* reactivate interrupt so WalSndLoop knows to continue */
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
@@ -1817,7 +1818,7 @@ WalSndWaitForWal(XLogRecPtr loc)
long sleeptime;
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
@@ -1937,8 +1938,8 @@ WalSndWaitForWal(XLogRecPtr loc)
WalSndWait(wakeEvents, sleeptime, wait_event);
}
- /* reactivate latch so WalSndLoop knows to continue */
- SetLatch(MyLatch);
+ /* reactivate interrupt flag so WalSndLoop knows to continue */
+ RaiseInterrupt(INTERRUPT_GENERAL);
return RecentFlushPtr;
}
@@ -2756,7 +2757,7 @@ WalSndLoop(WalSndSendDataCallback send_data)
for (;;)
{
/* Clear any already-pending wakeups */
- ResetLatch(MyLatch);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
@@ -3555,7 +3556,7 @@ static void
WalSndLastCycleHandler(SIGNAL_ARGS)
{
got_SIGUSR2 = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/* Set up signal handlers */
@@ -3661,7 +3662,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
@@ -3673,8 +3674,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 2622221809cd..7551092b6a6a 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -51,6 +51,7 @@
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
@@ -2882,7 +2883,7 @@ UnpinBufferNoOwner(BufferDesc *buf)
buf_state &= ~BM_PIN_COUNT_WAITER;
UnlockBufHdr(buf, buf_state);
- ProcSendSignal(wait_backend_pgprocno);
+ SendInterrupt(INTERRUPT_GENERAL, wait_backend_pgprocno);
}
else
UnlockBufHdr(buf, buf_state);
@@ -5344,7 +5345,12 @@ LockBufferForCleanup(Buffer buffer)
SetStartupBufferPinWaitBufId(-1);
}
else
- ProcWaitForSignal(WAIT_EVENT_BUFFER_PIN);
+ {
+ WaitInterrupt(1 << INTERRUPT_GENERAL, 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 dffdd57e9b52..65b02f241c99 100644
--- a/src/backend/storage/buffer/freelist.c
+++ b/src/backend/storage/buffer/freelist.c
@@ -19,6 +19,7 @@
#include "port/atomics.h"
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
+#include "storage/interrupt.h"
#include "storage/proc.h"
#define INT_ACCESS_ONCE(var) ((int)(*((volatile int *)&(var))))
@@ -219,27 +220,25 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint32 *buf_state, bool *from_r
/*
* If asked, we need to waken the bgwriter. Since we don't want to rely on
* a spinlock for this we force a read from shared memory once, and then
- * set the latch based on that value. We need to go through that length
- * because otherwise bgwprocno might be reset while/after we check because
- * the compiler might just reread from memory.
+ * send the interrupt based on that value. We need to go through that
+ * length because otherwise bgwprocno might be reset while/after we check
+ * because the compiler might just reread from memory.
*
- * This can possibly set the latch of the wrong process if the bgwriter
- * dies in the wrong moment. But since PGPROC->procLatch is never
- * deallocated the worst consequence of that is that we set the latch of
- * some arbitrary process.
+ * This can possibly send the interrupt to the wrong process if the
+ * bgwriter dies in the wrong moment, but that's harmless.
*/
bgwprocno = INT_ACCESS_ONCE(StrategyControl->bgwprocno);
if (bgwprocno != -1)
{
- /* reset bgwprocno first, before setting the latch */
+ /* reset bgwprocno first, before sending the interrupt */
StrategyControl->bgwprocno = -1;
/*
- * Not acquiring ProcArrayLock here which is slightly icky. It's
- * actually fine because procLatch isn't ever freed, so we just can
- * potentially set the wrong process' (or no process') latch.
+ * Not acquiring ProcArrayLock here which is slightly icky, because we
+ * can potentially send the interrupt to the wrong process (or no
+ * process), but it's harmless.
*/
- SetLatch(&ProcGlobal->allProcs[bgwprocno].procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, 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 d8a1653eb6a6..5c7c72f902ac 100644
--- a/src/backend/storage/ipc/Makefile
+++ b/src/backend/storage/ipc/Makefile
@@ -13,9 +13,9 @@ OBJS = \
dsm.o \
dsm_impl.o \
dsm_registry.o \
+ interrupt.o \
ipc.o \
ipci.o \
- latch.o \
pmsignal.o \
procarray.o \
procsignal.o \
@@ -25,6 +25,7 @@ OBJS = \
signalfuncs.o \
sinval.o \
sinvaladt.o \
- standby.o
+ standby.o \
+ waiteventset.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/ipc/interrupt.c b/src/backend/storage/ipc/interrupt.c
new file mode 100644
index 000000000000..ec2fd6a3cfb6
--- /dev/null
+++ b/src/backend/storage/ipc/interrupt.c
@@ -0,0 +1,128 @@
+/*-------------------------------------------------------------------------
+ *
+ * interrupt.c
+ * Interrupt handling routines.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/storage/ipc/interrupt.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include
+
+#include "miscadmin.h"
+#include "port/atomics.h"
+#include "storage/interrupt.h"
+#include "storage/ipc.h"
+#include "storage/proc.h"
+#include "storage/procsignal.h"
+#include "storage/waiteventset.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+
+static pg_atomic_uint32 LocalPendingInterrupts;
+
+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(InterruptType reason)
+{
+ uint32 old_pending;
+
+ old_pending = pg_atomic_fetch_or_u32(MyPendingInterrupts, 1 << reason);
+
+ /*
+ * 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 & (1 << reason | 1 << SLEEPING_ON_INTERRUPTS)) == 1 << SLEEPING_ON_INTERRUPTS)
+ WakeupMyProc();
+}
+
+/*
+ * 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(InterruptType reason, 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, 1 << reason);
+
+ /*
+ * 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 & (1 << reason | 1 << SLEEPING_ON_INTERRUPTS)) == 1 << SLEEPING_ON_INTERRUPTS)
+ WakeupOtherProc(proc);
+}
diff --git a/src/backend/storage/ipc/meson.build b/src/backend/storage/ipc/meson.build
index 5a936171f734..4eba41b78af8 100644
--- a/src/backend/storage/ipc/meson.build
+++ b/src/backend/storage/ipc/meson.build
@@ -5,9 +5,9 @@ backend_sources += files(
'dsm.c',
'dsm_impl.c',
'dsm_registry.c',
+ 'interrupt.c',
'ipc.c',
'ipci.c',
- 'latch.c',
'pmsignal.c',
'procarray.c',
'procsignal.c',
@@ -18,5 +18,6 @@ backend_sources += files(
'sinval.c',
'sinvaladt.c',
'standby.c',
+ 'waiteventset.c',
)
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 87027f27eb7a..2efba9ee05f8 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -25,8 +25,8 @@
#include "replication/logicalworker.h"
#include "replication/walsender.h"
#include "storage/condition_variable.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/shmem.h"
#include "storage/sinval.h"
#include "storage/smgr.h"
@@ -481,7 +481,7 @@ HandleProcSignalBarrierInterrupt(void)
{
InterruptPending = true;
ProcSignalBarrierPending = true;
- /* latch will be set by procsignal_sigusr1_handler */
+ /* interrupt will be raised by procsignal_sigusr1_handler */
}
/*
@@ -712,7 +712,7 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
/*
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 9235fcd08ec2..de0a5221e3ff 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -4,7 +4,7 @@
* single-reader, single-writer shared memory message queue
*
* Both the sender and the receiver must have a PGPROC; their respective
- * process latches are used for synchronization. Only the sender may send,
+ * process interrupts are used for synchronization. Only the sender may send,
* and only the receiver may receive. This is intended to allow a user
* backend to communicate with worker backends that it has registered.
*
@@ -22,6 +22,7 @@
#include "pgstat.h"
#include "port/pg_bitutils.h"
#include "postmaster/bgworker.h"
+#include "storage/interrupt.h"
#include "storage/shm_mq.h"
#include "storage/spin.h"
#include "utils/memutils.h"
@@ -44,10 +45,11 @@
*
* mq_detached needs no locking. It can be set by either the sender or the
* receiver, but only ever from false to true, so redundant writes don't
- * matter. It is important that if we set mq_detached and then set the
- * counterparty's latch, the counterparty must be certain to see the change
- * after waking up. Since SetLatch begins with a memory barrier and ResetLatch
- * ends with one, this should be OK.
+ * matter. It is important that if we set mq_detached and then send the
+ * interrupt to the counterparty, the counterparty must be certain to see the
+ * change after waking up. Since SendInterrupt begins with a memory barrier
+ * and ClearInterrupt ends with one, this should be OK.
+ * XXX: No explicit memory barriers in SendIntterupt/ClearInterrupt, do we need to add some?
*
* mq_ring_size and mq_ring_offset never change after initialization, and
* can therefore be read without the lock.
@@ -112,7 +114,7 @@ struct shm_mq
* yet updated in the shared memory. We will not update it until the written
* data is 1/4th of the ring size or the tuple queue is full. This will
* prevent frequent CPU cache misses, and it will also avoid frequent
- * SetLatch() calls, which are quite expensive.
+ * SendInterrupt() calls, which are quite expensive.
*
* mqh_partial_bytes, mqh_expected_bytes, and mqh_length_word_complete
* are used to track the state of non-blocking operations. When the caller
@@ -214,7 +216,7 @@ shm_mq_set_receiver(shm_mq *mq, PGPROC *proc)
SpinLockRelease(&mq->mq_mutex);
if (sender != NULL)
- SetLatch(&sender->procLatch);
+ SendInterrupt(INTERRUPT_GENERAL, 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(1 << 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(1 << 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(1 << 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 d83532760079..2ac9f09f7acc 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -20,6 +20,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/syslogger.h"
+#include "storage/interrupt.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -204,12 +205,12 @@ pg_wait_until_termination(int pid, int64 timeout)
/* Process interrupts, if any, before waiting */
CHECK_FOR_INTERRUPTS();
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- waittime,
- WAIT_EVENT_BACKEND_TERMINATION);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 d9b16f84d19e..72630b27aa16 100644
--- a/src/backend/storage/ipc/sinval.c
+++ b/src/backend/storage/ipc/sinval.c
@@ -16,7 +16,7 @@
#include "access/xact.h"
#include "miscadmin.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
#include "storage/sinvaladt.h"
#include "utils/inval.h"
@@ -31,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 25267f0f85d1..73de96acc7a3 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -26,6 +26,7 @@
#include "pgstat.h"
#include "replication/slot.h"
#include "storage/bufmgr.h"
+#include "storage/interrupt.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "storage/sinvaladt.h"
@@ -594,7 +595,7 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
* ResolveRecoveryConflictWithLock is called from ProcSleep()
* to resolve conflicts with other backends holding relation locks.
*
- * The WaitLatch sleep normally done in ProcSleep()
+ * The WaitInterrupt sleep normally done in ProcSleep()
* (when not InHotStandby) is performed here, for code clarity.
*
* We either resolve conflicts immediately or set a timeout to wake us at
@@ -696,7 +697,10 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
}
/* Wait to be signaled by the release of the Relation Lock */
- ProcWaitForSignal(PG_WAIT_LOCK | locktag.locktag_type);
+ WaitInterrupt(1 << INTERRUPT_GENERAL, 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(1 << 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(1 << 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/latch.c b/src/backend/storage/ipc/waiteventset.c
similarity index 76%
rename from src/backend/storage/ipc/latch.c
rename to src/backend/storage/ipc/waiteventset.c
index f19ce5e7aff0..9d10bc5fcd87 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/waiteventset.c
@@ -1,17 +1,32 @@
/*-------------------------------------------------------------------------
*
- * latch.c
- * Routines for inter-process latches
+ * waiteventset.c
+ * ppoll()/pselect() like abstraction
*
- * The poll() implementation uses the so-called self-pipe trick to overcome the
- * race condition involved with poll() and setting a global flag in the signal
- * handler. When a latch is set and the current process is waiting for it, the
- * signal handler wakes up the poll() in WaitLatch by writing a byte to a pipe.
- * A signal by itself doesn't interrupt poll() on all platforms, and even on
- * platforms where it does, a signal that arrives just before the poll() call
- * does not prevent poll() from entering sleep. An incoming byte on a pipe
- * however reliably interrupts the sleep, and causes poll() to return
- * immediately even if the signal arrives before poll() begins.
+ * WaitEvents are an abstraction for waiting for one or more events at a time.
+ * The waiting can be done in a race free fashion, similar ppoll() or
+ * pselect() (as opposed to plain poll()/select()).
+ *
+ * You can wait for:
+ * - an interrupt from another process or from signal handler in the same
+ * process (WL_INTERRUPT)
+ * - data to become readable or writeable on a socket (WL_SOCKET_*)
+ * - postmaster death (WL_POSTMASTER_DEATH or WL_EXIT_ON_PM_DEATH)
+ * - timeout (WL_TIMEOUT)
+ *
+ * Implementation
+ * --------------
+ *
+ * The poll() implementation uses the so-called self-pipe trick to overcome
+ * the race condition involved with poll() and setting a global flag in the
+ * signal handler. When an interrupt is received and the current process is
+ * waiting for it, the signal handler wakes up the poll() in WaitInterrupt by
+ * writing a byte to a pipe. A signal by itself doesn't interrupt poll() on
+ * all platforms, and even on platforms where it does, a signal that arrives
+ * just before the poll() call does not prevent poll() from entering sleep. An
+ * incoming byte on a pipe however reliably interrupts the sleep, and causes
+ * poll() to return immediately even if the signal arrives before poll()
+ * begins.
*
* The epoll() implementation overcomes the race with a different technique: it
* keeps SIGURG blocked and consumes from a signalfd() descriptor instead. We
@@ -27,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * src/backend/storage/ipc/latch.c
+ * src/backend/storage/ipc/waiteventset.c
*
*-------------------------------------------------------------------------
*/
@@ -57,9 +72,11 @@
#include "portability/instr_time.h"
#include "postmaster/postmaster.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/pmsignal.h"
+#include "storage/proc.h"
+#include "storage/waiteventset.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
@@ -98,7 +115,7 @@
#endif
#endif
-/* typedef in latch.h */
+/* typedef in waiteventset.h */
struct WaitEventSet
{
ResourceOwner owner;
@@ -113,13 +130,13 @@ struct WaitEventSet
WaitEvent *events;
/*
- * If WL_LATCH_SET is specified in any wait event, latch is a pointer to
- * said latch, and latch_pos the offset in the ->events array. This is
- * useful because we check the state of the latch before performing doing
- * syscalls related to waiting.
+ * If WL_INTERRUPT is specified in any wait event, interruptMask is the
+ * interrupts to wait for, and interrupt_pos the offset in the ->events
+ * array. This is useful because we check the state of pending interrupts
+ * before performing doing syscalls related to waiting.
*/
- Latch *latch;
- int latch_pos;
+ uint32 interrupt_mask;
+ int interrupt_pos;
/*
* WL_EXIT_ON_PM_DEATH is converted to WL_POSTMASTER_DEATH, but this flag
@@ -151,14 +168,14 @@ struct WaitEventSet
#endif
};
-/* A common WaitEventSet used to implement WaitLatch() */
-static WaitEventSet *LatchWaitSet;
+/* A common WaitEventSet used to implement WaitInterrupt() */
+static WaitEventSet *InterruptWaitSet;
-/* The position of the latch in LatchWaitSet. */
-#define LatchWaitSetLatchPos 0
+/* The position of the interrupt in InterruptWaitSet. */
+#define InterruptWaitSetInterruptPos 0
#ifndef WIN32
-/* Are we currently in WaitLatch? The signal handler would like to know. */
+/* Are we currently in WaitInterrupt? The signal handler would like to know. */
static volatile sig_atomic_t waiting = false;
#endif
@@ -174,9 +191,16 @@ static int selfpipe_writefd = -1;
/* Process owning the self-pipe --- needed for checking purposes */
static int selfpipe_owner_pid = 0;
+#endif
+
+#ifdef WAIT_USE_WIN32
+static HANDLE LocalInterruptWakeupEvent;
+#endif
/* Private function prototypes */
-static void latch_sigurg_handler(SIGNAL_ARGS);
+
+#ifdef WAIT_USE_SELF_PIPE
+static void interrupt_sigurg_handler(SIGNAL_ARGS);
static void sendSelfPipeByte(void);
#endif
@@ -223,13 +247,13 @@ ResourceOwnerForgetWaitEventSet(ResourceOwner owner, WaitEventSet *set)
/*
- * Initialize the process-local latch infrastructure.
+ * Initialize the process-local wait event infrastructure.
*
* This must be called once during startup of any process that can wait on
- * latches, before it issues any InitLatch() or OwnLatch() calls.
+ * interrupts.
*/
void
-InitializeLatchSupport(void)
+InitializeWaitEventSupport(void)
{
#if defined(WAIT_USE_SELF_PIPE)
int pipefd[2];
@@ -276,12 +300,12 @@ InitializeLatchSupport(void)
/*
* Set up the self-pipe that allows a signal handler to wake up the
- * poll()/epoll_wait() in WaitLatch. Make the write-end non-blocking, so
- * that SetLatch won't block if the event has already been set many times
- * filling the kernel buffer. Make the read-end non-blocking too, so that
- * we can easily clear the pipe by reading until EAGAIN or EWOULDBLOCK.
- * Also, make both FDs close-on-exec, since we surely do not want any
- * child processes messing with them.
+ * poll()/epoll_wait() in WaitInterrupt. Make the write-end non-blocking,
+ * so that SendInterrupt won't block if the event has already been set
+ * many times filling the kernel buffer. Make the read-end non-blocking
+ * too, so that we can easily clear the pipe by reading until EAGAIN or
+ * EWOULDBLOCK. Also, make both FDs close-on-exec, since we surely do not
+ * want any child processes messing with them.
*/
if (pipe(pipefd) < 0)
elog(FATAL, "pipe() failed: %m");
@@ -302,7 +326,7 @@ InitializeLatchSupport(void)
ReserveExternalFD();
ReserveExternalFD();
- pqsignal(SIGURG, latch_sigurg_handler);
+ pqsignal(SIGURG, interrupt_sigurg_handler);
#endif
#ifdef WAIT_USE_SIGNALFD
@@ -340,37 +364,43 @@ InitializeLatchSupport(void)
/* Ignore SIGURG, because we'll receive it via kqueue. */
pqsignal(SIGURG, SIG_IGN);
#endif
+
+#ifdef WAIT_USE_WIN32
+ LocalInterruptWakeupEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (LocalInterruptWakeupEvent == NULL)
+ elog(ERROR, "CreateEvent failed: error code %lu", GetLastError());
+#endif
}
void
-InitializeLatchWaitSet(void)
+InitializeInterruptWaitSet(void)
{
- int latch_pos PG_USED_FOR_ASSERTS_ONLY;
+ int interrupt_pos PG_USED_FOR_ASSERTS_ONLY;
- Assert(LatchWaitSet == NULL);
+ Assert(InterruptWaitSet == NULL);
- /* Set up the WaitEventSet used by WaitLatch(). */
- LatchWaitSet = CreateWaitEventSet(NULL, 2);
- latch_pos = AddWaitEventToSet(LatchWaitSet, WL_LATCH_SET, PGINVALID_SOCKET,
- MyLatch, NULL);
+ /* Set up the WaitEventSet used by WaitInterrupt(). */
+ InterruptWaitSet = CreateWaitEventSet(NULL, 2);
+ interrupt_pos = AddWaitEventToSet(InterruptWaitSet, WL_INTERRUPT, PGINVALID_SOCKET,
+ 0, NULL);
if (IsUnderPostmaster)
- AddWaitEventToSet(LatchWaitSet, WL_EXIT_ON_PM_DEATH,
- PGINVALID_SOCKET, NULL, NULL);
+ AddWaitEventToSet(InterruptWaitSet, WL_EXIT_ON_PM_DEATH,
+ PGINVALID_SOCKET, 0, NULL);
- Assert(latch_pos == LatchWaitSetLatchPos);
+ Assert(interrupt_pos == InterruptWaitSetInterruptPos);
}
void
-ShutdownLatchSupport(void)
+ShutdownWaitEventSupport(void)
{
#if defined(WAIT_USE_POLL)
pqsignal(SIGURG, SIG_IGN);
#endif
- if (LatchWaitSet)
+ if (InterruptWaitSet)
{
- FreeWaitEventSet(LatchWaitSet);
- LatchWaitSet = NULL;
+ FreeWaitEventSet(InterruptWaitSet);
+ InterruptWaitSet = NULL;
}
#if defined(WAIT_USE_SELF_PIPE)
@@ -388,134 +418,24 @@ ShutdownLatchSupport(void)
}
/*
- * Initialize a process-local latch.
- */
-void
-InitLatch(Latch *latch)
-{
- latch->is_set = false;
- latch->maybe_sleeping = false;
- latch->owner_pid = MyProcPid;
- latch->is_shared = false;
-
-#if defined(WAIT_USE_SELF_PIPE)
- /* Assert InitializeLatchSupport has been called in this process */
- Assert(selfpipe_readfd >= 0 && selfpipe_owner_pid == MyProcPid);
-#elif defined(WAIT_USE_SIGNALFD)
- /* Assert InitializeLatchSupport has been called in this process */
- Assert(signal_fd >= 0);
-#elif defined(WAIT_USE_WIN32)
- latch->event = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (latch->event == NULL)
- elog(ERROR, "CreateEvent failed: error code %lu", GetLastError());
-#endif /* WIN32 */
-}
-
-/*
- * Initialize a shared latch that can be set from other processes. The latch
- * is initially owned by no-one; use OwnLatch to associate it with the
- * current process.
- *
- * InitSharedLatch needs to be called in postmaster before forking child
- * processes, usually right after allocating the shared memory block
- * containing the latch with ShmemInitStruct. (The Unix implementation
- * doesn't actually require that, but the Windows one does.) Because of
- * this restriction, we have no concurrency issues to worry about here.
- *
- * Note that other handles created in this module are never marked as
- * inheritable. Thus we do not need to worry about cleaning up child
- * process references to postmaster-private latches or WaitEventSets.
- */
-void
-InitSharedLatch(Latch *latch)
-{
-#ifdef WIN32
- SECURITY_ATTRIBUTES sa;
-
- /*
- * Set up security attributes to specify that the events are inherited.
- */
- ZeroMemory(&sa, sizeof(sa));
- sa.nLength = sizeof(sa);
- sa.bInheritHandle = TRUE;
-
- latch->event = CreateEvent(&sa, TRUE, FALSE, NULL);
- if (latch->event == NULL)
- elog(ERROR, "CreateEvent failed: error code %lu", GetLastError());
-#endif
-
- latch->is_set = false;
- latch->maybe_sleeping = false;
- latch->owner_pid = 0;
- latch->is_shared = true;
-}
-
-/*
- * Associate a shared latch with the current process, allowing it to
- * wait on the latch.
- *
- * Although there is a sanity check for latch-already-owned, we don't do
- * any sort of locking here, meaning that we could fail to detect the error
- * if two processes try to own the same latch at about the same time. If
- * there is any risk of that, caller must provide an interlock to prevent it.
- */
-void
-OwnLatch(Latch *latch)
-{
- int owner_pid;
-
- /* Sanity checks */
- Assert(latch->is_shared);
-
-#if defined(WAIT_USE_SELF_PIPE)
- /* Assert InitializeLatchSupport has been called in this process */
- Assert(selfpipe_readfd >= 0 && selfpipe_owner_pid == MyProcPid);
-#elif defined(WAIT_USE_SIGNALFD)
- /* Assert InitializeLatchSupport has been called in this process */
- Assert(signal_fd >= 0);
-#endif
-
- owner_pid = latch->owner_pid;
- if (owner_pid != 0)
- elog(PANIC, "latch already owned by PID %d", owner_pid);
-
- latch->owner_pid = MyProcPid;
-}
-
-/*
- * Disown a shared latch currently owned by the current process.
- */
-void
-DisownLatch(Latch *latch)
-{
- Assert(latch->is_shared);
- Assert(latch->owner_pid == MyProcPid);
-
- latch->owner_pid = 0;
-}
-
-/*
- * Wait for a given latch to be set, or for postmaster death, or until timeout
- * is exceeded. 'wakeEvents' is a bitmask that specifies which of those events
- * to wait for. If the latch is already set (and WL_LATCH_SET is given), the
- * function returns immediately.
+ * Wait for any of the interrupts in interruptMask to be set, or for
+ * postmaster death, or until timeout is exceeded. 'wakeEvents' is a bitmask
+ * that specifies which of those events to wait for. If the interrupt is
+ * already pending (and WL_INTERRUPT is given), the function returns
+ * immediately.
*
* The "timeout" is given in milliseconds. It must be >= 0 if WL_TIMEOUT flag
* is given. Although it is declared as "long", we don't actually support
* timeouts longer than INT_MAX milliseconds. Note that some extra overhead
* is incurred when WL_TIMEOUT is given, so avoid using a timeout if possible.
*
- * The latch must be owned by the current process, ie. it must be a
- * process-local latch initialized with InitLatch, or a shared latch
- * associated with the current process by calling OwnLatch.
- *
* Returns bit mask indicating which condition(s) caused the wake-up. Note
* that if multiple wake-up conditions are true, there is no guarantee that
* we return all of them in one call, but we will return at least one.
*/
int
-WaitLatch(Latch *latch, int wakeEvents, long timeout,
- uint32 wait_event_info)
+WaitInterrupt(uint32 interruptMask, int wakeEvents, long timeout,
+ uint32 wait_event_info)
{
WaitEvent event;
@@ -525,17 +445,17 @@ WaitLatch(Latch *latch, int wakeEvents, long timeout,
(wakeEvents & WL_POSTMASTER_DEATH));
/*
- * Some callers may have a latch other than MyLatch, or no latch at all,
- * or want to handle postmaster death differently. It's cheap to assign
- * those, so just do it every time.
+ * Some callers may have an interrupt mask different from last time, or no
+ * interrupt mask at all, or want to handle postmaster death differently.
+ * It's cheap to assign those, so just do it every time.
*/
- if (!(wakeEvents & WL_LATCH_SET))
- latch = NULL;
- ModifyWaitEvent(LatchWaitSet, LatchWaitSetLatchPos, WL_LATCH_SET, latch);
- LatchWaitSet->exit_on_postmaster_death =
+ if (!(wakeEvents & WL_INTERRUPT))
+ interruptMask = 0;
+ ModifyWaitEvent(InterruptWaitSet, InterruptWaitSetInterruptPos, WL_INTERRUPT, interruptMask);
+ InterruptWaitSet->exit_on_postmaster_death =
((wakeEvents & WL_EXIT_ON_PM_DEATH) != 0);
- if (WaitEventSetWait(LatchWaitSet,
+ if (WaitEventSetWait(InterruptWaitSet,
(wakeEvents & WL_TIMEOUT) ? timeout : -1,
&event, 1,
wait_event_info) == 0)
@@ -545,7 +465,7 @@ WaitLatch(Latch *latch, int wakeEvents, long timeout,
}
/*
- * Like WaitLatch, but with an extra socket argument for WL_SOCKET_*
+ * Like WaitInterrupt, but with an extra socket argument for WL_SOCKET_*
* conditions.
*
* When waiting on a socket, EOF and error conditions always cause the socket
@@ -558,12 +478,12 @@ WaitLatch(Latch *latch, int wakeEvents, long timeout,
* where some behavior other than immediate exit is needed.
*
* NB: These days this is just a wrapper around the WaitEventSet API. When
- * using a latch very frequently, consider creating a longer living
+ * using an interrupt very frequently, consider creating a longer living
* WaitEventSet instead; that's more efficient.
*/
int
-WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
- long timeout, uint32 wait_event_info)
+WaitInterruptOrSocket(uint32 interruptMask, int wakeEvents, pgsocket sock,
+ long timeout, uint32 wait_event_info)
{
int ret = 0;
int rc;
@@ -575,9 +495,9 @@ WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
else
timeout = -1;
- if (wakeEvents & WL_LATCH_SET)
- AddWaitEventToSet(set, WL_LATCH_SET, PGINVALID_SOCKET,
- latch, NULL);
+ if (wakeEvents & WL_INTERRUPT)
+ AddWaitEventToSet(set, WL_INTERRUPT, PGINVALID_SOCKET,
+ interruptMask, NULL);
/* Postmaster-managed callers must handle postmaster death somehow. */
Assert(!IsUnderPostmaster ||
@@ -586,18 +506,18 @@ WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
if ((wakeEvents & WL_POSTMASTER_DEATH) && IsUnderPostmaster)
AddWaitEventToSet(set, WL_POSTMASTER_DEATH, PGINVALID_SOCKET,
- NULL, NULL);
+ 0, NULL);
if ((wakeEvents & WL_EXIT_ON_PM_DEATH) && IsUnderPostmaster)
AddWaitEventToSet(set, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET,
- NULL, NULL);
+ 0, NULL);
if (wakeEvents & WL_SOCKET_MASK)
{
int ev;
ev = wakeEvents & WL_SOCKET_MASK;
- AddWaitEventToSet(set, ev, sock, NULL, NULL);
+ AddWaitEventToSet(set, ev, sock, 0, NULL);
}
rc = WaitEventSetWait(set, timeout, &event, 1, wait_event_info);
@@ -606,7 +526,7 @@ WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
ret |= WL_TIMEOUT;
else
{
- ret |= event.events & (WL_LATCH_SET |
+ ret |= event.events & (WL_INTERRUPT |
WL_POSTMASTER_DEATH |
WL_SOCKET_MASK);
}
@@ -617,125 +537,52 @@ WaitLatchOrSocket(Latch *latch, int wakeEvents, pgsocket sock,
}
/*
- * Sets a latch and wakes up anyone waiting on it.
- *
- * This is cheap if the latch is already set, otherwise not so much.
+ * Wake up my process if it's currently waiting.
*
- * NB: when calling this in a signal handler, be sure to save and restore
- * errno around it. (That's standard practice in most signal handlers, of
- * course, but we used to omit it in handlers that only set a flag.)
+ * NB: be sure to save and restore errno around it. (That's standard practice
+ * in most signal handlers, of course, but we used to omit it in handlers that
+ * only set a flag.) XXX
*
* NB: this function is called from critical sections and signal handlers so
* throwing an error is not a good idea.
*/
void
-SetLatch(Latch *latch)
+WakeupMyProc(void)
{
#ifndef WIN32
- pid_t owner_pid;
-#else
- HANDLE handle;
-#endif
-
- /*
- * The memory barrier has to be placed here to ensure that any flag
- * variables possibly changed by this process have been flushed to main
- * memory, before we check/set is_set.
- */
- pg_memory_barrier();
-
- /* Quick exit if already set */
- if (latch->is_set)
- return;
-
- latch->is_set = true;
-
- pg_memory_barrier();
- if (!latch->maybe_sleeping)
- return;
-
-#ifndef WIN32
-
- /*
- * See if anyone's waiting for the latch. It can be the current process if
- * we're in a signal handler. We use the self-pipe or SIGURG to ourselves
- * to wake up WaitEventSetWaitBlock() without races in that case. If it's
- * another process, send a signal.
- *
- * Fetch owner_pid only once, in case the latch is concurrently getting
- * owned or disowned. XXX: This assumes that pid_t is atomic, which isn't
- * guaranteed to be true! In practice, the effective range of pid_t fits
- * in a 32 bit integer, and so should be atomic. In the worst case, we
- * might end up signaling the wrong process. Even then, you're very
- * unlucky if a process with that bogus pid exists and belongs to
- * Postgres; and PG database processes should handle excess SIGUSR1
- * interrupts without a problem anyhow.
- *
- * Another sort of race condition that's possible here is for a new
- * process to own the latch immediately after we look, so we don't signal
- * it. This is okay so long as all callers of ResetLatch/WaitLatch follow
- * the standard coding convention of waiting at the bottom of their loops,
- * not the top, so that they'll correctly process latch-setting events
- * that happen before they enter the loop.
- */
- owner_pid = latch->owner_pid;
- if (owner_pid == 0)
- return;
- else if (owner_pid == MyProcPid)
- {
#if defined(WAIT_USE_SELF_PIPE)
- if (waiting)
- sendSelfPipeByte();
+ if (waiting)
+ sendSelfPipeByte();
#else
- if (waiting)
- kill(MyProcPid, SIGURG);
+ if (waiting)
+ kill(MyProcPid, SIGURG);
#endif
- }
- else
- kill(owner_pid, SIGURG);
-
#else
-
- /*
- * See if anyone's waiting for the latch. It can be the current process if
- * we're in a signal handler.
- *
- * Use a local variable here just in case somebody changes the event field
- * concurrently (which really should not happen).
- */
- handle = latch->event;
- if (handle)
- {
- SetEvent(handle);
-
- /*
- * Note that we silently ignore any errors. We might be in a signal
- * handler or other critical path where it's not safe to call elog().
- */
- }
+ SetEvent(MyProc ? MyProc->interruptWakeupEvent : LocalInterruptWakeupEvent);
#endif
}
/*
- * Clear the latch. Calling WaitLatch after this will sleep, unless
- * the latch is set again before the WaitLatch call.
+ * Wake up another process if it's currently waiting.
*/
void
-ResetLatch(Latch *latch)
+WakeupOtherProc(PGPROC *proc)
{
- /* Only the owner should reset the latch */
- Assert(latch->owner_pid == MyProcPid);
- Assert(latch->maybe_sleeping == false);
-
- latch->is_set = false;
+ /*
+ * 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
+ SetEvent(proc->interruptWakeupEvent);
/*
- * Ensure that the write to is_set gets flushed to main memory before we
- * examine any flag variables. Otherwise a concurrent SetLatch might
- * falsely conclude that it needn't signal us, even though we have missed
- * seeing some flag updates that SetLatch was supposed to inform us of.
+ * Note that we silently ignore any errors. We might be in a signal
+ * handler or other critical path where it's not safe to call elog().
*/
- pg_memory_barrier();
+#endif
}
/*
@@ -799,7 +646,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;
@@ -884,9 +732,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)
{
@@ -923,7 +771,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
@@ -941,10 +789,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.
@@ -954,7 +798,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;
@@ -968,19 +812,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 */
@@ -996,10 +837,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)
@@ -1033,14 +874,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)
@@ -1055,19 +896,19 @@ ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch)
#endif
/*
- * If neither the event mask nor the associated latch changes, return
- * early. That's an important optimization for some sockets, where
+ * If neither the event mask nor the associated interrupt mask changes,
+ * return early. That's an important optimization for some sockets, where
* ModifyWaitEvent is frequently used to switch from waiting for reads to
* waiting on writes.
*/
if (events == event->events &&
- (!(event->events & WL_LATCH_SET) || set->latch == latch))
+ (!(event->events & WL_INTERRUPT) || set->interrupt_mask == interruptMask))
return;
- if (event->events & WL_LATCH_SET &&
+ if (event->events & WL_INTERRUPT &&
events != event->events)
{
- elog(ERROR, "cannot modify latch event");
+ elog(ERROR, "cannot modify interrupts event");
}
if (event->events & WL_POSTMASTER_DEATH)
@@ -1078,25 +919,19 @@ ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch)
/* FIXME: validate event mask */
event->events = events;
- if (events == WL_LATCH_SET)
+ if (events == WL_INTERRUPT)
{
- if (latch && latch->owner_pid != MyProcPid)
- elog(ERROR, "cannot wait on a latch owned by another process");
- set->latch = latch;
+ set->interrupt_mask = interruptMask;
/*
- * On Unix, we don't need to modify the kernel object because the
- * underlying pipe (if there is one) is the same for all latches so we
- * can return immediately. On Windows, we need to update our array of
- * handles, but we leave the old one in place and tolerate spurious
- * wakeups if the latch is disabled.
+ * We don't bother to adjust the event when the interrupt mask
+ * changes. It means that when interrupt mask is set to 0, we will
+ * listen on the kernel object unnecessarily, and might get some
+ * spurious wakeups. The interrupt sending code should not 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)
@@ -1126,9 +961,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)
@@ -1175,9 +1009,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)
@@ -1239,9 +1072,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;
@@ -1267,8 +1100,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 |
@@ -1283,10 +1115,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
{
@@ -1364,10 +1196,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)
{
@@ -1423,6 +1258,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);
@@ -1444,63 +1280,77 @@ 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. We don't attempt to report any other events that
+ * might also be satisfied in that case.
+ *
+ * If someone sets the interrupt flag between this and the
+ * WaitEventSetWaitBlock() below, the setter will write a byte to the pipe
+ * (or signal us and the signal handler will do that), and the readiness
+ * routine will return immediately.
+ *
+ * On unix, If there's a pending byte in the self pipe, we'll notice
+ * whenever blocking. Only clearing the pipe in that case avoids having to
+ * drain it every time 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.".
+ */
+ if (set->interrupt_mask != 0)
{
- int rc;
+ uint32 old_mask;
+ bool already_pending = false;
/*
- * Check if the latch is set already. If so, leave the loop
- * immediately, avoid blocking again. We don't attempt to report any
- * other events that might also be satisfied.
- *
- * If someone sets the latch between this and the
- * 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;
-
- break;
+ /* we will fall through without sleeping */
}
+ }
+
+ while (returned_events == 0)
+ {
+ int rc;
/*
* Wait for events using the readiness primitive chosen at the top of
@@ -1510,12 +1360,6 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
rc = WaitEventSetWaitBlock(set, cur_timeout,
occurred_events, nevents);
- if (set->latch)
- {
- Assert(set->latch->maybe_sleeping);
- set->latch->maybe_sleeping = false;
- }
-
if (rc == -1)
break; /* timeout occurred */
else
@@ -1531,6 +1375,11 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
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
@@ -1601,16 +1450,16 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
occurred_events->user_data = cur_event->user_data;
occurred_events->events = 0;
- if (cur_event->events == WL_LATCH_SET &&
+ if (cur_event->events == WL_INTERRUPT &&
cur_epoll_event->events & (EPOLLIN | EPOLLERR | EPOLLHUP))
{
/* Drain the signalfd. */
drain();
- if (set->latch && set->latch->is_set)
+ if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
{
occurred_events->fd = PGINVALID_SOCKET;
- occurred_events->events = WL_LATCH_SET;
+ occurred_events->events = WL_INTERRUPT;
occurred_events++;
returned_events++;
}
@@ -1763,13 +1612,13 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
occurred_events->user_data = cur_event->user_data;
occurred_events->events = 0;
- if (cur_event->events == WL_LATCH_SET &&
+ if (cur_event->events == WL_INTERRUPT &&
cur_kqueue_event->filter == EVFILT_SIGNAL)
{
- if (set->latch && set->latch->is_set)
+ if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
{
occurred_events->fd = PGINVALID_SOCKET;
- occurred_events->events = WL_LATCH_SET;
+ occurred_events->events = WL_INTERRUPT;
occurred_events++;
returned_events++;
}
@@ -1885,16 +1734,16 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
occurred_events->user_data = cur_event->user_data;
occurred_events->events = 0;
- if (cur_event->events == WL_LATCH_SET &&
+ if (cur_event->events == WL_INTERRUPT &&
(cur_pollfd->revents & (POLLIN | POLLHUP | POLLERR | POLLNVAL)))
{
/* There's data in the self-pipe, clear it. */
drain();
- if (set->latch && set->latch->is_set)
+ if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
{
occurred_events->fd = PGINVALID_SOCKET;
- occurred_events->events = WL_LATCH_SET;
+ occurred_events->events = WL_INTERRUPT;
occurred_events++;
returned_events++;
}
@@ -1993,6 +1842,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
@@ -2098,19 +1956,15 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
occurred_events->user_data = cur_event->user_data;
occurred_events->events = 0;
- if (cur_event->events == WL_LATCH_SET)
+ if (cur_event->events == WL_INTERRUPT)
{
- /*
- * We cannot use set->latch->event to reset the fired event if we
- * aren't waiting on this latch now.
- */
if (!ResetEvent(set->handles[cur_event->pos + 1]))
elog(ERROR, "ResetEvent failed: error code %lu", GetLastError());
- if (set->latch && set->latch->is_set)
+ if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
{
occurred_events->fd = PGINVALID_SOCKET;
- occurred_events->events = WL_LATCH_SET;
+ occurred_events->events = WL_INTERRUPT;
occurred_events++;
returned_events++;
}
@@ -2155,7 +2009,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
@@ -2261,18 +2115,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)
{
@@ -2289,7 +2142,7 @@ sendSelfPipeByte(void)
/*
* If the pipe is full, we don't need to retry, the data that's there
- * already is enough to wake up WaitLatch.
+ * already is enough to wake up WaitInterrupt.
*/
if (errno == EAGAIN || errno == EWOULDBLOCK)
return;
diff --git a/src/backend/storage/lmgr/condition_variable.c b/src/backend/storage/lmgr/condition_variable.c
index 112a518bae07..59fc6466b905 100644
--- a/src/backend/storage/lmgr/condition_variable.c
+++ b/src/backend/storage/lmgr/condition_variable.c
@@ -21,6 +21,7 @@
#include "miscadmin.h"
#include "portability/instr_time.h"
#include "storage/condition_variable.h"
+#include "storage/interrupt.h"
#include "storage/proc.h"
#include "storage/proclist.h"
#include "storage/spin.h"
@@ -147,23 +148,23 @@ ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
INSTR_TIME_SET_CURRENT(start_time);
Assert(timeout >= 0 && timeout <= INT_MAX);
cur_timeout = timeout;
- wait_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
+ wait_events = WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
}
else
- wait_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
+ wait_events = WL_INTERRUPT | WL_EXIT_ON_PM_DEATH;
while (true)
{
bool done = false;
/*
- * Wait for latch to be set. (If we're awakened for some other
- * reason, the code below will cope anyway.)
+ * Wait for interrupt. (If we're awakened for some other reason, the
+ * code below will cope anyway.)
*/
- (void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL, 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 2030322f957a..a84071056a4d 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -207,6 +207,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "port/pg_lfind.h"
+#include "storage/interrupt.h"
#include "storage/predicate.h"
#include "storage/predicate_internals.h"
#include "storage/proc.h"
@@ -1576,7 +1577,10 @@ GetSafeSnapshot(Snapshot origSnapshot)
SxactIsROUnsafe(MySerializableXact)))
{
LWLockRelease(SerializableXactHashLock);
- ProcWaitForSignal(WAIT_EVENT_SAFE_SNAPSHOT);
+ WaitInterrupt(1 << INTERRUPT_GENERAL, 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 fd7077dfcdfa..5cf2d80da3d7 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -42,6 +42,7 @@
#include "replication/slotsync.h"
#include "replication/syncrep.h"
#include "storage/condition_variable.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/pmsignal.h"
@@ -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(1 << 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 ab7137d0fffc..bec42f40601b 100644
--- a/src/backend/storage/sync/sync.c
+++ b/src/backend/storage/sync/sync.c
@@ -27,7 +27,7 @@
#include "portability/instr_time.h"
#include "postmaster/bgwriter.h"
#include "storage/fd.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
#include "storage/md.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
@@ -611,8 +611,8 @@ RegisterSyncRequest(const FileTag *ftag, SyncRequestType type,
if (ret || (!ret && !retryOnError))
break;
- WaitLatch(NULL, WL_EXIT_ON_PM_DEATH | WL_TIMEOUT, 10,
- WAIT_EVENT_REGISTER_SYNC_REQUEST);
+ WaitInterrupt(0, WL_EXIT_ON_PM_DEATH | WL_TIMEOUT, 10,
+ WAIT_EVENT_REGISTER_SYNC_REQUEST);
}
return ret;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 85902788181d..897ce08f7426 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -61,6 +61,7 @@
#include "replication/walsender.h"
#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
@@ -519,15 +520,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 +554,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 +580,7 @@ ProcessClientWriteInterrupt(bool blocked)
}
}
else
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
errno = save_errno;
@@ -3010,12 +3011,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.
*/
@@ -3039,8 +3040,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 */
@@ -3066,7 +3067,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 1aa8bbb3063c..5482df143880 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -38,7 +38,7 @@
#include "postmaster/syslogger.h"
#include "rewrite/rewriteHandler.h"
#include "storage/fd.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -373,16 +373,16 @@ pg_sleep(PG_FUNCTION_ARGS)
float8 endtime;
/*
- * We sleep using WaitLatch, to ensure that we'll wake up promptly if an
- * important signal (such as SIGALRM or SIGINT) arrives. Because
- * WaitLatch's upper limit of delay is INT_MAX milliseconds, and the user
- * might ask for more than that, we sleep for at most 10 minutes and then
- * loop.
+ * We sleep using WaitInterrupt, to ensure that we'll wake up promptly if
+ * an important signal (such as SIGALRM or SIGINT) arrives. Because
+ * WaitInterrupt's upper limit of delay is INT_MAX milliseconds, and the
+ * user might ask for more than that, we sleep for at most 10 minutes and
+ * then loop.
*
* By computing the intended stop time initially, we avoid accumulation of
* extra delay across multiple sleeps. This also ensures we won't delay
- * less than the specified time when WaitLatch is terminated early by a
- * non-query-canceling signal such as SIGHUP.
+ * less than the specified time when WaitInterrupt is terminated early by
+ * a non-query-canceling signal such as SIGHUP.
*/
#define GetNowFloat() ((float8) GetCurrentTimestamp() / 1000000.0)
@@ -403,11 +403,11 @@ pg_sleep(PG_FUNCTION_ARGS)
else
break;
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- delay_ms,
- WAIT_EVENT_PG_SLEEP);
- ResetLatch(MyLatch);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ 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 1b33eb6ea8db..df4ee306fc5f 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 03a54451ac21..cb5343d28cae 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 6349abb8fb69..635c8f219c31 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -42,8 +42,8 @@
#include "postmaster/postmaster.h"
#include "replication/slotsync.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
-#include "storage/latch.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
@@ -66,8 +66,6 @@ BackendType MyBackendType;
/* List of lock files to be removed at proc exit */
static List *lock_files = NIL;
-static Latch LocalLatchData;
-
/* ----------------------------------------------------------------
* ignoring system indexes support stuff
*
@@ -126,10 +124,9 @@ InitPostmasterChild(void)
pqinitmask();
#endif
- /* Initialize process-local latch support */
- InitializeLatchSupport();
- InitProcessLocalLatch();
- InitializeLatchWaitSet();
+ /* Initialize process-local interrupt support */
+ InitializeWaitEventSupport();
+ InitializeInterruptWaitSet();
/*
* If possible, make this process a group leader, so that the postmaster
@@ -187,10 +184,9 @@ InitStandaloneProcess(const char *argv0)
InitProcessGlobals();
- /* Initialize process-local latch support */
- InitializeLatchSupport();
- InitProcessLocalLatch();
- InitializeLatchWaitSet();
+ /* Initialize process-local interrupt support */
+ InitializeWaitEventSupport();
+ InitializeInterruptWaitSet();
/*
* For consistency with InitPostmasterChild, initialize signal mask here.
@@ -211,48 +207,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 01c4016ced60..ad63d3d0a890 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -45,6 +45,7 @@
#include "replication/walsender.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
@@ -1362,7 +1363,7 @@ TransactionTimeoutHandler(void)
{
TransactionTimeoutPending = true;
InterruptPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
static void
@@ -1370,7 +1371,7 @@ IdleInTransactionSessionTimeoutHandler(void)
{
IdleInTransactionSessionTimeoutPending = true;
InterruptPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
static void
@@ -1378,7 +1379,7 @@ IdleSessionTimeoutHandler(void)
{
IdleSessionTimeoutPending = true;
InterruptPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
static void
@@ -1386,7 +1387,7 @@ IdleStatsUpdateTimeoutHandler(void)
{
IdleStatsUpdateTimeoutPending = true;
InterruptPending = true;
- SetLatch(MyLatch);
+ RaiseInterrupt(INTERRUPT_GENERAL);
}
static void
@@ -1394,7 +1395,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 ec7e570920a5..44b8ceabee2b 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -17,7 +17,7 @@
#include
#include "miscadmin.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
@@ -371,10 +371,10 @@ handle_sig_alarm(SIGNAL_ARGS)
HOLD_INTERRUPTS();
/*
- * SIGALRM is always cause for waking anything waiting on the process
- * latch.
+ * SIGALRM is always cause for waking anything waiting on
+ * INTERRUPT_GENERAL.
*/
- 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 70d33226cb9d..065ec37da6f8 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 69ffe5498f9e..7daec7ef8307 100644
--- a/src/include/access/parallel.h
+++ b/src/include/access/parallel.h
@@ -14,6 +14,8 @@
#ifndef PARALLEL_H
#define PARALLEL_H
+#include
+
#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 46a90dfb0222..b8819cec694d 100644
--- a/src/include/libpq/libpq-be-fe-helpers.h
+++ b/src/include/libpq/libpq-be-fe-helpers.h
@@ -43,7 +43,7 @@
#include "libpq-fe.h"
#include "miscadmin.h"
#include "storage/fd.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
#include "utils/timestamp.h"
#include "utils/wait_event.h"
@@ -177,8 +177,8 @@ libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
return;
/*
- * WaitLatchOrSocket() can conceivably fail, handle that case here instead
- * of requiring all callers to do so.
+ * WaitInterruptOrSocket() can conceivably fail, handle that case here
+ * instead of requiring all callers to do so.
*/
PG_TRY();
{
@@ -209,16 +209,16 @@ libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info)
else
io_flag = WL_SOCKET_WRITEABLE;
- rc = WaitLatchOrSocket(MyLatch,
- WL_EXIT_ON_PM_DEATH | WL_LATCH_SET | io_flag,
- PQsocket(conn),
- 0,
- wait_event_info);
+ rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL,
+ 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(1 << 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(1 << 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 07e5b12536b6..433fba9bd95c 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 e4c0d1481e90..e6895a619cf2 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/bgworker.h b/src/include/postmaster/bgworker.h
index 22fc49ec27f4..8ad4a12728af 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 f55adc85efcb..db30e3fd6a8b 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(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
diff --git a/src/include/storage/interrupt.h b/src/include/storage/interrupt.h
new file mode 100644
index 000000000000..39815bf48851
--- /dev/null
+++ b/src/include/storage/interrupt.h
@@ -0,0 +1,174 @@
+/*-------------------------------------------------------------------------
+ *
+ * interrupt.h
+ * Interrupt handling routines.
+ *
+ * "Interrupts" are a set of flags that represent conditions that should be
+ * handled at a later time. They are roughly analogous to Unix signals,
+ * except that they are handled cooperatively by checking for them at many
+ * points in the code.
+ *
+ * Interrupt flags can be "raised" synchronously by code that wants to defer
+ * an action, or asynchronously by timer signal handlers, other signal
+ * handlers or "sent" by other backends setting them directly.
+ *
+ * Most code currently deals with the INTERRUPT_GENERAL 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(1 << 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(1 << 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
+ * src/include/storage/interrupt.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef STORAGE_INTERRUPT_H
+#define STORAGE_INTERRUPT_H
+
+#include "port/atomics.h"
+#include "storage/procnumber.h"
+#include "storage/waiteventset.h"
+
+#include
+
+extern PGDLLIMPORT pg_atomic_uint32 *MyPendingInterrupts;
+
+/*
+ * 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 = 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,
+
+ /*
+ * 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,
+
+ /* sent to logical replication launcher, when a subscription changes */
+ INTERRUPT_SUBSCRIPTION_CHANGE,
+} InterruptType;
+
+/*
+ * Test an interrupt flag.
+ */
+static inline bool
+InterruptIsPending(InterruptType reason)
+{
+ return (pg_atomic_read_u32(MyPendingInterrupts) & (1 << reason)) != 0;
+}
+
+/*
+ * Test an interrupt flag.
+ */
+static inline bool
+InterruptsPending(uint32 mask)
+{
+ return (pg_atomic_read_u32(MyPendingInterrupts) & (mask)) != 0;
+}
+
+/*
+ * Clear an interrupt flag.
+ */
+static inline void
+ClearInterrupt(InterruptType reason)
+{
+ pg_atomic_fetch_and_u32(MyPendingInterrupts, ~(1 << reason));
+}
+
+/*
+ * Test and clear an interrupt flag.
+ */
+static inline bool
+ConsumeInterrupt(InterruptType reason)
+{
+ if (likely(!InterruptIsPending(reason)))
+ return false;
+
+ ClearInterrupt(reason);
+
+ return true;
+}
+
+extern void RaiseInterrupt(InterruptType reason);
+extern void SendInterrupt(InterruptType reason, ProcNumber pgprocno);
+extern int WaitInterrupt(uint32 interruptMask, int wakeEvents, long timeout,
+ uint32 wait_event_info);
+extern int WaitInterruptOrSocket(uint32 interruptMask, int wakeEvents, pgsocket sock,
+ long timeout, uint32 wait_event_info);
+extern void SwitchToLocalInterrupts(void);
+extern void SwitchToSharedInterrupts(void);
+extern void InitializeInterruptWaitSet(void);
+
+#endif
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
deleted file mode 100644
index 7e194d536f05..000000000000
--- a/src/include/storage/latch.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * latch.h
- * Routines for interprocess latches
- *
- * A latch is a boolean variable, with operations that let processes sleep
- * until it is set. A latch can be set from another process, or a signal
- * handler within the same process.
- *
- * The latch interface is a reliable replacement for the common pattern of
- * using pg_usleep() or select() to wait until a signal arrives, where the
- * signal handler sets a flag variable. Because on some platforms an
- * incoming signal doesn't interrupt sleep, and even on platforms where it
- * does there is a race condition if the signal arrives just before
- * entering the sleep, the common pattern must periodically wake up and
- * poll the flag variable. The pselect() system call was invented to solve
- * this problem, but it is not portable enough. Latches are designed to
- * overcome these limitations, allowing you to sleep without polling and
- * ensuring quick response to signals from other processes.
- *
- * There are two kinds of latches: local and shared. A local latch is
- * initialized by InitLatch, and can only be set from the same process.
- * A local latch can be used to wait for a signal to arrive, by calling
- * SetLatch in the signal handler. A shared latch resides in shared memory,
- * and must be initialized at postmaster startup by InitSharedLatch. Before
- * a shared latch can be waited on, it must be associated with a process
- * with OwnLatch. Only the process owning the latch can wait on it, but any
- * process can set it.
- *
- * There are three basic operations on a latch:
- *
- * SetLatch - Sets the latch
- * ResetLatch - Clears the latch, allowing it to be set again
- * WaitLatch - Waits for the latch to become set
- *
- * WaitLatch includes a provision for timeouts (which should be avoided
- * when possible, as they incur extra overhead) and a provision for
- * postmaster child processes to wake up immediately on postmaster death.
- * See latch.c for detailed specifications for the exported functions.
- *
- * The correct pattern to wait for event(s) is:
- *
- * for (;;)
- * {
- * ResetLatch();
- * if (work to do)
- * Do Stuff();
- * WaitLatch();
- * }
- *
- * It's important to reset the latch *before* checking if there's work to
- * do. Otherwise, if someone sets the latch between the check and the
- * ResetLatch call, you will miss it and Wait will incorrectly block.
- *
- * Another valid coding pattern looks like:
- *
- * for (;;)
- * {
- * if (work to do)
- * Do Stuff(); // in particular, exit loop if some condition satisfied
- * WaitLatch();
- * ResetLatch();
- * }
- *
- * This is useful to reduce latch traffic if it's expected that the loop's
- * termination condition will often be satisfied in the first iteration;
- * the cost is an extra loop iteration before blocking when it is not.
- * What must be avoided is placing any checks for asynchronous events after
- * WaitLatch and before ResetLatch, as that creates a race condition.
- *
- * To wake up the waiter, you must first set a global flag or something
- * else that the wait loop tests in the "if (work to do)" part, and call
- * SetLatch *after* that. SetLatch is designed to return quickly if the
- * latch is already set.
- *
- * On some platforms, signals will not interrupt the latch wait primitive
- * by themselves. Therefore, it is critical that any signal handler that
- * is meant to terminate a WaitLatch wait calls SetLatch.
- *
- * Note that use of the process latch (PGPROC.procLatch) is generally better
- * than an ad-hoc shared latch for signaling auxiliary processes. This is
- * because generic signal handlers will call SetLatch on the process latch
- * only, so using any latch other than the process latch effectively precludes
- * use of any generic handler.
- *
- *
- * WaitEventSets allow to wait for latches being set and additional events -
- * postmaster dying and socket readiness of several sockets currently - at the
- * same time. On many platforms using a long lived event set is more
- * efficient than using WaitLatch or WaitLatchOrSocket.
- *
- *
- * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * src/include/storage/latch.h
- *
- *-------------------------------------------------------------------------
- */
-#ifndef LATCH_H
-#define LATCH_H
-
-#include
-
-#include "utils/resowner.h"
-
-/*
- * Latch structure should be treated as opaque and only accessed through
- * the public functions. It is defined here to allow embedding Latches as
- * part of bigger structs.
- */
-typedef struct Latch
-{
- sig_atomic_t is_set;
- sig_atomic_t maybe_sleeping;
- bool is_shared;
- int owner_pid;
-#ifdef WIN32
- HANDLE event;
-#endif
-} Latch;
-
-/*
- * Bitmasks for events that may wake-up WaitLatch(), WaitLatchOrSocket(), or
- * WaitEventSetWait().
- */
-#define WL_LATCH_SET (1 << 0)
-#define WL_SOCKET_READABLE (1 << 1)
-#define WL_SOCKET_WRITEABLE (1 << 2)
-#define WL_TIMEOUT (1 << 3) /* not for WaitEventSetWait() */
-#define WL_POSTMASTER_DEATH (1 << 4)
-#define WL_EXIT_ON_PM_DEATH (1 << 5)
-#ifdef WIN32
-#define WL_SOCKET_CONNECTED (1 << 6)
-#else
-/* avoid having to deal with case on platforms not requiring it */
-#define WL_SOCKET_CONNECTED WL_SOCKET_WRITEABLE
-#endif
-#define WL_SOCKET_CLOSED (1 << 7)
-#ifdef WIN32
-#define WL_SOCKET_ACCEPT (1 << 8)
-#else
-/* avoid having to deal with case on platforms not requiring it */
-#define WL_SOCKET_ACCEPT WL_SOCKET_READABLE
-#endif
-#define WL_SOCKET_MASK (WL_SOCKET_READABLE | \
- WL_SOCKET_WRITEABLE | \
- WL_SOCKET_CONNECTED | \
- WL_SOCKET_ACCEPT | \
- WL_SOCKET_CLOSED)
-
-typedef struct WaitEvent
-{
- int pos; /* position in the event data structure */
- uint32 events; /* triggered events */
- pgsocket fd; /* socket fd associated with event */
- void *user_data; /* pointer provided in AddWaitEventToSet */
-#ifdef WIN32
- bool reset; /* Is reset of the event required? */
-#endif
-} WaitEvent;
-
-/* forward declaration to avoid exposing latch.c implementation details */
-typedef struct WaitEventSet WaitEventSet;
-
-/*
- * prototypes for functions in latch.c
- */
-extern void InitializeLatchSupport(void);
-extern void InitLatch(Latch *latch);
-extern void InitSharedLatch(Latch *latch);
-extern void OwnLatch(Latch *latch);
-extern void DisownLatch(Latch *latch);
-extern void SetLatch(Latch *latch);
-extern void ResetLatch(Latch *latch);
-extern void ShutdownLatchSupport(void);
-
-extern WaitEventSet *CreateWaitEventSet(ResourceOwner resowner, int nevents);
-extern void FreeWaitEventSet(WaitEventSet *set);
-extern void FreeWaitEventSetAfterFork(WaitEventSet *set);
-extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
- Latch *latch, void *user_data);
-extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
-
-extern int WaitEventSetWait(WaitEventSet *set, long timeout,
- WaitEvent *occurred_events, int nevents,
- uint32 wait_event_info);
-extern int WaitLatch(Latch *latch, int wakeEvents, long timeout,
- uint32 wait_event_info);
-extern int WaitLatchOrSocket(Latch *latch, int wakeEvents,
- pgsocket sock, long timeout, uint32 wait_event_info);
-extern void InitializeLatchWaitSet(void);
-extern int GetNumRegisteredWaitEvents(WaitEventSet *set);
-extern bool WaitEventSetCanReportClosed(void);
-
-#endif /* LATCH_H */
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 0b1fa61310f7..e6f9cc15e6d8 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -17,7 +17,6 @@
#include "access/clog.h"
#include "access/xlogdefs.h"
#include "lib/ilist.h"
-#include "storage/latch.h"
#include "storage/lock.h"
#include "storage/pg_sema.h"
#include "storage/proclist_types.h"
@@ -172,9 +171,6 @@ struct PGPROC
PGSemaphore sem; /* ONE semaphore to sleep on */
ProcWaitStatus waitStatus;
- Latch procLatch; /* generic latch for process */
-
-
TransactionId xid; /* id of top-level transaction currently being
* executed by this proc, if running and XID
* is assigned; else InvalidTransactionId.
@@ -310,6 +306,14 @@ struct PGPROC
PGPROC *lockGroupLeader; /* lock group leader, if I'm a member */
dlist_head lockGroupMembers; /* list of members, if I'm a leader */
dlist_node lockGroupLink; /* my member link, if I'm a member */
+
+ pg_atomic_uint32 pendingInterrupts;
+ pg_atomic_uint32 maybeSleepingOnInterrupts;
+
+#ifdef WIN32
+ /* Event handle to wake up the process when sending an interrupt */
+ HANDLE interruptWakeupEvent;
+#endif
};
/* NOTE: "typedef struct PGPROC PGPROC" appears in storage/lock.h. */
@@ -425,6 +429,8 @@ typedef struct PROC_HDR
*/
ProcNumber walwriterProc;
ProcNumber checkpointerProc;
+ ProcNumber walreceiverProc;
+ ProcNumber startupProc;
/* Current shared estimate of appropriate spins_per_delay value */
int spins_per_delay;
@@ -502,9 +508,6 @@ extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
extern void CheckDeadLockAlert(void);
extern void LockErrorCleanup(void);
-extern void ProcWaitForSignal(uint32 wait_event_info);
-extern void ProcSendSignal(ProcNumber procNumber);
-
extern PGPROC *AuxiliaryPidGetProc(int pid);
extern void BecomeLockGroupLeader(void);
diff --git a/src/include/storage/waiteventset.h b/src/include/storage/waiteventset.h
new file mode 100644
index 000000000000..ed34ff8b63ec
--- /dev/null
+++ b/src/include/storage/waiteventset.h
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------
+ *
+ * waiteventset.h
+ * ppoll() / pselect() like interface for waiting for events
+ *
+ * This is a reliable replacement for the common pattern of using pg_usleep()
+ * or select() to wait until a signal arrives, where the signal handler raises
+ * an interrupt (see storage/interrupt.h). Because on some platforms an
+ * incoming signal doesn't interrupt sleep, and even on platforms where it
+ * does there is a race condition if the signal arrives just before entering
+ * the sleep, the common pattern must periodically wake up and poll the flag
+ * variable. The pselect() system call was invented to solve this problem, but
+ * it is not portable enough. WaitEventSets and Interrupts are designed to
+ * overcome these limitations, allowing you to sleep without polling and
+ * ensuring quick response to signals from other processes.
+ *
+ * WaitInterrupt includes a provision for timeouts (which should be avoided
+ * when possible, as they incur extra overhead) and a provision for postmaster
+ * child processes to wake up immediately on postmaster death. See
+ * storage/ipc/interrupt.c for detailed specifications for the exported
+ * functions.
+ *
+ * On some platforms, signals will not interrupt the wait primitive by
+ * themselves. Therefore, it is critical that any signal handler that is
+ * meant to terminate a WaitInterrupt wait calls RaiseInterrupt.
+ *
+ * WaitEventSets allow to wait for interrupts being set and additional events -
+ * postmaster dying and socket readiness of several sockets currently - at the
+ * same time. On many platforms using a long lived event set is more
+ * efficient than using WaitInterrupt or WaitInterruptOrSocket.
+ *
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/waiteventset.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef WAITEVENTSET_H
+#define WAITEVENTSET_H
+
+#include
+
+#include "storage/procnumber.h"
+#include "utils/resowner.h"
+
+/*
+ * Bitmasks for events that may wake-up WaitInterrupt(),
+ * WaitInterruptOrSocket(), or WaitEventSetWait().
+ */
+#define WL_INTERRUPT (1 << 0)
+#define WL_SOCKET_READABLE (1 << 1)
+#define WL_SOCKET_WRITEABLE (1 << 2)
+#define WL_TIMEOUT (1 << 3) /* not for WaitEventSetWait() */
+#define WL_POSTMASTER_DEATH (1 << 4)
+#define WL_EXIT_ON_PM_DEATH (1 << 5)
+#ifdef WIN32
+#define WL_SOCKET_CONNECTED (1 << 6)
+#else
+/* avoid having to deal with case on platforms not requiring it */
+#define WL_SOCKET_CONNECTED WL_SOCKET_WRITEABLE
+#endif
+#define WL_SOCKET_CLOSED (1 << 7)
+#ifdef WIN32
+#define WL_SOCKET_ACCEPT (1 << 8)
+#else
+/* avoid having to deal with case on platforms not requiring it */
+#define WL_SOCKET_ACCEPT WL_SOCKET_READABLE
+#endif
+#define WL_SOCKET_MASK (WL_SOCKET_READABLE | \
+ WL_SOCKET_WRITEABLE | \
+ WL_SOCKET_CONNECTED | \
+ WL_SOCKET_ACCEPT | \
+ WL_SOCKET_CLOSED)
+
+typedef struct WaitEvent
+{
+ int pos; /* position in the event data structure */
+ uint32 events; /* triggered events */
+ pgsocket fd; /* socket fd associated with event */
+ void *user_data; /* pointer provided in AddWaitEventToSet */
+#ifdef WIN32
+ bool reset; /* Is reset of the event required? */
+#endif
+} WaitEvent;
+
+/* forward declaration to avoid exposing interrupt.c implementation details */
+typedef struct WaitEventSet WaitEventSet;
+
+struct PGPROC;
+
+/*
+ * prototypes for functions in waiteventset.c
+ */
+extern void InitializeWaitEventSupport(void);
+extern void ShutdownWaitEventSupport(void);
+#ifdef WIN32
+extern HANDLE CreateSharedWakeupHandle(void);
+#endif
+
+extern WaitEventSet *CreateWaitEventSet(ResourceOwner resowner, int nevents);
+extern void FreeWaitEventSet(WaitEventSet *set);
+extern void FreeWaitEventSetAfterFork(WaitEventSet *set);
+extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
+ uint32 interruptMask, void *user_data);
+extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, uint32 interruptMask);
+
+extern int WaitEventSetWait(WaitEventSet *set, long timeout,
+ WaitEvent *occurred_events, int nevents,
+ uint32 wait_event_info);
+extern void InitializeInterruptWaitSet(void);
+extern int GetNumRegisteredWaitEvents(WaitEventSet *set);
+extern bool WaitEventSetCanReportClosed(void);
+
+/* low level functions used to implement SendInterrupt */
+extern void WakeupMyProc(void);
+extern void WakeupOtherProc(struct PGPROC *proc);
+
+#endif /* WAITEVENTSET_H */
diff --git a/src/port/pgsleep.c b/src/port/pgsleep.c
index 1284458bfce3..9d8ef261297d 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 e01c0c9de936..03078078893d 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 fb235604394e..8c81c2b4d316 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -18,6 +18,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/bgworker.h"
+#include "storage/interrupt.h"
#include "storage/shm_toc.h"
#include "test_shm_mq.h"
#include "utils/memutils.h"
@@ -133,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;
@@ -222,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)
@@ -285,11 +285,11 @@ wait_for_workers_to_become_ready(worker_state *wstate,
we_bgworker_startup = WaitEventExtensionNew("TestShmMqBgWorkerStartup");
/* Wait to be signaled. */
- (void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
- we_bgworker_startup);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL, 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 3d235568b81d..c3efac5a8fa7 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -16,6 +16,7 @@
#include "fmgr.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "storage/interrupt.h"
#include "varatt.h"
#include "test_shm_mq.h"
@@ -234,13 +235,13 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
/*
* If we made no progress, wait for one of the other processes to
- * which we are connected to set our latch, indicating that they
- * have read or written data and therefore there may now be work
- * for us to do.
+ * which we are connected to send us an interrupt, indicating that
+ * they have read or written data and therefore there may now be
+ * work for us to do.
*/
- (void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,
- we_message_queue);
- ResetLatch(MyLatch);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL, 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/test_shm_mq.h b/src/test/modules/test_shm_mq/test_shm_mq.h
index 0ae7bd64cd0b..7f27a61aff86 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 6c4fbc782747..e5d353d11b3b 100644
--- a/src/test/modules/test_shm_mq/worker.c
+++ b/src/test/modules/test_shm_mq/worker.c
@@ -20,6 +20,7 @@
#include "postgres.h"
#include "miscadmin.h"
+#include "storage/interrupt.h"
#include "storage/ipc.h"
#include "storage/procarray.h"
#include "storage/shm_mq.h"
@@ -52,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.
@@ -122,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);
- }
- SetLatch(®istrant->procLatch);
+ 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 cf5b7505ec7c..61edf861a885 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -5,7 +5,7 @@
* patterns: establishing a database connection; starting and committing
* transactions; using GUC variables, and heeding SIGHUP to reread
* the configuration file; reporting to pg_stat_activity; using the
- * process latch to sleep and exit in case of postmaster death.
+ * WaitInterrupt to sleep and exit in case of postmaster death.
*
* This code connects to a database, creates a schema and table, and summarizes
* the numbers contained therein. To see it working, insert an initial value
@@ -26,7 +26,7 @@
#include "miscadmin.h"
#include "postmaster/bgworker.h"
#include "postmaster/interrupt.h"
-#include "storage/latch.h"
+#include "storage/interrupt.h"
/* these headers are used by this particular worker's code */
#include "access/xact.h"
@@ -213,15 +213,15 @@ worker_spi_main(Datum main_arg)
/*
* Background workers mustn't call usleep() or any direct equivalent:
- * instead, they may wait on their process latch, which sleeps as
- * necessary, but is awakened if postmaster dies. That way the
- * background process goes away immediately in an emergency.
+ * instead, they may use WaitInterrupt, which sleeps as necessary, but
+ * is awakened if postmaster dies. That way the background process
+ * goes away immediately in an emergency.
*/
- (void) WaitLatch(MyLatch,
- WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
- worker_spi_naptime * 1000L,
- worker_spi_wait_event_main);
- ResetLatch(MyLatch);
+ (void) WaitInterrupt(1 << INTERRUPT_GENERAL,
+ WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+ worker_spi_naptime * 1000L,
+ worker_spi_wait_event_main);
+ ClearInterrupt(INTERRUPT_GENERAL);
CHECK_FOR_INTERRUPTS();
@@ -366,7 +366,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.
@@ -415,8 +414,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);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e1c4f913f848..42521ad821d3 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1256,6 +1256,7 @@ Integer
IntegerSet
InternalDefaultACL
InternalGrant
+InterruptType
Interval
IntervalAggState
IntoClause
@@ -1494,7 +1495,6 @@ LZ4State
LabelProvider
LagTracker
LargeObjectDesc
-Latch
LauncherLastStartTimesEntry
LerpFunc
LexDescr