From ef7c1b386aea1c41d2fd509a6962e7d8a954a7b5 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Wed, 5 Mar 2025 20:35:36 +0200 Subject: [PATCH v6 5/5] Replace ProcSignals and other ad hoc signals with interrupts This replaces the INTERRUPT_GENERAL, or setting the process's latch before that, with more fine-grained interrupts. All the ProcSignals are converted into interrupts, as well as things like timers and config-reload requests. Most callers of WaitInterrupt now use INTERRUPT_CFI_MASK, which is a bitmask that includes all the interrupts that are handled by CHECK_FOR_INTERRUPTS(). CHECK_FOR_INTERRUPTS() now clears the interrupts bits that it processes, the caller is no longer required to do ClearInterrupt (or ResetLatch) for those bits. If you wake up for other interrupts that are not handled by CHECK_FOR_INTERRUPTS(), you still need to clear those. Now that there are separate bits for different things, it's possible to do e.g. WaitInterrupt(INTERRUPT_CONFIG_RELOAD | INTERRUPT_QUERY_CANCEL) to wait just for a config reload request or query cancellation, and not wake up on other events like a shutdown request. That's not very useful in practice, as CHECK_FOR_INTERRUPTS() still processes those interrupts if you call it, and you would want to process all the standard interrupts promptly anyway. It might be useful to check for specific interrupts with InterruptPending(), without immediately processing them, however. spgdoinsert() does something like that. In this commit I didn't change its overall logic, but it probably could be made more fine-grained now. More importantly, you can now have additional interrupts that are processed less frequently and do not wake up the backend when it's not prepared to process them. For example, the SINVAL_CATCHUP interrupt is only processed when the backend is idle. Previously, a catchup request would interrupt the process when it was not idle but was waiting on other things; now it only wakes up when the backend is idle and is ready to process it. That's not too significant for catchup interrupts because they don't occur that often anyway for a few extra wakeups to matter, but it's still nice and could be more significant with other interrupts. There's still a general-purpose INTERRUPT_GENERAL interrupt that is multiplexed for many different purposes in different processes, for example to wake up the walwriter when it has work to do, or to wake up processes waiting on a condition variable. The common property of those uses is that there's some other flag or condition that is checked on each wakeup, the wakeup interrupt itself means merely that something interesting might've happened. TODO: * ShutdownRequestPending became INTERRUPT_SHUTDOWN_AUX and ProcDiePending became INTERRUPT_DIE, but do we really need two different interrupts for requesting a process to terminate? * There are only a few interrupts bits left. Consolidate the existing ones, switch to a 64-bit atomic, or mulitplex some of them? XXX: original text from Thomas's patch: "ProcSignals" were a two-step way for one backend to ask another backend to do something, by multiplexing SIGUSR1. Historically, real work was done in the SIGUSR1 handler, but commit 0da096d7 removed the last case of that. Now the handler just set "interrupt pending" flags, called SetLatch(MyLatch) to close races, and then did all the interesting work at the next CHECK_FOR_INTERRUPTS() call. This commit removes the ProcSignal step, replacing ProcSignals with Interrupts. Benefits of this refactoring include removing unnecessary concepts and terminology, avoiding the risk of people putting buggy code back into signal handlers, removing a whole lot of global variables, removing dependencies on PIDs and the process model, removing risks of sending a signal that has default action of termination to the wrong process, and taking a step towards being able remove the fake signal system from the Windows port. Notable changes: * all calls to SendProcSignal(pid, PROCSIG_XXX) become SendInterrupt(INTERRUPT_XXX, procno) * all XxxPending global variables are gone; they are represented as bits in MyProc->pending_interrupts * the SIGUSR1 handler is gone * places that know the PIDs of other backends must now use procnos instead The only code left in src/backend/storage/ipc/procsignal.c relates to ProcSignalBarriers. XXX Those should perhaps be renamed. SIGUSR1 can now be set to SIG_IGN in most backends. Some special cases remain, that were not using the general sigusr1_handler (now removed). Author: Thomas Munro Author: Heikki Linnakangas Discussion: https://postgr.es/m/CA%2BhUKG%2B3MkS21yK4jL4cgZywdnnGKiBg0jatoV6kzaniBmcqbQ%40mail.gmail.com --- contrib/pg_prewarm/autoprewarm.c | 25 +- contrib/postgres_fdw/connection.c | 3 +- doc/src/sgml/sources.sgml | 3 +- src/backend/access/heap/vacuumlazy.c | 3 +- src/backend/access/spgist/spgdoinsert.c | 9 +- src/backend/access/transam/README.parallel | 2 +- src/backend/access/transam/parallel.c | 48 +-- src/backend/access/transam/xlog.c | 3 +- src/backend/access/transam/xlogfuncs.c | 6 +- src/backend/access/transam/xlogrecovery.c | 31 +- src/backend/backup/basebackup_throttle.c | 12 +- src/backend/commands/async.c | 103 ++---- src/backend/commands/dbcommands.c | 1 + src/backend/commands/tablespace.c | 1 + src/backend/commands/vacuum.c | 8 +- src/backend/executor/nodeGather.c | 3 +- src/backend/libpq/be-secure.c | 18 +- src/backend/libpq/pqcomm.c | 18 +- src/backend/libpq/pqmq.c | 22 +- src/backend/postmaster/autovacuum.c | 41 +-- src/backend/postmaster/bgworker.c | 7 +- src/backend/postmaster/bgwriter.c | 15 +- src/backend/postmaster/checkpointer.c | 69 ++-- src/backend/postmaster/interrupt_handlers.c | 26 +- src/backend/postmaster/pgarch.c | 20 +- src/backend/postmaster/postmaster.c | 3 +- src/backend/postmaster/startup.c | 16 +- src/backend/postmaster/syslogger.c | 8 +- src/backend/postmaster/walsummarizer.c | 26 +- src/backend/postmaster/walwriter.c | 8 +- .../libpqwalreceiver/libpqwalreceiver.c | 12 +- .../replication/logical/applyparallelworker.c | 57 +--- src/backend/replication/logical/launcher.c | 43 +-- src/backend/replication/logical/slotsync.c | 57 ++-- src/backend/replication/logical/tablesync.c | 12 +- src/backend/replication/logical/worker.c | 19 +- src/backend/replication/slot.c | 89 ++--- src/backend/replication/slotfuncs.c | 18 +- src/backend/replication/syncrep.c | 13 +- src/backend/replication/walreceiver.c | 19 +- src/backend/replication/walsender.c | 78 +++-- src/backend/storage/buffer/bufmgr.c | 18 +- src/backend/storage/ipc/interrupt.c | 15 +- src/backend/storage/ipc/ipc.c | 6 +- src/backend/storage/ipc/procarray.c | 20 +- src/backend/storage/ipc/procsignal.c | 242 ++------------ src/backend/storage/ipc/shm_mq.c | 64 ++-- src/backend/storage/ipc/signalfuncs.c | 2 +- src/backend/storage/ipc/sinval.c | 52 +-- src/backend/storage/ipc/sinvaladt.c | 14 +- src/backend/storage/ipc/standby.c | 81 +++-- src/backend/storage/ipc/waiteventset.c | 4 +- src/backend/storage/lmgr/condition_variable.c | 3 +- src/backend/storage/lmgr/predicate.c | 5 +- src/backend/storage/lmgr/proc.c | 15 +- src/backend/tcop/postgres.c | 313 ++++++++---------- src/backend/utils/activity/pgstat_database.c | 20 +- src/backend/utils/adt/mcxtfuncs.c | 10 +- src/backend/utils/adt/misc.c | 3 +- src/backend/utils/init/globals.c | 13 +- src/backend/utils/init/postinit.c | 20 +- src/backend/utils/misc/timeout.c | 6 - src/backend/utils/mmgr/mcxt.c | 20 +- src/include/access/parallel.h | 4 - src/include/commands/async.h | 6 - src/include/libpq/libpq-be-fe-helpers.h | 14 +- src/include/libpq/pqmq.h | 2 +- src/include/miscadmin.h | 61 ++-- src/include/pgstat.h | 2 +- src/include/postmaster/interrupt_handlers.h | 5 - src/include/replication/logicalworker.h | 5 - src/include/replication/slot.h | 6 +- src/include/replication/walsender.h | 2 +- src/include/replication/walsender_private.h | 1 + src/include/replication/worker_internal.h | 5 +- src/include/storage/interrupt.h | 270 ++++++++++++--- src/include/storage/procarray.h | 7 +- src/include/storage/procsignal.h | 41 --- src/include/storage/sinval.h | 5 - src/include/storage/standby.h | 4 +- src/include/storage/waiteventset.h | 5 +- src/include/tcop/tcopprot.h | 6 +- src/include/utils/memutils.h | 1 - src/test/modules/test_shm_mq/setup.c | 9 +- src/test/modules/test_shm_mq/test.c | 6 +- src/test/modules/worker_spi/worker_spi.c | 9 +- 86 files changed, 1040 insertions(+), 1357 deletions(-) diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c index f7ce0a9a77a..ff6bd3252e8 100644 --- a/contrib/pg_prewarm/autoprewarm.c +++ b/contrib/pg_prewarm/autoprewarm.c @@ -40,7 +40,6 @@ #include "storage/interrupt.h" #include "storage/ipc.h" #include "storage/lwlock.h" -#include "storage/procsignal.h" #include "storage/smgr.h" #include "tcop/tcopprot.h" #include "utils/guc.h" @@ -150,7 +149,7 @@ autoprewarm_main(Datum main_arg) /* Establish signal handlers; once that's done, unblock signals. */ pqsignal(SIGTERM, SignalHandlerForShutdownRequest); pqsignal(SIGHUP, SignalHandlerForConfigReload); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); BackgroundWorkerUnblockSignals(); /* Create (if necessary) and attach to our shared memory area. */ @@ -199,24 +198,22 @@ autoprewarm_main(Datum main_arg) if (first_time) { apw_load_buffers(); - final_dump_allowed = !ShutdownRequestPending; + final_dump_allowed = !InterruptPending(INTERRUPT_SHUTDOWN_AUX); last_dump_time = GetCurrentTimestamp(); } /* Periodically dump buffers until terminated. */ - while (!ShutdownRequestPending) + while (!InterruptPending(INTERRUPT_SHUTDOWN_AUX)) { /* In case of a SIGHUP, just reload the configuration. */ - if (ConfigReloadPending) - { - ConfigReloadPending = false; + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) ProcessConfigFile(PGC_SIGHUP); - } if (autoprewarm_interval <= 0) { /* We're only dumping at shutdown, so just wait forever. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_SHUTDOWN_AUX | + INTERRUPT_CONFIG_RELOAD, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, -1L, PG_WAIT_EXTENSION); @@ -243,14 +240,12 @@ autoprewarm_main(Datum main_arg) } /* Sleep until the next dump time. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_SHUTDOWN_AUX | + INTERRUPT_CONFIG_RELOAD, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, delay_in_ms, PG_WAIT_EXTENSION); } - - /* Reset the latch, loop. */ - ClearInterrupt(INTERRUPT_GENERAL); } /* @@ -395,7 +390,7 @@ apw_load_buffers(void) * Likewise, don't launch if we've already been told to shut down. * (The launch would fail anyway, but we might as well skip it.) */ - if (ShutdownRequestPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_AUX)) break; /* @@ -416,7 +411,7 @@ apw_load_buffers(void) LWLockRelease(&apw_state->lock); /* Report our success, if we were able to finish. */ - if (!ShutdownRequestPending) + if (!InterruptPending(INTERRUPT_SHUTDOWN_AUX)) ereport(LOG, (errmsg("autoprewarm successfully prewarmed %d of %d previously-loaded blocks", apw_state->prewarmed_blocks, num_elements))); diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c index 4c4f9ea84af..45f887722de 100644 --- a/contrib/postgres_fdw/connection.c +++ b/contrib/postgres_fdw/connection.c @@ -1654,12 +1654,11 @@ pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime, pgfdw_we_cleanup_result = WaitEventExtensionNew("PostgresFdwCleanupResult"); /* Sleep until there's something to do */ - wc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL, + wc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK, WL_INTERRUPT | WL_SOCKET_READABLE | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, PQsocket(conn), cur_timeout, pgfdw_we_cleanup_result); - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); diff --git a/doc/src/sgml/sources.sgml b/doc/src/sgml/sources.sgml index fc642f5f713..018d6970c24 100644 --- a/doc/src/sgml/sources.sgml +++ b/doc/src/sgml/sources.sgml @@ -1000,8 +1000,7 @@ MemoryContextSwitchTo(MemoryContext context) static void handle_sighup(SIGNAL_ARGS) { - got_SIGHUP = true; - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_CONFIG_RELOAD); } diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index f8df18660ed..9dc09979798 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -3243,11 +3243,10 @@ lazy_truncate_heap(LVRelState *vacrel) return; } - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL, WAIT_EVENT_VACUUM_TRUNCATE); - ClearInterrupt(INTERRUPT_GENERAL); } /* diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c index af6b27b2135..98ed431b470 100644 --- a/src/backend/access/spgist/spgdoinsert.c +++ b/src/backend/access/spgist/spgdoinsert.c @@ -2040,8 +2040,11 @@ spgdoinsert(Relation index, SpGistState *state, * pending, break out of the loop and deal with the situation below. * Set result = false because we must restart the insertion if the * interrupt isn't a query-cancel-or-die case. + * + * XXX: INTERRUPT_CFI_MASK covers more than just query cancel and die. + * Could we be more precise here? */ - if (INTERRUPTS_PENDING_CONDITION()) + if (INTERRUPTS_PENDING_CONDITION(INTERRUPT_CFI_MASK)) { result = false; break; @@ -2167,7 +2170,7 @@ spgdoinsert(Relation index, SpGistState *state, * repeatedly, check for query cancel (see comments above). */ process_inner_tuple: - if (INTERRUPTS_PENDING_CONDITION()) + if (INTERRUPTS_PENDING_CONDITION(INTERRUPT_CFI_MASK)) { result = false; break; @@ -2343,7 +2346,7 @@ spgdoinsert(Relation index, SpGistState *state, * were the case, telling the caller to retry would create an infinite * loop. */ - Assert(INTERRUPTS_CAN_BE_PROCESSED()); + Assert(INTERRUPTS_CAN_BE_PROCESSED(INTERRUPT_CFI_MASK)); /* * Finally, check for interrupts again. If there was a query cancel, diff --git a/src/backend/access/transam/README.parallel b/src/backend/access/transam/README.parallel index 9df3da91b0c..a3593dd4f54 100644 --- a/src/backend/access/transam/README.parallel +++ b/src/backend/access/transam/README.parallel @@ -37,7 +37,7 @@ with fewer parallel workers than it originally requested, so catering to this case imposes no additional burden. Whenever a new message (or partial message; very large messages may wrap) is -sent to the error-reporting queue, PROCSIG_PARALLEL_MESSAGE is sent to the +sent to the error-reporting queue, INTERRUPT_PARALLEL_MESSAGE is sent to the initiating backend. This causes the next CHECK_FOR_INTERRUPTS() in the initiating backend to read and rethrow the message. For the most part, this makes error reporting in parallel mode "just work". Of course, to work diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index bf211efb63e..68e6c22512b 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -115,9 +115,6 @@ typedef struct FixedParallelState */ int ParallelWorkerNumber = -1; -/* Is there a parallel message pending which we need to receive? */ -volatile sig_atomic_t ParallelMessagePending = false; - /* Are we initializing a parallel worker? */ bool InitializingParallelWorker = false; @@ -127,9 +124,6 @@ static FixedParallelState *MyFixedParallelState; /* List of active parallel contexts. */ static dlist_head pcxt_list = DLIST_STATIC_INIT(pcxt_list); -/* Backend-local copy of data from FixedParallelState. */ -static pid_t ParallelLeaderPid; - /* * List of internal parallel worker entry points. We need this for * reasons explained in LookupParallelWorkerFunction(), below. @@ -243,7 +237,7 @@ InitializeParallelDSM(ParallelContext *pcxt) * We can deal with that edge case by pretending no workers were * requested. */ - if (!INTERRUPTS_CAN_BE_PROCESSED()) + if (!INTERRUPTS_CAN_BE_PROCESSED(INTERRUPT_CFI_MASK)) pcxt->nworkers = 0; /* @@ -759,16 +753,20 @@ WaitForParallelWorkersToAttach(ParallelContext *pcxt) { /* * Worker not yet started, so we must wait. The postmaster - * will notify us if the worker's state changes. The - * interrupt might also get set for some other reason, but if - * so we'll just end up waiting for the same worker again. + * will notify us with INTERRUPT_GENERAL if the worker's state + * changes. The interrupt might also get set for some other + * reason, but if so we'll just end up waiting for the same + * worker again. */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, -1, WAIT_EVENT_BGWORKER_STARTUP); if (rc & WL_INTERRUPT) + { + CHECK_FOR_INTERRUPTS(); ClearInterrupt(INTERRUPT_GENERAL); + } } } @@ -883,7 +881,7 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt) } } - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, -1, WAIT_EVENT_PARALLEL_FINISH); ClearInterrupt(INTERRUPT_GENERAL); @@ -1027,21 +1025,6 @@ ParallelContextActive(void) return !dlist_is_empty(&pcxt_list); } -/* - * Handle receipt of an interrupt indicating a parallel worker message. - * - * Note: this is called within a signal handler! All we can do is set - * a flag that will cause the next CHECK_FOR_INTERRUPTS() to invoke - * ProcessParallelMessages(). - */ -void -HandleParallelMessageInterrupt(void) -{ - InterruptPending = true; - ParallelMessagePending = true; - RaiseInterrupt(INTERRUPT_GENERAL); -} - /* * Process any queued protocol messages received from parallel workers. */ @@ -1077,7 +1060,7 @@ ProcessParallelMessages(void) oldcontext = MemoryContextSwitchTo(hpm_context); /* OK to process messages. Reset the flag saying there are more to do. */ - ParallelMessagePending = false; + ClearInterrupt(INTERRUPT_PARALLEL_MESSAGE); dlist_foreach(iter, &pcxt_list) { @@ -1358,7 +1341,6 @@ ParallelWorkerMain(Datum main_arg) MyFixedParallelState = fps; /* Arrange to signal the leader if we exit. */ - ParallelLeaderPid = fps->parallel_leader_pid; ParallelLeaderProcNumber = fps->parallel_leader_proc_number; before_shmem_exit(ParallelWorkerShutdown, PointerGetDatum(seg)); @@ -1374,8 +1356,7 @@ ParallelWorkerMain(Datum main_arg) shm_mq_set_sender(mq, MyProc); mqh = shm_mq_attach(mq, seg, NULL); pq_redirect_to_shm_mq(seg, mqh); - pq_set_parallel_leader(fps->parallel_leader_pid, - fps->parallel_leader_proc_number); + pq_set_parallel_leader(fps->parallel_leader_proc_number); /* * Hooray! Primary initialization is complete. Now, we need to set up our @@ -1614,9 +1595,8 @@ ParallelWorkerReportLastRecEnd(XLogRecPtr last_xlog_end) static void ParallelWorkerShutdown(int code, Datum arg) { - SendProcSignal(ParallelLeaderPid, - PROCSIG_PARALLEL_MESSAGE, - ParallelLeaderProcNumber); + SendInterrupt(INTERRUPT_PARALLEL_MESSAGE, + ParallelLeaderProcNumber); dsm_detach((dsm_segment *) DatumGetPointer(arg)); } diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 4740dc89ef8..4f218eec41d 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -9382,11 +9382,10 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive) reported_waiting = true; } - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 1000L, WAIT_EVENT_BACKUP_WAIT_WAL_ARCHIVE); - ClearInterrupt(INTERRUPT_GENERAL); if (++waits >= seconds_before_warning) { diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index ffe0237326b..d6defe26b95 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -708,6 +708,8 @@ pg_promote(PG_FUNCTION_ARGS) errmsg("failed to send signal to postmaster: %m"))); } + /* XXX: we could interrupt the startup process directly */ + /* return immediately if waiting was not requested */ if (!wait) PG_RETURN_BOOL(true); @@ -718,14 +720,12 @@ pg_promote(PG_FUNCTION_ARGS) { int rc; - ClearInterrupt(INTERRUPT_GENERAL); - if (!RecoveryInProgress()) PG_RETURN_BOOL(true); CHECK_FOR_INTERRUPTS(); - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK, WL_INTERRUPT | WL_TIMEOUT | WL_POSTMASTER_DEATH, 1000L / WAITS_PER_SECOND, WAIT_EVENT_PROMOTE); diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index 1e31b0b761a..f42559861e7 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -3004,18 +3004,12 @@ recoveryApplyDelay(XLogReaderState *record) while (true) { - /* - * INTERRUPT_GENERAL is used for all the usual interrupts, like config - * reloads. The wakeups when more WAL arrive use a different - * interrupt number (INTERRUPT_RECOVERY_CONTINUE) so that more WAL - * arriving don't wake up the startup process excessively when we're - * waiting in other places, like for recovery conflicts. - */ - ClearInterrupt(INTERRUPT_GENERAL); - /* This might change recovery_min_apply_delay. */ ProcessStartupProcInterrupts(); + /* + * INTERRUPT_RECOVERY_CONTINUE indicates that more WAL has arrived. + */ ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE); if (CheckForStandbyTrigger()) break; @@ -3037,8 +3031,8 @@ recoveryApplyDelay(XLogReaderState *record) elog(DEBUG2, "recovery apply delay %ld milliseconds", msecs); - (void) WaitInterrupt(1 << INTERRUPT_RECOVERY_CONTINUE | - 1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_STARTUP_PROC_MASK | + INTERRUPT_RECOVERY_CONTINUE, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, msecs, WAIT_EVENT_RECOVERY_APPLY_DELAY); @@ -3696,18 +3690,18 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, /* Do background tasks that might benefit us later. */ KnownAssignedTransactionIdsIdleMaintenance(); - (void) WaitInterrupt(1 << INTERRUPT_RECOVERY_CONTINUE | - 1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_STARTUP_PROC_MASK | + INTERRUPT_RECOVERY_CONTINUE, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, wait_time, WAIT_EVENT_RECOVERY_RETRIEVE_RETRY_INTERVAL); - ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE); now = GetCurrentTimestamp(); - /* Handle interrupt signals of startup process */ - ClearInterrupt(INTERRUPT_GENERAL); + /* Handle standard interrupts of startup process */ ProcessStartupProcInterrupts(); + + ClearInterrupt(INTERRUPT_RECOVERY_CONTINUE); } last_fail_time = now; currentSource = XLOG_FROM_ARCHIVE; @@ -3973,8 +3967,8 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, * Wait for more WAL to arrive, when we will be woken * immediately by the WAL receiver. */ - (void) WaitInterrupt(1 << INTERRUPT_RECOVERY_CONTINUE | - 1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_STARTUP_PROC_MASK | + INTERRUPT_RECOVERY_CONTINUE, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, -1L, WAIT_EVENT_RECOVERY_WAL_STREAM); @@ -3998,7 +3992,6 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, * This possibly-long loop needs to handle interrupts of startup * process. */ - ClearInterrupt(INTERRUPT_GENERAL); ProcessStartupProcInterrupts(); } diff --git a/src/backend/backup/basebackup_throttle.c b/src/backend/backup/basebackup_throttle.c index 05443e0853a..50edc67534a 100644 --- a/src/backend/backup/basebackup_throttle.c +++ b/src/backend/backup/basebackup_throttle.c @@ -155,6 +155,8 @@ throttle(bbsink_throttle *sink, size_t increment) sleep; int wait_result; + CHECK_FOR_INTERRUPTS(); + /* Time elapsed since the last measurement (and possible wake up). */ elapsed = GetCurrentTimestamp() - sink->throttled_last; @@ -163,23 +165,15 @@ throttle(bbsink_throttle *sink, size_t increment) if (sleep <= 0) break; - ClearInterrupt(INTERRUPT_GENERAL); - - /* We're eating a wakeup, so check for interrupts */ - CHECK_FOR_INTERRUPTS(); - /* * (TAR_SEND_SIZE / throttling_sample * elapsed_min_unit) should be * the maximum time to sleep. Thus the cast to long is safe. */ - wait_result = WaitInterrupt(1 << INTERRUPT_GENERAL, + wait_result = WaitInterrupt(INTERRUPT_CFI_MASK, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, (long) (sleep / 1000), WAIT_EVENT_BASE_BACKUP_THROTTLE); - if (wait_result & WL_INTERRUPT) - CHECK_FOR_INTERRUPTS(); - /* Done waiting? */ if (wait_result & WL_TIMEOUT) break; diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 201d7150083..989c50e1ee0 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -39,7 +39,7 @@ * is no need for WAL support or fsync'ing. * * 3. Every backend that is listening on at least one channel registers by - * entering its PID into the array in AsyncQueueControl. It then scans all + * entering itself into the array in AsyncQueueControl. It then scans all * incoming notifications in the central queue and first compares the * database OID of the notification with its own database OID and then * compares the notified channel with the list of channels that it listens @@ -72,7 +72,7 @@ * Then we signal any backends that may be interested in our messages * (including our own backend, if listening). This is done by * SignalBackends(), which scans the list of listening backends and sends a - * PROCSIG_NOTIFY_INTERRUPT signal to every listening backend (we don't + * INTERRUPT_ASYNC_NOTIFY interrupt to every listening backend (we don't * know which backend is listening on which channel so we must signal them * all). We can exclude backends that are already up to date, though, and * we can also exclude backends that are in other databases (unless they @@ -90,13 +90,11 @@ * frontend until the command is done; but notifies to other backends * should go out immediately after each commit. * - * 5. Upon receipt of a PROCSIG_NOTIFY_INTERRUPT signal, the signal handler - * raises INTERRUPT_GENERAL, which triggers the event to be processed - * immediately if this backend is idle (i.e., it is waiting for a frontend + * 5. The INTERRUPT_ASYNC_NOTIFY interrupt is processed immediately in the + * listening backend if it is idle (i.e., it is waiting for a frontend * command and is not within a transaction block. C.f. - * ProcessClientReadInterrupt()). Otherwise the handler may only set a - * flag, which will cause the processing to occur just before we next go - * idle. + * ProcessClientReadInterrupt()). Otherwise the interrupt is processed + * later, just before we next go idle. * * Inbound-notify processing consists of reading all of the notifications * that have arrived since scanning last time. We read every notification @@ -127,7 +125,6 @@ #include #include -#include #include "access/parallel.h" #include "access/slru.h" @@ -143,7 +140,6 @@ #include "storage/interrupt.h" #include "storage/ipc.h" #include "storage/lmgr.h" -#include "storage/procsignal.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/guc_hooks.h" @@ -271,7 +267,7 @@ typedef struct QueueBackendStatus * NotifyQueueTailLock, then NotifyQueueLock, and lastly SLRU bank lock. * * Each backend uses the backend[] array entry with index equal to its - * ProcNumber. We rely on this to make SendProcSignal fast. + * ProcNumber. We rely on this for sending interrupts. * * The backend[] array entries for actively-listening backends are threaded * together using firstListener and the nextListener links, so that we can @@ -404,15 +400,6 @@ struct NotificationHash static NotificationList *pendingNotifies = NULL; -/* - * Inbound notifications are initially processed by HandleNotifyInterrupt(), - * called from inside a signal handler. That just sets the - * notifyInterruptPending flag and raises the INTERRUPT_GENERAL interrupt. - * ProcessNotifyInterrupt() will then be called whenever it's safe to actually - * deal with the interrupt. - */ -volatile sig_atomic_t notifyInterruptPending = false; - /* True if we've registered an on_shmem_exit cleanup */ static bool unlistenExitRegistered = false; @@ -1581,29 +1568,25 @@ asyncQueueFillWarning(void) static void SignalBackends(void) { - int32 *pids; ProcNumber *procnos; int count; /* * Identify backends that we need to signal. We don't want to send * signals while holding the NotifyQueueLock, so this loop just builds a - * list of target PIDs. + * list of target backends. * * XXX in principle these pallocs could fail, which would be bad. Maybe * preallocate the arrays? They're not that large, though. */ - pids = (int32 *) palloc(MaxBackends * sizeof(int32)); procnos = (ProcNumber *) palloc(MaxBackends * sizeof(ProcNumber)); count = 0; LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE); for (ProcNumber i = QUEUE_FIRST_LISTENER; i != INVALID_PROC_NUMBER; i = QUEUE_NEXT_LISTENER(i)) { - int32 pid = QUEUE_BACKEND_PID(i); QueuePosition pos; - Assert(pid != InvalidPid); pos = QUEUE_BACKEND_POS(i); if (QUEUE_BACKEND_DBOID(i) == MyDatabaseId) { @@ -1625,38 +1608,27 @@ SignalBackends(void) continue; } /* OK, need to signal this one */ - pids[count] = pid; procnos[count] = i; count++; } LWLockRelease(NotifyQueueLock); - /* Now send signals */ + /* Now send the interrupts */ for (int i = 0; i < count; i++) { - int32 pid = pids[i]; + ProcNumber procno = procnos[i]; /* - * If we are signaling our own process, no need to involve the kernel; - * just set the flag directly. + * Note: it's unlikely but certainly possible that the backend exited + * since we released NotifyQueueLock. We'll send the interrupt to + * wrong backend in that case, but that's harmless. */ - if (pid == MyProcPid) - { - notifyInterruptPending = true; - continue; - } - - /* - * Note: assuming things aren't broken, a signal failure here could - * only occur if the target backend exited since we released - * NotifyQueueLock; which is unlikely but certainly possible. So we - * just log a low-level debug message if it happens. - */ - if (SendProcSignal(pid, PROCSIG_NOTIFY_INTERRUPT, procnos[i]) < 0) - elog(DEBUG3, "could not signal backend with PID %d: %m", pid); + if (procno == MyProcNumber) + RaiseInterrupt(INTERRUPT_ASYNC_NOTIFY); + else + SendInterrupt(INTERRUPT_ASYNC_NOTIFY, procno); } - pfree(pids); pfree(procnos); } @@ -1793,39 +1765,12 @@ AtSubAbort_Notify(void) } } -/* - * HandleNotifyInterrupt - * - * Signal handler portion of interrupt handling. Let the backend know - * that there's a pending notify interrupt. If we're currently reading - * from the client, this will interrupt the read and - * ProcessClientReadInterrupt() will call ProcessNotifyInterrupt(). - */ -void -HandleNotifyInterrupt(void) -{ - /* - * Note: this is called by a SIGNAL HANDLER. You must be very wary what - * you do here. - */ - - /* signal that work needs to be done */ - notifyInterruptPending = true; - - /* make sure the event is processed in due course */ - RaiseInterrupt(INTERRUPT_GENERAL); -} - /* * ProcessNotifyInterrupt * - * This is called if we see notifyInterruptPending set, just before - * transmitting ReadyForQuery at the end of a frontend command, and - * also if a notify signal occurs while reading from the frontend. - * HandleNotifyInterrupt() will cause the read to be interrupted with - * INTERRUPT_GENERAL, and this routine will get called. If we are truly - * idle (ie, *not* inside a transaction block), process the incoming - * notifies. + * This is called if we see INTERRUPT_ASYNC_NOTIFY set, just before + * transmitting ReadyForQuery at the end of a frontend command, and also + * if a notify signal occurs while reading from the frontend. * * If "flush" is true, force any frontend messages out immediately. * This can be false when being called at the end of a frontend command, @@ -1834,11 +1779,10 @@ HandleNotifyInterrupt(void) void ProcessNotifyInterrupt(bool flush) { - if (IsTransactionOrTransactionBlock()) - return; /* not really idle */ + Assert(!IsTransactionOrTransactionBlock()); /* Loop in case another signal arrives while sending messages */ - while (notifyInterruptPending) + while (ConsumeInterrupt(INTERRUPT_ASYNC_NOTIFY)) ProcessIncomingNotify(flush); } @@ -2183,9 +2127,6 @@ asyncQueueAdvanceTail(void) static void ProcessIncomingNotify(bool flush) { - /* We *must* reset the flag */ - notifyInterruptPending = false; - /* Do nothing else if we aren't actively listening */ if (listenChannels == NIL) return; diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 1753b289074..d1557542b76 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -60,6 +60,7 @@ #include "storage/lmgr.h" #include "storage/md.h" #include "storage/procarray.h" +#include "storage/procsignal.h" #include "storage/smgr.h" #include "utils/acl.h" #include "utils/builtins.h" diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index a9005cc7212..80b6c721bac 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -70,6 +70,7 @@ #include "miscadmin.h" #include "postmaster/bgwriter.h" #include "storage/fd.h" +#include "storage/procsignal.h" #include "storage/standby.h" #include "utils/acl.h" #include "utils/builtins.h" diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 8017448de56..4d6b4d0b7d1 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -48,6 +48,7 @@ #include "postmaster/bgworker_internals.h" #include "postmaster/interrupt_handlers.h" #include "storage/bufmgr.h" +#include "storage/interrupt.h" #include "storage/lmgr.h" #include "storage/pmsignal.h" #include "storage/proc.h" @@ -2397,8 +2398,7 @@ vacuum_delay_point(bool is_analyze) /* Always check for interrupts */ CHECK_FOR_INTERRUPTS(); - if (InterruptPending || - (!VacuumCostActive && !ConfigReloadPending)) + if (!VacuumCostActive && !InterruptPending(INTERRUPT_CONFIG_RELOAD)) return; /* @@ -2407,9 +2407,9 @@ vacuum_delay_point(bool is_analyze) * [autovacuum_]vacuum_cost_delay to take effect while a table is being * vacuumed or analyzed. */ - if (ConfigReloadPending && AmAutoVacuumWorkerProcess()) + if (InterruptPending(INTERRUPT_CONFIG_RELOAD) && AmAutoVacuumWorkerProcess()) { - ConfigReloadPending = false; + ClearInterrupt(INTERRUPT_CONFIG_RELOAD); ProcessConfigFile(PGC_SIGHUP); VacuumUpdateCosts(); } diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 5d0a58384b0..bf957ab1a87 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -383,7 +383,8 @@ gather_readnext(GatherState *gatherstate) return NULL; /* Nothing to do except wait for developments. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | + INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, WAIT_EVENT_EXECUTE_GATHER); ClearInterrupt(INTERRUPT_GENERAL); diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c index c61f0bdb4c0..3d05e80364e 100644 --- a/src/backend/libpq/be-secure.c +++ b/src/backend/libpq/be-secure.c @@ -179,10 +179,11 @@ ssize_t secure_read(Port *port, void *ptr, size_t len) { ssize_t n; + uint32 interruptMask; int waitfor; /* Deal with any already-pending interrupt condition. */ - ProcessClientReadInterrupt(false); + interruptMask = ProcessClientReadInterrupt(false); retry: #ifdef USE_SSL @@ -214,6 +215,7 @@ retry: Assert(waitfor); ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, waitfor, 0); + ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetInterruptPos, WL_INTERRUPT, interruptMask); WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1, WAIT_EVENT_CLIENT_READ); @@ -243,8 +245,7 @@ retry: /* Handle interrupt. */ if (event.events & WL_INTERRUPT) { - ClearInterrupt(INTERRUPT_GENERAL); - ProcessClientReadInterrupt(true); + interruptMask = ProcessClientReadInterrupt(true); /* * We'll retry the read. Most likely it will return immediately @@ -259,7 +260,7 @@ retry: * Process interrupts that happened during a successful (or non-blocking, * or hard-failed) read. */ - ProcessClientReadInterrupt(false); + (void) ProcessClientReadInterrupt(false); return n; } @@ -306,10 +307,11 @@ ssize_t secure_write(Port *port, const void *ptr, size_t len) { ssize_t n; + uint32 interruptMask; int waitfor; /* Deal with any already-pending interrupt condition. */ - ProcessClientWriteInterrupt(false); + interruptMask = ProcessClientWriteInterrupt(false); retry: waitfor = 0; @@ -340,6 +342,7 @@ retry: Assert(waitfor); ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, waitfor, 0); + ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetInterruptPos, WL_INTERRUPT, interruptMask); WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1, WAIT_EVENT_CLIENT_WRITE); @@ -353,8 +356,7 @@ retry: /* Handle interrupt. */ if (event.events & WL_INTERRUPT) { - ClearInterrupt(INTERRUPT_GENERAL); - ProcessClientWriteInterrupt(true); + interruptMask = ProcessClientWriteInterrupt(true); /* * We'll retry the write. Most likely it will return immediately @@ -369,7 +371,7 @@ retry: * Process interrupts that happened during a successful (or non-blocking, * or hard-failed) write. */ - ProcessClientWriteInterrupt(false); + (void) ProcessClientWriteInterrupt(false); return n; } diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index daf7c7435bb..5a92803070e 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -309,7 +309,7 @@ pq_init(ClientSocket *client_sock) socket_pos = AddWaitEventToSet(FeBeWaitSet, WL_SOCKET_WRITEABLE, port->sock, 0, NULL); interrupt_pos = AddWaitEventToSet(FeBeWaitSet, WL_INTERRUPT, PGINVALID_SOCKET, - 1 << INTERRUPT_GENERAL, NULL); + INTERRUPT_CFI_MASK, NULL); AddWaitEventToSet(FeBeWaitSet, WL_POSTMASTER_DEATH, PGINVALID_SOCKET, 0, NULL); @@ -1413,8 +1413,7 @@ internal_flush_buffer(const char *buf, size_t *start, size_t *end) * the connection. */ *start = *end = 0; - ClientConnectionLost = 1; - InterruptPending = 1; + RaiseInterrupt(INTERRUPT_CLIENT_CONNECTION_LOST); return EOF; } @@ -2065,24 +2064,13 @@ pq_check_connection(void) * all FeBeWaitSet socket wait sites do the same. */ ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, WL_SOCKET_CLOSED, 0); + ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetInterruptPos, WL_INTERRUPT, 0); -retry: rc = WaitEventSetWait(FeBeWaitSet, 0, events, lengthof(events), 0); for (int i = 0; i < rc; ++i) { if (events[i].events & WL_SOCKET_CLOSED) return false; - if (events[i].events & WL_INTERRUPT) - { - /* - * An interrupt event might be preventing other events from being - * reported. Reset it and poll again. No need to restore it - * because no code should expect INTERRUPT_GENERAL to survive - * across CHECK_FOR_INTERRUPTS(). - */ - ClearInterrupt(INTERRUPT_GENERAL); - goto retry; - } } return true; diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c index ef080f63112..6394f924399 100644 --- a/src/backend/libpq/pqmq.c +++ b/src/backend/libpq/pqmq.c @@ -26,7 +26,6 @@ static shm_mq_handle *pq_mq_handle; static bool pq_mq_busy = false; -static pid_t pq_mq_parallel_leader_pid = 0; static ProcNumber pq_mq_parallel_leader_proc_number = INVALID_PROC_NUMBER; static void pq_cleanup_redirect_to_shm_mq(dsm_segment *seg, Datum arg); @@ -72,14 +71,13 @@ pq_cleanup_redirect_to_shm_mq(dsm_segment *seg, Datum arg) } /* - * Arrange to SendProcSignal() to the parallel leader each time we transmit - * message data via the shm_mq. + * Arrange to interrupt to the parallel leader each time we transmit message + * data via the shm_mq. */ void -pq_set_parallel_leader(pid_t pid, ProcNumber procNumber) +pq_set_parallel_leader(ProcNumber procNumber) { Assert(PqCommMethods == &PqCommMqMethods); - pq_mq_parallel_leader_pid = pid; pq_mq_parallel_leader_proc_number = procNumber; } @@ -164,25 +162,23 @@ mq_putmessage(char msgtype, const char *s, size_t len) */ result = shm_mq_sendv(pq_mq_handle, iov, 2, true, true); - if (pq_mq_parallel_leader_pid != 0) + if (pq_mq_parallel_leader_proc_number != INVALID_PROC_NUMBER) { if (IsLogicalParallelApplyWorker()) - SendProcSignal(pq_mq_parallel_leader_pid, - PROCSIG_PARALLEL_APPLY_MESSAGE, - pq_mq_parallel_leader_proc_number); + SendInterrupt(INTERRUPT_PARALLEL_APPLY_MESSAGE, + pq_mq_parallel_leader_proc_number); else { Assert(IsParallelWorker()); - SendProcSignal(pq_mq_parallel_leader_pid, - PROCSIG_PARALLEL_MESSAGE, - pq_mq_parallel_leader_proc_number); + SendInterrupt(INTERRUPT_PARALLEL_MESSAGE, + pq_mq_parallel_leader_proc_number); } } if (result != SHM_MQ_WOULD_BLOCK) break; - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, WAIT_EVENT_MESSAGE_QUEUE_PUT_MESSAGE); ClearInterrupt(INTERRUPT_GENERAL); diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index e27da9fd88a..4bbf413e3ec 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -401,7 +401,7 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len) InitializeTimeouts(); /* establishes SIGALRM handler */ pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, avl_sigusr2_handler); pqsignal(SIGFPE, FloatExceptionHandler); pqsignal(SIGCHLD, SIG_DFL); @@ -451,7 +451,7 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len) /* Forget any pending QueryCancel or timeout request */ disable_all_timeouts(false); - QueryCancelPending = false; /* second to avoid race condition */ + ClearInterrupt(INTERRUPT_QUERY_CANCEL); /* second to avoid race condition */ /* Report the error to the server log */ EmitErrorReport(); @@ -492,7 +492,7 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len) RESUME_INTERRUPTS(); /* if in shutdown mode, no need for anything further; just go away */ - if (ShutdownRequestPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_AUX)) AutoVacLauncherShutdown(); /* @@ -551,7 +551,7 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len) */ if (!AutoVacuumingActive()) { - if (!ShutdownRequestPending) + if (!InterruptPending(INTERRUPT_SHUTDOWN_AUX)) do_start_worker(); proc_exit(0); /* done */ } @@ -568,7 +568,7 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len) rebuild_database_list(InvalidOid); /* loop until shutdown request */ - while (!ShutdownRequestPending) + while (!InterruptPending(INTERRUPT_SHUTDOWN_AUX)) { struct timeval nap; TimestampTz current_time = 0; @@ -587,15 +587,19 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len) * Wait until naptime expires or we get some type of signal (all the * signal handlers will wake us by calling RaiseInterrupt). */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_SHUTDOWN_AUX | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_BARRIER | + INTERRUPT_LOG_MEMORY_CONTEXT | + INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L), WAIT_EVENT_AUTOVACUUM_MAIN); - ClearInterrupt(INTERRUPT_GENERAL); - ProcessAutoVacLauncherInterrupts(); + ClearInterrupt(INTERRUPT_GENERAL); + /* * a worker finished, or postmaster signaled failure to start a worker */ @@ -745,14 +749,13 @@ static void ProcessAutoVacLauncherInterrupts(void) { /* the normal shutdown case */ - if (ShutdownRequestPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_AUX)) AutoVacLauncherShutdown(); - if (ConfigReloadPending) + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) { int autovacuum_max_workers_prev = autovacuum_max_workers; - ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); /* shutdown requested in config file? */ @@ -772,11 +775,11 @@ ProcessAutoVacLauncherInterrupts(void) } /* Process barrier events */ - if (ProcSignalBarrierPending) + if (InterruptPending(INTERRUPT_BARRIER)) ProcessProcSignalBarrier(); /* Perform logging of memory contexts of this process */ - if (LogMemoryContextPending) + if (InterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT)) ProcessLogMemoryContextInterrupt(); /* Process sinval catchup interrupts that happened while sleeping */ @@ -1408,7 +1411,7 @@ AutoVacWorkerMain(const void *startup_data, size_t startup_data_len) InitializeTimeouts(); /* establishes SIGALRM handler */ pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); pqsignal(SIGFPE, FloatExceptionHandler); pqsignal(SIGCHLD, SIG_DFL); @@ -2278,9 +2281,8 @@ do_autovacuum(void) /* * Check for config changes before processing each collected table. */ - if (ConfigReloadPending) + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) { - ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); /* @@ -2438,7 +2440,7 @@ do_autovacuum(void) * current table (we're done with it, so it would make no sense to * cancel at this point.) */ - QueryCancelPending = false; + ClearInterrupt(INTERRUPT_QUERY_CANCEL); } PG_CATCH(); { @@ -2522,9 +2524,8 @@ deleted: * Check for config changes before acquiring lock for further jobs. */ CHECK_FOR_INTERRUPTS(); - if (ConfigReloadPending) + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) { - ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); VacuumUpdateCosts(); } @@ -2636,7 +2637,7 @@ perform_work_item(AutoVacuumWorkItem *workitem) * (we're done with it, so it would make no sense to cancel at this * point.) */ - QueryCancelPending = false; + ClearInterrupt(INTERRUPT_QUERY_CANCEL); } PG_CATCH(); { diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c index 6cc68bf411d..08969ff7693 100644 --- a/src/backend/postmaster/bgworker.c +++ b/src/backend/postmaster/bgworker.c @@ -26,7 +26,6 @@ #include "storage/lwlock.h" #include "storage/pmsignal.h" #include "storage/proc.h" -#include "storage/procsignal.h" #include "storage/shmem.h" #include "tcop/tcopprot.h" #include "utils/ascii.h" @@ -751,7 +750,7 @@ BackgroundWorkerMain(const void *startup_data, size_t startup_data_len) * SIGINT is used to signal canceling the current action */ pqsignal(SIGINT, StatementCancelHandler); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGFPE, FloatExceptionHandler); /* XXX Any other handlers needed here? */ @@ -1233,7 +1232,7 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp) if (status != BGWH_NOT_YET_STARTED) break; - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_POSTMASTER_DEATH, 0, WAIT_EVENT_BGWORKER_STARTUP); @@ -1277,7 +1276,7 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle) if (status == BGWH_STOPPED) break; - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_POSTMASTER_DEATH, 0, WAIT_EVENT_BGWORKER_SHUTDOWN); diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c index a37257218b1..8d8b85652be 100644 --- a/src/backend/postmaster/bgwriter.c +++ b/src/backend/postmaster/bgwriter.c @@ -45,7 +45,6 @@ #include "storage/interrupt.h" #include "storage/lwlock.h" #include "storage/proc.h" -#include "storage/procsignal.h" #include "storage/smgr.h" #include "storage/standby.h" #include "utils/memutils.h" @@ -106,7 +105,7 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len) /* SIGQUIT handler was already set up by InitPostmasterChild */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); /* @@ -224,9 +223,7 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len) bool can_hibernate; int rc; - /* Clear any already-pending wakeups */ - ClearInterrupt(INTERRUPT_GENERAL); - + /* Process any pending standard interrupts */ ProcessMainLoopInterrupts(); /* @@ -303,7 +300,7 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len) * down with interrupt events that are likely to happen frequently * during normal operation. */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_MAIN_LOOP_MASK, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, BgWriterDelay /* ms */ , WAIT_EVENT_BGWRITER_MAIN); @@ -327,10 +324,14 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len) */ if (rc == WL_TIMEOUT && can_hibernate && prev_hibernate) { + /* Clear any already-pending wakeups */ + ClearInterrupt(INTERRUPT_GENERAL); + /* Ask for notification at next buffer allocation */ StrategyNotifyBgWriter(MyProcNumber); /* Sleep ... */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_MAIN_LOOP_MASK | + INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, BgWriterDelay * HIBERNATE_FACTOR, WAIT_EVENT_BGWRITER_HIBERNATE); diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c index da6caa64457..8ce5f6b7f00 100644 --- a/src/backend/postmaster/checkpointer.c +++ b/src/backend/postmaster/checkpointer.c @@ -81,10 +81,11 @@ * The algorithm for backends is: * 1. Record current values of ckpt_failed and ckpt_started, and * set request flags, while holding ckpt_lck. - * 2. Send signal to request checkpoint. + * 2. Send INTERRUPT_GENERAL to wake up the checkpointer. * 3. Sleep until ckpt_started changes. Now you know a checkpoint has * begun since you started this algorithm (although *not* that it was - * specifically initiated by your signal), and that it is using your flags. + * specifically initiated by your interrupt), and that it is using your + * flags. * 4. Record new value of ckpt_started. * 5. Sleep until ckpt_done >= saved value of ckpt_started. (Use modulo * arithmetic here in case counters wrap around.) Now you know a @@ -146,7 +147,6 @@ double CheckPointCompletionTarget = 0.9; * Private state */ static bool ckpt_active = false; -static volatile sig_atomic_t ShutdownXLOGPending = false; /* these values are valid when ckpt_active is true: */ static pg_time_t ckpt_start_time; @@ -202,7 +202,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len) /* SIGQUIT handler was already set up by InitPostmasterChild */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SignalHandlerForShutdownRequest); /* @@ -356,12 +356,12 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len) ClearInterrupt(INTERRUPT_GENERAL); /* - * Process any requests or signals received recently. + * Process any requests or interrupts received recently. */ AbsorbSyncRequests(); ProcessCheckpointerInterrupts(); - if (ShutdownXLOGPending || ShutdownRequestPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_XLOG) || InterruptPending(INTERRUPT_SHUTDOWN_AUX)) break; /* @@ -538,7 +538,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len) * interrupt might have been reset (e.g. in CheckpointWriteDelay). */ ProcessCheckpointerInterrupts(); - if (ShutdownXLOGPending || ShutdownRequestPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_XLOG) || InterruptPending(INTERRUPT_SHUTDOWN_AUX)) break; } @@ -557,7 +557,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len) continue; /* - * Sleep until we are signaled or it's time for another checkpoint or + * Sleep until we are woken up or it's time for another checkpoint or * xlog file switch. */ now = (pg_time_t) time(NULL); @@ -573,10 +573,18 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len) cur_timeout = Min(cur_timeout, XLogArchiveTimeout - elapsed_secs); } - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, - WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, - cur_timeout * 1000L /* convert to ms */ , - WAIT_EVENT_CHECKPOINTER_MAIN); + (void) WaitInterrupt( + /* these are handled in the main loop */ + INTERRUPT_GENERAL | /* checkpoint requested */ + INTERRUPT_SHUTDOWN_XLOG | + INTERRUPT_SHUTDOWN_AUX | + /* ProcessCheckpointerInterrupts() */ + INTERRUPT_BARRIER | + INTERRUPT_LOG_MEMORY_CONTEXT | + INTERRUPT_CONFIG_RELOAD, + WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, + cur_timeout * 1000L /* convert to ms */ , + WAIT_EVENT_CHECKPOINTER_MAIN); } /* @@ -585,7 +593,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len) */ ExitOnAnyError = true; - if (ShutdownXLOGPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_XLOG)) { /* * Close down the database. @@ -603,7 +611,7 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len) * Tell postmaster that we're done. */ SendPostmasterSignal(PMSIGNAL_XLOG_IS_SHUTDOWN); - ShutdownXLOGPending = false; + ClearInterrupt(INTERRUPT_SHUTDOWN_XLOG); } /* @@ -613,15 +621,16 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len) */ for (;;) { - /* Clear any already-pending wakeups */ - ClearInterrupt(INTERRUPT_GENERAL); - ProcessCheckpointerInterrupts(); - if (ShutdownRequestPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_AUX)) break; - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_SHUTDOWN_AUX | + /* ProcessCheckpointerInterrupts() */ + INTERRUPT_BARRIER | + INTERRUPT_LOG_MEMORY_CONTEXT | + INTERRUPT_CONFIG_RELOAD, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, WAIT_EVENT_CHECKPOINTER_SHUTDOWN); @@ -637,12 +646,11 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len) static void ProcessCheckpointerInterrupts(void) { - if (ProcSignalBarrierPending) + if (InterruptPending(INTERRUPT_BARRIER)) ProcessProcSignalBarrier(); - if (ConfigReloadPending) + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) { - ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); /* @@ -660,7 +668,7 @@ ProcessCheckpointerInterrupts(void) } /* Perform logging of memory contexts of this process */ - if (LogMemoryContextPending) + if (InterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT)) ProcessLogMemoryContextInterrupt(); } @@ -778,14 +786,13 @@ CheckpointWriteDelay(int flags, double progress) * in which case we just try to catch up as quickly as possible. */ if (!(flags & CHECKPOINT_IMMEDIATE) && - !ShutdownXLOGPending && - !ShutdownRequestPending && + !InterruptPending(INTERRUPT_SHUTDOWN_XLOG) && + !InterruptPending(INTERRUPT_SHUTDOWN_AUX) && !ImmediateCheckpointRequested() && IsCheckpointOnSchedule(progress)) { - if (ConfigReloadPending) + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) { - ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); /* update shmem copies of config variables */ UpdateSharedMemoryConfig(); @@ -805,7 +812,8 @@ CheckpointWriteDelay(int flags, double progress) * Checkpointer and bgwriter are no longer related so take the Big * Sleep. */ - WaitInterrupt(1 << INTERRUPT_GENERAL, + WaitInterrupt(INTERRUPT_GENERAL | + INTERRUPT_CONFIG_RELOAD, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH | WL_TIMEOUT, 100, WAIT_EVENT_CHECKPOINT_WRITE_DELAY); @@ -823,7 +831,7 @@ CheckpointWriteDelay(int flags, double progress) } /* Check for barrier events. */ - if (ProcSignalBarrierPending) + if (InterruptPending(INTERRUPT_BARRIER)) ProcessProcSignalBarrier(); } @@ -917,8 +925,7 @@ IsCheckpointOnSchedule(double progress) static void ReqShutdownXLOG(SIGNAL_ARGS) { - ShutdownXLOGPending = true; - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_SHUTDOWN_XLOG); } diff --git a/src/backend/postmaster/interrupt_handlers.c b/src/backend/postmaster/interrupt_handlers.c index 10a4710f403..f08b9684e0b 100644 --- a/src/backend/postmaster/interrupt_handlers.c +++ b/src/backend/postmaster/interrupt_handlers.c @@ -24,29 +24,23 @@ #include "utils/guc.h" #include "utils/memutils.h" -volatile sig_atomic_t ConfigReloadPending = false; -volatile sig_atomic_t ShutdownRequestPending = false; - /* * Simple interrupt handler for main loops of background processes. */ void ProcessMainLoopInterrupts(void) { - if (ProcSignalBarrierPending) + if (InterruptPending(INTERRUPT_BARRIER)) ProcessProcSignalBarrier(); - if (ConfigReloadPending) - { - ConfigReloadPending = false; + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) ProcessConfigFile(PGC_SIGHUP); - } - if (ShutdownRequestPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_AUX)) proc_exit(0); /* Perform logging of memory contexts of this process */ - if (LogMemoryContextPending) + if (InterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT)) ProcessLogMemoryContextInterrupt(); } @@ -54,14 +48,13 @@ ProcessMainLoopInterrupts(void) * Simple signal handler for triggering a configuration reload. * * Normally, this handler would be used for SIGHUP. The idea is that code - * which uses it would arrange to check the ConfigReloadPending flag at - * convenient places inside main loops, or else call ProcessMainLoopInterrupts. + * which uses it would arrange to check the INTERRUPT_CONFIG_RELOAD interrupt at + * convenient places inside main loops, or else call HandleMainLoopInterrupts. */ void SignalHandlerForConfigReload(SIGNAL_ARGS) { - ConfigReloadPending = true; - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_CONFIG_RELOAD); } /* @@ -98,12 +91,11 @@ SignalHandlerForCrashExit(SIGNAL_ARGS) * writer and the logical replication parallel apply worker exits on either * SIGINT or SIGTERM. * - * ShutdownRequestPending should be checked at a convenient place within the + * INTERRUPT_SHUTDOWN_AUX should be checked at a convenient place within the * main loop, or else the main loop should call ProcessMainLoopInterrupts. */ void SignalHandlerForShutdownRequest(SIGNAL_ARGS) { - ShutdownRequestPending = true; - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_SHUTDOWN_AUX); } diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c index 1e921c0e651..7dde75cd709 100644 --- a/src/backend/postmaster/pgarch.c +++ b/src/backend/postmaster/pgarch.c @@ -231,7 +231,7 @@ PgArchiverMain(const void *startup_data, size_t startup_data_len) /* SIGQUIT handler was already set up by InitPostmasterChild */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, pgarch_waken_stop); /* Reset some signals that are accepted by postmaster but not here */ @@ -332,7 +332,7 @@ pgarch_MainLoop(void) * idea. If more than 60 seconds pass since SIGTERM, exit anyway, so * that the postmaster can start a new archiver if needed. */ - if (ShutdownRequestPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_AUX)) { time_t curtime = time(NULL); @@ -354,7 +354,11 @@ pgarch_MainLoop(void) { int rc; - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_GENERAL | + INTERRUPT_SHUTDOWN_AUX | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_BARRIER | + INTERRUPT_LOG_MEMORY_CONTEXT, WL_INTERRUPT | WL_TIMEOUT | WL_POSTMASTER_DEATH, PGARCH_AUTOWAKE_INTERVAL * 1000L, WAIT_EVENT_ARCHIVER_MAIN); @@ -406,7 +410,7 @@ pgarch_ArchiverCopyLoop(void) * command, and the second is to avoid conflicts with another * archiver spawned by a newer postmaster. */ - if (ShutdownRequestPending || !PostmasterIsAlive()) + if (InterruptPending(INTERRUPT_SHUTDOWN_AUX) || !PostmasterIsAlive()) return; /* @@ -857,19 +861,19 @@ pgarch_die(int code, Datum arg) static void ProcessPgArchInterrupts(void) { - if (ProcSignalBarrierPending) + if (InterruptPending(INTERRUPT_BARRIER)) ProcessProcSignalBarrier(); /* Perform logging of memory contexts of this process */ - if (LogMemoryContextPending) + if (InterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT)) ProcessLogMemoryContextInterrupt(); - if (ConfigReloadPending) + if (InterruptPending(INTERRUPT_CONFIG_RELOAD)) { char *archiveLib = pstrdup(XLogArchiveLibrary); bool archiveLibChanged; - ConfigReloadPending = false; + ClearInterrupt(INTERRUPT_CONFIG_RELOAD); ProcessConfigFile(PGC_SIGHUP); if (XLogArchiveLibrary[0] != '\0' && XLogArchiveCommand[0] != '\0') diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index fcc2511da43..9524c26a140 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -1621,7 +1621,8 @@ ConfigurePostmasterWaitSet(bool accept_connections) pm_wait_set = CreateWaitEventSet(NULL, accept_connections ? (1 + NumListenSockets) : 1); AddWaitEventToSet(pm_wait_set, WL_INTERRUPT, PGINVALID_SOCKET, - 1 << INTERRUPT_GENERAL, NULL); + INTERRUPT_CONFIG_RELOAD | INTERRUPT_GENERAL, + NULL); if (accept_connections) { diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c index f2ac862513b..1ebd10d684d 100644 --- a/src/backend/postmaster/startup.c +++ b/src/backend/postmaster/startup.c @@ -26,6 +26,7 @@ #include "miscadmin.h" #include "postmaster/auxprocess.h" #include "postmaster/startup.h" +#include "storage/interrupt.h" #include "storage/ipc.h" #include "storage/pmsignal.h" #include "storage/proc.h" @@ -50,7 +51,6 @@ /* * Flags set by interrupt handlers for later service in the redo loop. */ -static volatile sig_atomic_t got_SIGHUP = false; static volatile sig_atomic_t shutdown_requested = false; static volatile sig_atomic_t promote_signaled = false; @@ -101,8 +101,7 @@ StartupProcTriggerHandler(SIGNAL_ARGS) static void StartupProcSigHupHandler(SIGNAL_ARGS) { - got_SIGHUP = true; - WakeupRecovery(); + RaiseInterrupt(INTERRUPT_CONFIG_RELOAD); } /* SIGTERM: set flag to abort redo and exit */ @@ -161,11 +160,8 @@ ProcessStartupProcInterrupts(void) /* * Process any requests or signals received recently. */ - if (got_SIGHUP) - { - got_SIGHUP = false; + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) StartupRereadConfig(); - } /* * Check if we were requested to exit without finishing recovery. @@ -187,11 +183,11 @@ ProcessStartupProcInterrupts(void) exit(1); /* Process barrier events */ - if (ProcSignalBarrierPending) + if (InterruptPending(INTERRUPT_BARRIER)) ProcessProcSignalBarrier(); /* Perform logging of memory contexts of this process */ - if (LogMemoryContextPending) + if (InterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT)) ProcessLogMemoryContextInterrupt(); } @@ -241,7 +237,7 @@ StartupProcessMain(const void *startup_data, size_t startup_data_len) /* SIGQUIT handler was already set up by InitPostmasterChild */ InitializeTimeouts(); /* establishes SIGALRM handler */ pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, StartupProcTriggerHandler); /* diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c index 803dcde071b..4d9edb36cb3 100644 --- a/src/backend/postmaster/syslogger.c +++ b/src/backend/postmaster/syslogger.c @@ -338,7 +338,10 @@ SysLoggerMain(const void *startup_data, size_t startup_data_len) * (including the postmaster). */ wes = CreateWaitEventSet(NULL, 2); - AddWaitEventToSet(wes, WL_INTERRUPT, PGINVALID_SOCKET, 1 << INTERRUPT_GENERAL, NULL); + AddWaitEventToSet(wes, WL_INTERRUPT, PGINVALID_SOCKET, + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_GENERAL, /* for log rotation */ + NULL); #ifndef WIN32 AddWaitEventToSet(wes, WL_SOCKET_READABLE, syslogPipe[0], 0, NULL); #endif @@ -361,9 +364,8 @@ SysLoggerMain(const void *startup_data, size_t startup_data_len) /* * Process any requests or signals received recently. */ - if (ConfigReloadPending) + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) { - ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); /* diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c index 9288a1b299a..e3bfb613609 100644 --- a/src/backend/postmaster/walsummarizer.c +++ b/src/backend/postmaster/walsummarizer.c @@ -251,7 +251,7 @@ WalSummarizerMain(const void *startup_data, size_t startup_data_len) /* SIGQUIT handler was already set up by InitPostmasterChild */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); /* not used */ /* Advertise ourselves. */ @@ -316,7 +316,12 @@ WalSummarizerMain(const void *startup_data, size_t startup_data_len) * So a really fast retry time doesn't seem to be especially * beneficial, and it will clutter the logs. */ - (void) WaitInterrupt(0, WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10000, + (void) WaitInterrupt(INTERRUPT_GENERAL | + INTERRUPT_BARRIER | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_SHUTDOWN_AUX | + INTERRUPT_LOG_MEMORY_CONTEXT, + WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10000, WAIT_EVENT_WAL_SUMMARIZER_ERROR); } @@ -856,16 +861,13 @@ GetLatestLSN(TimeLineID *tli) static void ProcessWalSummarizerInterrupts(void) { - if (ProcSignalBarrierPending) + if (InterruptPending(INTERRUPT_BARRIER)) ProcessProcSignalBarrier(); - if (ConfigReloadPending) - { - ConfigReloadPending = false; + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) ProcessConfigFile(PGC_SIGHUP); - } - if (ShutdownRequestPending || !summarize_wal) + if (InterruptPending(INTERRUPT_SHUTDOWN_AUX) || !summarize_wal) { ereport(DEBUG1, errmsg_internal("WAL summarizer shutting down")); @@ -873,7 +875,7 @@ ProcessWalSummarizerInterrupts(void) } /* Perform logging of memory contexts of this process */ - if (LogMemoryContextPending) + if (InterruptPending(INTERRUPT_LOG_MEMORY_CONTEXT)) ProcessLogMemoryContextInterrupt(); } @@ -1639,7 +1641,11 @@ summarizer_wait_for_wal(void) pgstat_report_wal(false); /* OK, now sleep. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_GENERAL | + INTERRUPT_SHUTDOWN_AUX | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_BARRIER | + INTERRUPT_LOG_MEMORY_CONTEXT, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, sleep_quanta * MS_PER_SLEEP_QUANTUM, WAIT_EVENT_WAL_SUMMARIZER_WAL); diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c index 9678c01c5a3..d74ee8f5d0c 100644 --- a/src/backend/postmaster/walwriter.c +++ b/src/backend/postmaster/walwriter.c @@ -57,7 +57,6 @@ #include "storage/interrupt.h" #include "storage/lwlock.h" #include "storage/proc.h" -#include "storage/procsignal.h" #include "storage/smgr.h" #include "utils/hsearch.h" #include "utils/memutils.h" @@ -109,7 +108,7 @@ WalWriterMain(const void *startup_data, size_t startup_data_len) /* SIGQUIT handler was already set up by InitPostmasterChild */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); /* not used */ /* @@ -236,9 +235,6 @@ WalWriterMain(const void *startup_data, size_t startup_data_len) SetWalWriterSleeping(hibernating); } - /* Clear any already-pending wakeups */ - ClearInterrupt(INTERRUPT_GENERAL); - /* Process any signals received recently */ ProcessMainLoopInterrupts(); @@ -264,7 +260,7 @@ WalWriterMain(const void *startup_data, size_t startup_data_len) else cur_timeout = WalWriterDelay * HIBERNATE_FACTOR; - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_MAIN_LOOP_MASK, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, cur_timeout, WAIT_EVENT_WAL_WRITER_MAIN); diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c index 41137b4d373..3205efba6d4 100644 --- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c +++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c @@ -237,7 +237,8 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical, else io_flag = WL_SOCKET_WRITEABLE; - rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL, + rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK | + INTERRUPT_SHUTDOWN_AUX, WL_EXIT_ON_PM_DEATH | WL_INTERRUPT | io_flag, PQsocket(conn->streamConn), 0, @@ -245,10 +246,7 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical, /* Interrupted? */ if (rc & WL_INTERRUPT) - { - ClearInterrupt(INTERRUPT_GENERAL); ProcessWalRcvInterrupts(); - } /* If socket is ready, advance the libpq state machine */ if (rc & io_flag) @@ -848,7 +846,8 @@ libpqrcv_PQgetResult(PGconn *streamConn) * since we'll get interrupted by signals and can handle any * interrupts here. */ - rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL, + rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK | + INTERRUPT_SHUTDOWN_AUX, WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE | WL_INTERRUPT, PQsocket(streamConn), @@ -857,10 +856,7 @@ libpqrcv_PQgetResult(PGconn *streamConn) /* Interrupted? */ if (rc & WL_INTERRUPT) - { - ClearInterrupt(INTERRUPT_GENERAL); ProcessWalRcvInterrupts(); - } /* Consume whatever data is available from the socket */ if (PQconsumeInput(streamConn) == 0) diff --git a/src/backend/replication/logical/applyparallelworker.c b/src/backend/replication/logical/applyparallelworker.c index 5d27b4d5886..6dbdad622a5 100644 --- a/src/backend/replication/logical/applyparallelworker.c +++ b/src/backend/replication/logical/applyparallelworker.c @@ -239,12 +239,6 @@ static List *ParallelApplyWorkerPool = NIL; */ ParallelApplyWorkerShared *MyParallelShared = NULL; -/* - * Is there a message sent by a parallel apply worker that the leader apply - * worker needs to receive? - */ -volatile sig_atomic_t ParallelApplyMessagePending = false; - /* * Cache the parallel apply worker information required for applying the * current streaming transaction. It is used to save the cost of searching the @@ -714,7 +708,7 @@ ProcessParallelApplyInterrupts(void) { CHECK_FOR_INTERRUPTS(); - if (ShutdownRequestPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_AUX)) { ereport(LOG, (errmsg("logical replication parallel apply worker for subscription \"%s\" has finished", @@ -723,11 +717,8 @@ ProcessParallelApplyInterrupts(void) proc_exit(0); } - if (ConfigReloadPending) - { - ConfigReloadPending = false; + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) ProcessConfigFile(PGC_SIGHUP); - } } /* Parallel apply worker main loop. */ @@ -805,7 +796,10 @@ LogicalParallelApplyLoop(shm_mq_handle *mqh) int rc; /* Wait for more work. */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | + INTERRUPT_SHUTDOWN_AUX | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 1000L, WAIT_EVENT_LOGICAL_PARALLEL_APPLY_MAIN); @@ -844,9 +838,8 @@ LogicalParallelApplyLoop(shm_mq_handle *mqh) static void pa_shutdown(int code, Datum arg) { - SendProcSignal(MyLogicalRepWorker->leader_pid, - PROCSIG_PARALLEL_APPLY_MESSAGE, - INVALID_PROC_NUMBER); + SendInterrupt(INTERRUPT_PARALLEL_APPLY_MESSAGE, + MyLogicalRepWorker->leader_pgprocno); dsm_detach((dsm_segment *) DatumGetPointer(arg)); } @@ -934,8 +927,7 @@ ParallelApplyWorkerMain(Datum main_arg) error_mqh = shm_mq_attach(mq, seg, NULL); pq_redirect_to_shm_mq(seg, error_mqh); - pq_set_parallel_leader(MyLogicalRepWorker->leader_pid, - INVALID_PROC_NUMBER); + pq_set_parallel_leader(MyLogicalRepWorker->leader_pgprocno); MyLogicalRepWorker->last_send_time = MyLogicalRepWorker->last_recv_time = MyLogicalRepWorker->reply_time = 0; @@ -979,21 +971,6 @@ ParallelApplyWorkerMain(Datum main_arg) Assert(false); } -/* - * Handle receipt of an interrupt indicating a parallel apply worker message. - * - * Note: this is called within a signal handler! All we can do is set a flag - * that will cause the next CHECK_FOR_INTERRUPTS() to invoke - * ProcessParallelApplyMessages(). - */ -void -HandleParallelApplyMessageInterrupt(void) -{ - InterruptPending = true; - ParallelApplyMessagePending = true; - RaiseInterrupt(INTERRUPT_GENERAL); -} - /* * Process a single protocol message received from a single parallel apply * worker. @@ -1058,7 +1035,7 @@ ProcessParallelApplyMessage(StringInfo msg) } /* - * Handle any queued protocol messages received from parallel apply workers. + * Process any queued protocol messages received from parallel apply workers. */ void ProcessParallelApplyMessages(void) @@ -1091,7 +1068,7 @@ ProcessParallelApplyMessages(void) oldcontext = MemoryContextSwitchTo(hpam_context); - ParallelApplyMessagePending = false; + ClearInterrupt(INTERRUPT_PARALLEL_APPLY_MESSAGE); foreach(lc, ParallelApplyWorkerPool) { @@ -1183,15 +1160,15 @@ pa_send_data(ParallelApplyWorkerInfo *winfo, Size nbytes, const void *data) Assert(result == SHM_MQ_WOULD_BLOCK); /* Wait before retrying. */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, SHM_SEND_RETRY_INTERVAL_MS, WAIT_EVENT_LOGICAL_APPLY_SEND_DATA); if (rc & WL_INTERRUPT) { - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); } if (startTime == 0) @@ -1255,16 +1232,16 @@ pa_wait_for_xact_state(ParallelApplyWorkerInfo *winfo, break; /* Wait to be signalled. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10L, WAIT_EVENT_LOGICAL_PARALLEL_APPLY_STATE_CHANGE); - /* Clear the interrupt flag so we don't spin. */ - ClearInterrupt(INTERRUPT_GENERAL); - /* An interrupt may have occurred while we were waiting. */ CHECK_FOR_INTERRUPTS(); + + /* Clear the interrupt flag so we don't spin. */ + ClearInterrupt(INTERRUPT_GENERAL); } } diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c index ab33444a293..3fbb31df2d1 100644 --- a/src/backend/replication/logical/launcher.c +++ b/src/backend/replication/logical/launcher.c @@ -214,14 +214,14 @@ WaitForReplicationWorkerAttach(LogicalRepWorker *worker, * interrupt about the worker attach. But we don't expect to have to * wait long. */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10L, WAIT_EVENT_BGWORKER_STARTUP); if (rc & WL_INTERRUPT) { - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); } } } @@ -442,6 +442,7 @@ retry: worker->relstate_lsn = InvalidXLogRecPtr; worker->stream_fileset = NULL; worker->leader_pid = is_parallel_apply_worker ? MyProcPid : InvalidPid; + worker->leader_pgprocno = is_parallel_apply_worker ? MyProcNumber : INVALID_PROC_NUMBER; worker->parallel_apply = is_parallel_apply_worker; worker->last_lsn = InvalidXLogRecPtr; TIMESTAMP_NOBEGIN(worker->last_send_time); @@ -522,7 +523,7 @@ retry: * slot. */ static void -logicalrep_worker_stop_internal(LogicalRepWorker *worker, int signo) +logicalrep_worker_stop_internal(LogicalRepWorker *worker, int interrupt) { uint16 generation; @@ -545,14 +546,14 @@ logicalrep_worker_stop_internal(LogicalRepWorker *worker, int signo) LWLockRelease(LogicalRepWorkerLock); /* Wait a bit --- we don't expect to have to wait long. */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10L, WAIT_EVENT_BGWORKER_STARTUP); if (rc & WL_INTERRUPT) { - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); } /* Recheck worker status. */ @@ -572,7 +573,7 @@ logicalrep_worker_stop_internal(LogicalRepWorker *worker, int signo) } /* Now terminate the worker ... */ - kill(worker->proc->pid, signo); + SendInterrupt(interrupt, GetNumberFromPGProc(worker->proc)); /* ... and wait for it to die. */ for (;;) @@ -586,14 +587,14 @@ logicalrep_worker_stop_internal(LogicalRepWorker *worker, int signo) LWLockRelease(LogicalRepWorkerLock); /* Wait a bit --- we don't expect to have to wait long. */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10L, WAIT_EVENT_BGWORKER_SHUTDOWN); if (rc & WL_INTERRUPT) { - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); } LWLockAcquire(LogicalRepWorkerLock, LW_SHARED); @@ -615,7 +616,7 @@ logicalrep_worker_stop(Oid subid, Oid relid) if (worker) { Assert(!isParallelApplyWorker(worker)); - logicalrep_worker_stop_internal(worker, SIGTERM); + logicalrep_worker_stop_internal(worker, INTERRUPT_DIE); } LWLockRelease(LogicalRepWorkerLock); @@ -662,13 +663,14 @@ logicalrep_pa_worker_stop(ParallelApplyWorkerInfo *winfo) * Only stop the worker if the generation matches and the worker is alive. */ if (worker->generation == generation && worker->proc) - logicalrep_worker_stop_internal(worker, SIGINT); + logicalrep_worker_stop_internal(worker, INTERRUPT_QUERY_CANCEL); LWLockRelease(LogicalRepWorkerLock); } /* - * Wake up (using interrupt) any logical replication worker for specified sub/rel. + * Wake up (using INTERRUPT_GENERAL) any logical replication worker for + * specified sub/rel. */ void logicalrep_worker_wakeup(Oid subid, Oid relid) @@ -763,7 +765,7 @@ logicalrep_worker_detach(void) LogicalRepWorker *w = (LogicalRepWorker *) lfirst(lc); if (isParallelApplyWorker(w)) - logicalrep_worker_stop_internal(w, SIGTERM); + logicalrep_worker_stop_internal(w, INTERRUPT_DIE); } LWLockRelease(LogicalRepWorkerLock); @@ -793,6 +795,7 @@ logicalrep_worker_cleanup(LogicalRepWorker *worker) worker->subid = InvalidOid; worker->relid = InvalidOid; worker->leader_pid = InvalidPid; + worker->leader_pgprocno = INVALID_PROC_NUMBER; worker->parallel_apply = false; } @@ -1218,22 +1221,20 @@ ApplyLauncherMain(Datum main_arg) MemoryContextDelete(subctx); /* Wait for more work. */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL | - 1 << INTERRUPT_SUBSCRIPTION_CHANGE, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_SUBSCRIPTION_CHANGE | + INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, wait_time, WAIT_EVENT_LOGICAL_LAUNCHER_MAIN); if (rc & WL_INTERRUPT) { - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); - } - - if (ConfigReloadPending) - { - ConfigReloadPending = false; - ProcessConfigFile(PGC_SIGHUP); + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) + ProcessConfigFile(PGC_SIGHUP); + ClearInterrupt(INTERRUPT_GENERAL); } } diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c index 505cf21c143..5d1f535e7c6 100644 --- a/src/backend/replication/logical/slotsync.c +++ b/src/backend/replication/logical/slotsync.c @@ -73,9 +73,9 @@ /* * Struct for sharing information to control slot synchronization. * - * The slot sync worker's pid is needed by the startup process to shut it - * down during promotion. The startup process shuts down the slot sync worker - * and also sets stopSignaled=true to handle the race condition when the + * The slot sync worker's proc number is needed by the startup process to shut + * it down during promotion. The startup process shuts down the slot sync + * worker and also sets stopSignaled=true to handle the race condition when the * postmaster has not noticed the promotion yet and thus may end up restarting * the slot sync worker. If stopSignaled is set, the worker will exit in such a * case. The SQL function pg_sync_replication_slots() will also error out if @@ -95,7 +95,7 @@ */ typedef struct SlotSyncCtxStruct { - pid_t pid; + ProcNumber procno; bool stopSignaled; bool syncing; time_t last_start_time; @@ -1113,7 +1113,7 @@ slotsync_reread_config(void) Assert(sync_replication_slots); - ConfigReloadPending = false; + ClearInterrupt(INTERRUPT_CONFIG_RELOAD); ProcessConfigFile(PGC_SIGHUP); conninfo_changed = strcmp(old_primary_conninfo, PrimaryConnInfo) != 0; @@ -1155,7 +1155,7 @@ ProcessSlotSyncInterrupts(WalReceiverConn *wrconn) { CHECK_FOR_INTERRUPTS(); - if (ShutdownRequestPending) + if (InterruptPending(INTERRUPT_DIE)) { ereport(LOG, errmsg("replication slot synchronization worker is shutting down on receiving SIGINT")); @@ -1163,7 +1163,7 @@ ProcessSlotSyncInterrupts(WalReceiverConn *wrconn) proc_exit(0); } - if (ConfigReloadPending) + if (InterruptPending(INTERRUPT_CONFIG_RELOAD)) slotsync_reread_config(); } @@ -1207,7 +1207,7 @@ slotsync_worker_onexit(int code, Datum arg) SpinLockAcquire(&SlotSyncCtx->mutex); - SlotSyncCtx->pid = InvalidPid; + SlotSyncCtx->procno = INVALID_PROC_NUMBER; /* * If syncing_slots is true, it indicates that the process errored out @@ -1253,7 +1253,9 @@ wait_for_slot_activity(bool some_slot_updated) sleep_ms = MIN_SLOTSYNC_WORKER_NAPTIME_MS; } - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | + INTERRUPT_GENERAL | + INTERRUPT_CONFIG_RELOAD, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, sleep_ms, WAIT_EVENT_REPLICATION_SLOTSYNC_MAIN); @@ -1267,12 +1269,12 @@ wait_for_slot_activity(bool some_slot_updated) * Otherwise, advertise that a sync is in progress. */ static void -check_and_set_sync_info(pid_t worker_pid) +check_and_set_sync_info(ProcNumber worker_procno) { SpinLockAcquire(&SlotSyncCtx->mutex); /* The worker pid must not be already assigned in SlotSyncCtx */ - Assert(worker_pid == InvalidPid || SlotSyncCtx->pid == InvalidPid); + Assert(worker_procno == INVALID_PROC_NUMBER || SlotSyncCtx->procno == INVALID_PROC_NUMBER); /* * Emit an error if startup process signaled the slot sync machinery to @@ -1297,10 +1299,10 @@ check_and_set_sync_info(pid_t worker_pid) SlotSyncCtx->syncing = true; /* - * Advertise the required PID so that the startup process can kill the - * slot sync worker on promotion. + * Advertise the required proc number so that the startup process can kill + * the slot sync worker on promotion. */ - SlotSyncCtx->pid = worker_pid; + SlotSyncCtx->procno = worker_procno; SpinLockRelease(&SlotSyncCtx->mutex); @@ -1392,16 +1394,16 @@ ReplSlotSyncWorkerMain(const void *startup_data, size_t startup_data_len) pqsignal(SIGINT, SignalHandlerForShutdownRequest); pqsignal(SIGTERM, die); pqsignal(SIGFPE, FloatExceptionHandler); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGCHLD, SIG_DFL); - check_and_set_sync_info(MyProcPid); + check_and_set_sync_info(MyProcNumber); ereport(LOG, errmsg("slot sync worker started")); - /* Register it as soon as SlotSyncCtx->pid is initialized. */ + /* Register it as soon as SlotSyncCtx->procno is initialized. */ before_shmem_exit(slotsync_worker_onexit, (Datum) 0); /* @@ -1522,7 +1524,7 @@ update_synced_slots_inactive_since(void) return; /* The slot sync worker or SQL function mustn't be running by now */ - Assert((SlotSyncCtx->pid == InvalidPid) && !SlotSyncCtx->syncing); + Assert((SlotSyncCtx->procno == INVALID_PROC_NUMBER) && !SlotSyncCtx->syncing); LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); @@ -1536,7 +1538,7 @@ update_synced_slots_inactive_since(void) Assert(SlotIsLogical(s)); /* The slot must not be acquired by any process */ - Assert(s->active_pid == 0); + Assert(s->active_proc == INVALID_PROC_NUMBER); /* Use the same inactive_since time for all the slots. */ if (now == 0) @@ -1559,7 +1561,7 @@ update_synced_slots_inactive_since(void) void ShutDownSlotSync(void) { - pid_t worker_pid; + ProcNumber worker_procno; SpinLockAcquire(&SlotSyncCtx->mutex); @@ -1576,12 +1578,12 @@ ShutDownSlotSync(void) return; } - worker_pid = SlotSyncCtx->pid; + worker_procno = SlotSyncCtx->procno; SpinLockRelease(&SlotSyncCtx->mutex); - if (worker_pid != InvalidPid) - kill(worker_pid, SIGINT); + if (worker_procno != INVALID_PROC_NUMBER) + SendInterrupt(INTERRUPT_QUERY_CANCEL, worker_procno); /* Wait for slot sync to end */ for (;;) @@ -1589,14 +1591,15 @@ ShutDownSlotSync(void) int rc; /* Wait a bit, we don't expect to have to wait long */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | + INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 10L, WAIT_EVENT_REPLICATION_SLOTSYNC_SHUTDOWN); if (rc & WL_INTERRUPT) { - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); } SpinLockAcquire(&SlotSyncCtx->mutex); @@ -1674,7 +1677,7 @@ SlotSyncShmemInit(void) if (!found) { memset(SlotSyncCtx, 0, size); - SlotSyncCtx->pid = InvalidPid; + SlotSyncCtx->procno = INVALID_PROC_NUMBER; SpinLockInit(&SlotSyncCtx->mutex); } } @@ -1724,7 +1727,7 @@ SyncReplicationSlots(WalReceiverConn *wrconn) { PG_ENSURE_ERROR_CLEANUP(slotsync_failure_callback, PointerGetDatum(wrconn)); { - check_and_set_sync_info(InvalidPid); + check_and_set_sync_info(INVALID_PROC_NUMBER); validate_remote_info(wrconn); diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c index 877fb7aca36..eb2a4993f17 100644 --- a/src/backend/replication/logical/tablesync.c +++ b/src/backend/replication/logical/tablesync.c @@ -211,7 +211,7 @@ wait_for_relation_state_change(Oid relid, char expected_state) if (!worker) break; - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 1000L, WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE); @@ -261,10 +261,10 @@ wait_for_worker_state_change(char expected_state) break; /* - * Wait. We expect to get an interrupt wakeup from the apply worker, - * but use a timeout in case it dies without sending one. + * Wait. We expect to get an INTERRUPT_GENERAL wakeup from the apply + * worker, but use a timeout in case it dies without sending one. */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, 1000L, WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE); @@ -774,12 +774,10 @@ copy_read_data(void *outbuf, int minread, int maxread) /* * Wait for more data or interrupt. */ - (void) WaitInterruptOrSocket(1 << INTERRUPT_GENERAL, + (void) WaitInterruptOrSocket(INTERRUPT_CFI_MASK, WL_SOCKET_READABLE | WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, fd, 1000L, WAIT_EVENT_LOGICAL_SYNC_DATA); - - ClearInterrupt(INTERRUPT_GENERAL); } return bytesread; diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 29d03af0ac5..71b93d4945f 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -3655,11 +3655,8 @@ LogicalRepApplyLoop(XLogRecPtr last_received) int c; StringInfoData s; - if (ConfigReloadPending) - { - ConfigReloadPending = false; + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) ProcessConfigFile(PGC_SIGHUP); - } /* Reset timeout. */ last_recv_timestamp = GetCurrentTimestamp(); @@ -3754,7 +3751,9 @@ LogicalRepApplyLoop(XLogRecPtr last_received) else wait_time = NAPTIME_PER_CYCLE; - rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL, + rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_GENERAL, WL_SOCKET_READABLE | WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, fd, wait_time, @@ -3762,14 +3761,10 @@ LogicalRepApplyLoop(XLogRecPtr last_received) if (rc & WL_INTERRUPT) { - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); - } - - if (ConfigReloadPending) - { - ConfigReloadPending = false; - ProcessConfigFile(PGC_SIGHUP); + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) + ProcessConfigFile(PGC_SIGHUP); + ClearInterrupt(INTERRUPT_GENERAL); } if (rc & WL_TIMEOUT) diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index ca271330594..84be4a050f3 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -51,6 +51,7 @@ #include "replication/slot.h" #include "replication/walsender_private.h" #include "storage/fd.h" +#include "storage/interrupt.h" #include "storage/ipc.h" #include "storage/proc.h" #include "storage/procarray.h" @@ -224,6 +225,7 @@ ReplicationSlotsShmemInit(void) ReplicationSlot *slot = &ReplicationSlotCtl->replication_slots[i]; /* everything else is zeroed by the memset above */ + slot->active_proc = INVALID_PROC_NUMBER; SpinLockInit(&slot->mutex); LWLockInitialize(&slot->io_in_progress_lock, LWTRANCHE_REPLICATION_SLOT_IO); @@ -402,7 +404,7 @@ ReplicationSlotCreate(const char *name, bool db_specific, * be doing that. So it's safe to initialize the slot. */ Assert(!slot->in_use); - Assert(slot->active_pid == 0); + Assert(slot->active_proc == INVALID_PROC_NUMBER); /* first initialize persistent data */ memset(&slot->data, 0, sizeof(ReplicationSlotPersistentData)); @@ -444,8 +446,8 @@ ReplicationSlotCreate(const char *name, bool db_specific, /* We can now mark the slot active, and that makes it our slot. */ SpinLockAcquire(&slot->mutex); - Assert(slot->active_pid == 0); - slot->active_pid = MyProcPid; + Assert(slot->active_proc == INVALID_PROC_NUMBER); + slot->active_proc = MyProcNumber; SpinLockRelease(&slot->mutex); MyReplicationSlot = slot; @@ -559,7 +561,7 @@ void ReplicationSlotAcquire(const char *name, bool nowait, bool error_if_invalid) { ReplicationSlot *s; - int active_pid; + ProcNumber active_proc; Assert(name != NULL); @@ -600,15 +602,15 @@ retry: * to inactive_since in InvalidatePossiblyObsoleteSlot. */ SpinLockAcquire(&s->mutex); - if (s->active_pid == 0) - s->active_pid = MyProcPid; - active_pid = s->active_pid; + if (s->active_proc == INVALID_PROC_NUMBER) + s->active_proc = MyProcNumber; + active_proc = s->active_proc; ReplicationSlotSetInactiveSince(s, 0, false); SpinLockRelease(&s->mutex); } else { - active_pid = MyProcPid; + active_proc = MyProcNumber; ReplicationSlotSetInactiveSince(s, 0, true); } LWLockRelease(ReplicationSlotControlLock); @@ -618,7 +620,7 @@ retry: * wait until the owning process signals us that it's been released, or * error out. */ - if (active_pid != MyProcPid) + if (active_proc != MyProcNumber) { if (!nowait) { @@ -632,7 +634,7 @@ retry: ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("replication slot \"%s\" is active for PID %d", - NameStr(s->data.name), active_pid))); + NameStr(s->data.name), GetPGProcByNumber(active_proc)->pid))); } else if (!nowait) ConditionVariableCancelSleep(); /* no sleep needed after all */ @@ -690,7 +692,7 @@ ReplicationSlotRelease(void) bool is_logical = false; /* keep compiler quiet */ TimestampTz now = 0; - Assert(slot != NULL && slot->active_pid != 0); + Assert(slot != NULL && slot->active_proc != INVALID_PROC_NUMBER); if (am_walsender) { @@ -736,7 +738,7 @@ ReplicationSlotRelease(void) * disconnecting, but wake up others that may be waiting for it. */ SpinLockAcquire(&slot->mutex); - slot->active_pid = 0; + slot->active_proc = INVALID_PROC_NUMBER; ReplicationSlotSetInactiveSince(slot, now, false); SpinLockRelease(&slot->mutex); ConditionVariableBroadcast(&slot->active_cv); @@ -788,7 +790,7 @@ restart: continue; SpinLockAcquire(&s->mutex); - if ((s->active_pid == MyProcPid && + if ((s->active_proc == MyProcNumber && (!synced_only || s->data.synced))) { Assert(s->data.persistency == RS_TEMPORARY); @@ -976,7 +978,7 @@ ReplicationSlotDropPtr(ReplicationSlot *slot) bool fail_softly = slot->data.persistency != RS_PERSISTENT; SpinLockAcquire(&slot->mutex); - slot->active_pid = 0; + slot->active_proc = INVALID_PROC_NUMBER; SpinLockRelease(&slot->mutex); /* wake up anyone waiting on this slot */ @@ -998,7 +1000,7 @@ ReplicationSlotDropPtr(ReplicationSlot *slot) * Also wake up processes waiting for it. */ LWLockAcquire(ReplicationSlotControlLock, LW_EXCLUSIVE); - slot->active_pid = 0; + slot->active_proc = INVALID_PROC_NUMBER; slot->in_use = false; LWLockRelease(ReplicationSlotControlLock); ConditionVariableBroadcast(&slot->active_cv); @@ -1293,7 +1295,7 @@ ReplicationSlotsCountDBSlots(Oid dboid, int *nslots, int *nactive) /* count slots with spinlock held */ SpinLockAcquire(&s->mutex); (*nslots)++; - if (s->active_pid != 0) + if (s->active_proc != INVALID_PROC_NUMBER) (*nactive)++; SpinLockRelease(&s->mutex); } @@ -1331,7 +1333,7 @@ restart: { ReplicationSlot *s; char *slotname; - int active_pid; + ProcNumber active_proc; s = &ReplicationSlotCtl->replication_slots[i]; @@ -1353,11 +1355,11 @@ restart: SpinLockAcquire(&s->mutex); /* can't change while ReplicationSlotControlLock is held */ slotname = NameStr(s->data.name); - active_pid = s->active_pid; - if (active_pid == 0) + active_proc = s->active_proc; + if (active_proc == INVALID_PROC_NUMBER) { MyReplicationSlot = s; - s->active_pid = MyProcPid; + s->active_proc = MyProcNumber; } SpinLockRelease(&s->mutex); @@ -1382,11 +1384,11 @@ restart: * XXX: We can consider shutting down the slot sync worker before * trying to drop synced temporary slots here. */ - if (active_pid) + if (active_proc != INVALID_PROC_NUMBER) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("replication slot \"%s\" is active for PID %d", - slotname, active_pid))); + slotname, GetPGProcByNumber(active_proc)->pid))); /* * To avoid duplicating ReplicationSlotDropAcquired() and to avoid @@ -1721,7 +1723,7 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes, Oid dboid, TransactionId snapshotConflictHorizon, bool *invalidated) { - int last_signaled_pid = 0; + ProcNumber last_signaled_proc = INVALID_PROC_NUMBER; bool released_lock = false; bool terminated = false; TransactionId initial_effective_xmin = InvalidTransactionId; @@ -1734,7 +1736,7 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes, { XLogRecPtr restart_lsn; NameData slotname; - int active_pid = 0; + ProcNumber active_proc = INVALID_PROC_NUMBER; ReplicationSlotInvalidationCause invalidation_cause = RS_INVAL_NONE; TimestampTz now = 0; long slot_idle_secs = 0; @@ -1817,17 +1819,17 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes, } slotname = s->data.name; - active_pid = s->active_pid; + active_proc = s->active_proc; /* * If the slot can be acquired, do so and mark it invalidated * immediately. Otherwise we'll signal the owning process, below, and * retry. */ - if (active_pid == 0) + if (active_proc == INVALID_PROC_NUMBER) { MyReplicationSlot = s; - s->active_pid = MyProcPid; + s->active_proc = MyProcNumber; s->data.invalidated = invalidation_cause; /* @@ -1864,7 +1866,7 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes, &slot_idle_usecs); } - if (active_pid != 0) + if (active_proc != INVALID_PROC_NUMBER) { /* * Prepare the sleep on the slot's condition variable before @@ -1887,22 +1889,25 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes, * process owns it. To handle that, we signal only if the PID of * the owning process has changed from the previous time. (This * logic assumes that the same PID is not reused very quickly.) + * + * FIXME: need another check now that we're using ProcNumbers, which + * do get recycled quickly */ - if (last_signaled_pid != active_pid) + if (last_signaled_proc != active_proc) { - ReportSlotInvalidation(invalidation_cause, true, active_pid, + ReportSlotInvalidation(invalidation_cause, true, + GetPGProcByNumber(active_proc)->pid, slotname, restart_lsn, oldestLSN, snapshotConflictHorizon, slot_idle_secs); if (MyBackendType == B_STARTUP) - (void) SendProcSignal(active_pid, - PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT, - INVALID_PROC_NUMBER); + SendInterrupt(INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT, + active_proc); else - (void) kill(active_pid, SIGTERM); + SendInterrupt(INTERRUPT_DIE, active_proc); - last_signaled_pid = active_pid; + last_signaled_proc = active_proc; terminated = true; invalidation_cause_prev = invalidation_cause; } @@ -1937,7 +1942,8 @@ InvalidatePossiblyObsoleteSlot(uint32 possible_causes, ReplicationSlotSave(); ReplicationSlotRelease(); - ReportSlotInvalidation(invalidation_cause, false, active_pid, + ReportSlotInvalidation(invalidation_cause, false, + GetPGProcByNumber(active_proc)->pid, slotname, restart_lsn, oldestLSN, snapshotConflictHorizon, slot_idle_secs); @@ -2576,7 +2582,7 @@ RestoreSlotFromDisk(const char *name) slot->candidate_restart_valid = InvalidXLogRecPtr; slot->in_use = true; - slot->active_pid = 0; + slot->active_proc = INVALID_PROC_NUMBER; /* * Set the time since the slot has become inactive after loading the @@ -2884,7 +2890,7 @@ StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel) SpinLockAcquire(&slot->mutex); restart_lsn = slot->data.restart_lsn; invalidated = slot->data.invalidated != RS_INVAL_NONE; - inactive = slot->active_pid == 0; + inactive = slot->active_proc == INVALID_PROC_NUMBER; SpinLockRelease(&slot->mutex); if (invalidated) @@ -2970,11 +2976,8 @@ WaitForStandbyConfirmation(XLogRecPtr wait_for_lsn) { CHECK_FOR_INTERRUPTS(); - if (ConfigReloadPending) - { - ConfigReloadPending = false; + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) ProcessConfigFile(PGC_SIGHUP); - } /* Exit if done waiting for every slot. */ if (StandbySlotsHaveCaughtup(wait_for_lsn, WARNING)) @@ -2984,6 +2987,8 @@ WaitForStandbyConfirmation(XLogRecPtr wait_for_lsn) * Wait for the slots in the synchronized_standby_slots to catch up, * but use a timeout (1s) so we can also check if the * synchronized_standby_slots has been changed. + * + * FIXME: I think we need a wait to add a CV to WaitEventSet */ ConditionVariableTimedSleep(&WalSndCtl->wal_confirm_rcv_cv, 1000, WAIT_EVENT_WAIT_FOR_STANDBY_CONFIRMATION); diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c index 146eef5871e..6820b131465 100644 --- a/src/backend/replication/slotfuncs.c +++ b/src/backend/replication/slotfuncs.c @@ -20,6 +20,7 @@ #include "replication/logical.h" #include "replication/slot.h" #include "replication/slotsync.h" +#include "storage/proc.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/pg_lsn.h" @@ -255,6 +256,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS) { ReplicationSlot *slot = &ReplicationSlotCtl->replication_slots[slotno]; ReplicationSlot slot_contents; + int active_pid; Datum values[PG_GET_REPLICATION_SLOTS_COLS]; bool nulls[PG_GET_REPLICATION_SLOTS_COLS]; WALAvailability walstate; @@ -267,6 +269,10 @@ pg_get_replication_slots(PG_FUNCTION_ARGS) /* Copy slot contents while holding spinlock, then examine at leisure */ SpinLockAcquire(&slot->mutex); slot_contents = *slot; + if (slot_contents.active_proc != INVALID_PROC_NUMBER) + active_pid = GetPGProcByNumber(slot_contents.active_proc)->pid; + else + active_pid = 0; SpinLockRelease(&slot->mutex); memset(values, 0, sizeof(values)); @@ -291,10 +297,10 @@ pg_get_replication_slots(PG_FUNCTION_ARGS) values[i++] = ObjectIdGetDatum(slot_contents.data.database); values[i++] = BoolGetDatum(slot_contents.data.persistency == RS_TEMPORARY); - values[i++] = BoolGetDatum(slot_contents.active_pid != 0); + values[i++] = BoolGetDatum(slot_contents.active_proc != INVALID_PROC_NUMBER); - if (slot_contents.active_pid != 0) - values[i++] = Int32GetDatum(slot_contents.active_pid); + if (slot_contents.active_proc != INVALID_PROC_NUMBER) + values[i++] = Int32GetDatum(active_pid); else nulls[i++] = true; @@ -359,13 +365,13 @@ pg_get_replication_slots(PG_FUNCTION_ARGS) */ if (!XLogRecPtrIsInvalid(slot_contents.data.restart_lsn)) { - int pid; + ProcNumber procno; SpinLockAcquire(&slot->mutex); - pid = slot->active_pid; + procno = slot->active_proc; slot_contents.data.restart_lsn = slot->data.restart_lsn; SpinLockRelease(&slot->mutex); - if (pid != 0) + if (procno != INVALID_PROC_NUMBER) { values[i++] = CStringGetTextDatum("unreserved"); walstate = WALAVAIL_UNRESERVED; diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c index 1c0d79b8568..444ef498cec 100644 --- a/src/backend/replication/syncrep.c +++ b/src/backend/replication/syncrep.c @@ -252,10 +252,10 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit) * entitled to assume that an acknowledged commit is also replicated, * which might not be true. So in this case we issue a WARNING (which * some clients may be able to interpret) and shut off further output. - * We do NOT reset ProcDiePending, so that the process will die after + * We do NOT clear the interrupt bit, so that the process will die after * the commit is cleaned up. */ - if (ProcDiePending) + if (InterruptPending(INTERRUPT_DIE)) { ereport(WARNING, (errcode(ERRCODE_ADMIN_SHUTDOWN), @@ -272,9 +272,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit) * altogether is not helpful, so we just terminate the wait with a * suitable warning. */ - if (QueryCancelPending) + if (ConsumeInterrupt(INTERRUPT_QUERY_CANCEL)) { - QueryCancelPending = false; ereport(WARNING, (errmsg("canceling wait for synchronous replication due to user request"), errdetail("The transaction has already committed locally, but might not have been replicated to the standby."))); @@ -286,7 +285,9 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit) * Wait on interrupt. Any condition that should wake us up will set * the interrupt, so no need for timeout. */ - rc = WaitInterrupt(1 << INTERRUPT_GENERAL, + rc = WaitInterrupt(INTERRUPT_GENERAL | + INTERRUPT_DIE | + INTERRUPT_QUERY_CANCEL, WL_INTERRUPT | WL_POSTMASTER_DEATH, -1, WAIT_EVENT_SYNC_REP); @@ -297,7 +298,7 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit) */ if (rc & WL_POSTMASTER_DEATH) { - ProcDiePending = true; + RaiseInterrupt(INTERRUPT_DIE); whereToSendOutput = DestNone; SyncRepCancelWait(); break; diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c index 4bc23ae21a3..cee0fd2a08f 100644 --- a/src/backend/replication/walreceiver.c +++ b/src/backend/replication/walreceiver.c @@ -71,7 +71,6 @@ #include "storage/ipc.h" #include "storage/proc.h" #include "storage/procarray.h" -#include "storage/procsignal.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/guc.h" @@ -170,7 +169,7 @@ ProcessWalRcvInterrupts(void) */ CHECK_FOR_INTERRUPTS(); - if (ShutdownRequestPending) + if (InterruptPending(INTERRUPT_SHUTDOWN_AUX)) { ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), @@ -285,7 +284,7 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len) /* SIGQUIT handler was already set up by InitPostmasterChild */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); /* Reset some signals that are accepted by postmaster but not here */ @@ -462,9 +461,8 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len) /* Process any requests or signals received recently */ ProcessWalRcvInterrupts(); - if (ConfigReloadPending) + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) { - ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); /* recompute wakeup times */ now = GetCurrentTimestamp(); @@ -547,7 +545,10 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len) * avoiding some system calls. */ Assert(wait_fd != PGINVALID_SOCKET); - rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL, + rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK | + INTERRUPT_SHUTDOWN_AUX | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_GENERAL, WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT | WL_INTERRUPT, wait_fd, @@ -555,7 +556,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len) WAIT_EVENT_WAL_RECEIVER_MAIN); if (rc & WL_INTERRUPT) { - ClearInterrupt(INTERRUPT_GENERAL); ProcessWalRcvInterrupts(); if (walrcv->force_reply) @@ -570,6 +570,7 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len) pg_memory_barrier(); XLogWalRcvSendReply(true, false); } + ClearInterrupt(INTERRUPT_GENERAL); } if (rc & WL_TIMEOUT) { @@ -735,7 +736,9 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI) } SpinLockRelease(&walrcv->mutex); - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | + INTERRUPT_SHUTDOWN_AUX | + INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, WAIT_EVENT_WAL_RECEIVER_WAIT_START); diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 4cafa69b89c..5ac203ef4fd 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -25,7 +25,7 @@ * the walsender will exit quickly without sending any more XLOG records. * * If the server is shut down, checkpointer sends us - * PROCSIG_WALSND_INIT_STOPPING after all regular backends have exited. If + * INTERRUPT_WALSND_INIT_STOPPING after all regular backends have exited. If * the backend is idle or runs an SQL query this causes the backend to * shutdown, if logical replication is in progress all existing WAL records * are processed followed by a shutdown. Otherwise this causes the walsender @@ -200,9 +200,9 @@ static volatile sig_atomic_t got_STOPPING = false; /* * This is set while we are streaming. When not set - * PROCSIG_WALSND_INIT_STOPPING signal will be handled like SIGTERM. When set, - * the main loop is responsible for checking got_STOPPING and terminating when - * it's set (after streaming any remaining WAL). + * INTERRUPT_WALSND_INIT_STOPPING interrupt will be handled like SIGTERM. When + * set, the main loop is responsible for checking got_STOPPING and terminating + * when it's set (after streaming any remaining WAL). */ static volatile sig_atomic_t replication_active = false; @@ -260,7 +260,7 @@ static void WalSndKeepalive(bool requestReply, XLogRecPtr writePtr); static void WalSndKeepaliveIfNecessary(void); static void WalSndCheckTimeOut(void); static long WalSndComputeSleeptime(TimestampTz now); -static void WalSndWait(uint32 socket_events, long timeout, uint32 wait_event); +static void WalSndWait(uint32 socket_events, long timeout, uint32 wait_event, uint32 interruptMask); static void WalSndPrepareWrite(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid, bool last_write); static void WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid, bool last_write); static void WalSndUpdateProgress(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid, @@ -1609,7 +1609,10 @@ ProcessPendingWrites(void) /* Sleep until something happens or we time out */ WalSndWait(WL_SOCKET_WRITEABLE | WL_SOCKET_READABLE, sleeptime, - WAIT_EVENT_WAL_SENDER_WRITE_DATA); + WAIT_EVENT_WAL_SENDER_WRITE_DATA, + INTERRUPT_CFI_MASK | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_GENERAL); /* Clear any already-pending wakeups */ ClearInterrupt(INTERRUPT_GENERAL); @@ -1617,9 +1620,8 @@ ProcessPendingWrites(void) CHECK_FOR_INTERRUPTS(); /* Process any requests or signals received recently */ - if (ConfigReloadPending) + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) { - ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); SyncRepInitConfig(); } @@ -1629,7 +1631,7 @@ ProcessPendingWrites(void) WalSndShutdown(); } - /* reactivate interrupt so WalSndLoop knows to continue */ + /* reactivate interrupt flag so WalSndLoop knows to continue */ RaiseInterrupt(INTERRUPT_GENERAL); } @@ -1823,9 +1825,8 @@ WalSndWaitForWal(XLogRecPtr loc) CHECK_FOR_INTERRUPTS(); /* Process any requests or signals received recently */ - if (ConfigReloadPending) + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) { - ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); SyncRepInitConfig(); } @@ -1935,7 +1936,10 @@ WalSndWaitForWal(XLogRecPtr loc) Assert(wait_event != 0); - WalSndWait(wakeEvents, sleeptime, wait_event); + WalSndWait(wakeEvents, sleeptime, wait_event, + INTERRUPT_CFI_MASK | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_GENERAL); } /* reactivate interrupt flag so WalSndLoop knows to continue */ @@ -2760,9 +2764,8 @@ WalSndLoop(WalSndSendDataCallback send_data) CHECK_FOR_INTERRUPTS(); /* Process any requests or signals received recently */ - if (ConfigReloadPending) + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) { - ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); SyncRepInitConfig(); } @@ -2858,7 +2861,11 @@ WalSndLoop(WalSndSendDataCallback send_data) wakeEvents |= WL_SOCKET_WRITEABLE; /* Sleep until something happens or we time out */ - WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN); + WalSndWait(wakeEvents, sleeptime, WAIT_EVENT_WAL_SENDER_MAIN, + INTERRUPT_CFI_MASK | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_GENERAL + ); } } } @@ -2896,6 +2903,7 @@ InitWalSenderSlot(void) /* * Found a free slot. Reserve it for us. */ + walsnd->pgprocno = MyProcNumber; walsnd->pid = MyProcPid; walsnd->state = WALSNDSTATE_STARTUP; walsnd->sentPtr = InvalidXLogRecPtr; @@ -2952,6 +2960,7 @@ WalSndKill(int code, Datum arg) SpinLockAcquire(&walsnd->mutex); /* Mark WalSnd struct as no longer being in use. */ walsnd->pid = 0; + walsnd->pgprocno = 0; SpinLockRelease(&walsnd->mutex); } @@ -3526,10 +3535,10 @@ WalSndRqstFileReload(void) } /* - * Handle PROCSIG_WALSND_INIT_STOPPING signal. + * Process INTERRUPT_WALSND_INIT_STOPPING interrupt */ void -HandleWalSndInitStopping(void) +ProcessWalSndInitStopping(void) { Assert(am_walsender); @@ -3540,9 +3549,12 @@ HandleWalSndInitStopping(void) * standby, and then exit gracefully. */ if (!replication_active) - kill(MyProcPid, SIGTERM); + RaiseInterrupt(INTERRUPT_DIE); else + { got_STOPPING = true; + RaiseInterrupt(INTERRUPT_GENERAL); + } } /* @@ -3568,7 +3580,7 @@ WalSndSignals(void) /* SIGQUIT handler was already set up by InitPostmasterChild */ InitializeTimeouts(); /* establishes SIGALRM handler */ pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, WalSndLastCycleHandler); /* request a last cycle and * shutdown */ @@ -3656,24 +3668,28 @@ WalSndWakeup(bool physical, bool logical) * on postmaster death. */ static void -WalSndWait(uint32 socket_events, long timeout, uint32 wait_event) +WalSndWait(uint32 socket_events, long timeout, uint32 wait_event, uint32 interruptMask) { WaitEvent event; + /* for condition variables */ + interruptMask |= INTERRUPT_GENERAL; + ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetSocketPos, socket_events, 0); + ModifyWaitEvent(FeBeWaitSet, FeBeWaitSetInterruptPos, WL_INTERRUPT, interruptMask); /* * We use a condition variable to efficiently wake up walsenders in * WalSndWakeup(). * * Every walsender prepares to sleep on a shared memory CV. Note that it - * just prepares to sleep on the CV (i.e., adds itself to the CV's - * waitlist), but does not actually wait on the CV (IOW, it never calls - * ConditionVariableSleep()). It still uses WaitEventSetWait() for - * waiting, because we also need to wait for socket events. The processes - * (startup process, walreceiver etc.) wanting to wake up walsenders use - * ConditionVariableBroadcast(), which in turn calls SendInterrupt(), - * helping walsenders come out of WaitEventSetWait(). + * just prepares to sleep on the CV waitlist), but does not actually wait + * on the CV (IOW, it never calls ConditionVariableSleep()). It still uses + * WaitEventSetWait() for waiting, because we also need to wait for socket + * events. The processes (startup process, walreceiver etc.) wanting to + * wake up walsenders use ConditionVariableBroadcast(), which in turn + * calls SendInterrupt(), helping walsenders come out of + * WaitEventSetWait(). * * This approach is simple and efficient because, one doesn't have to loop * through all the walsenders slots, with a spinlock acquisition and @@ -3721,16 +3737,16 @@ WalSndInitStopping(void) for (i = 0; i < max_wal_senders; i++) { WalSnd *walsnd = &WalSndCtl->walsnds[i]; - pid_t pid; + ProcNumber procno; SpinLockAcquire(&walsnd->mutex); - pid = walsnd->pid; + procno = walsnd->pgprocno; SpinLockRelease(&walsnd->mutex); - if (pid == 0) + if (procno == INVALID_PROC_NUMBER) continue; - SendProcSignal(pid, PROCSIG_WALSND_INIT_STOPPING, INVALID_PROC_NUMBER); + SendInterrupt(INTERRUPT_WALSND_INIT_STOPPING, procno); } } diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index c5bf4a70925..1bf0ec509ab 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -55,6 +55,7 @@ #include "storage/ipc.h" #include "storage/lmgr.h" #include "storage/proc.h" +#include "storage/procsignal.h" #include "storage/read_stream.h" #include "storage/smgr.h" #include "storage/standby.h" @@ -2976,7 +2977,7 @@ BufferSync(int flags) UnlockBufHdr(bufHdr, buf_state); /* Check for barrier events in case NBuffers is large. */ - if (ProcSignalBarrierPending) + if (InterruptPending(INTERRUPT_BARRIER)) ProcessProcSignalBarrier(); } @@ -3057,7 +3058,7 @@ BufferSync(int flags) s->num_to_scan++; /* Check for barrier events. */ - if (ProcSignalBarrierPending) + if (InterruptPending(INTERRUPT_BARRIER)) ProcessProcSignalBarrier(); } @@ -5213,7 +5214,7 @@ LockBufferForCleanup(Buffer buffer) * deadlock_timeout for it. */ if (logged_recovery_conflict) - LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, + LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN, waitStart, GetCurrentTimestamp(), NULL, false); @@ -5263,7 +5264,7 @@ LockBufferForCleanup(Buffer buffer) if (TimestampDifferenceExceeds(waitStart, now, DeadlockTimeout)) { - LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, + LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN, waitStart, now, NULL, true); logged_recovery_conflict = true; } @@ -5285,16 +5286,17 @@ LockBufferForCleanup(Buffer buffer) } else { - WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, WAIT_EVENT_BUFFER_PIN); - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); } /* * Remove flag marking us as waiter. Normally this will not be set - * anymore, but ProcWaitForSignal() can return for other signals as - * well. We take care to only reset the flag if we're the waiter, as + * anymore, but WaitInterrupt can return for other reasons as well. + * We take care to only reset the flag if we're the waiter, as * theoretically another backend could have started waiting. That's * impossible with the current usages due to table level locking, but * better be safe. diff --git a/src/backend/storage/ipc/interrupt.c b/src/backend/storage/ipc/interrupt.c index 6e58f64b096..ac22555cc77 100644 --- a/src/backend/storage/ipc/interrupt.c +++ b/src/backend/storage/ipc/interrupt.c @@ -21,6 +21,7 @@ #include "storage/interrupt.h" #include "storage/proc.h" #include "storage/waiteventset.h" +#include "utils/resowner.h" /* A common WaitEventSet used to implement WaitInterrupt() */ static WaitEventSet *InterruptWaitSet; @@ -89,17 +90,17 @@ SwitchToSharedInterrupts(void) * Set an interrupt flag in this backend. */ void -RaiseInterrupt(InterruptType reason) +RaiseInterrupt(uint32 interruptMask) { uint32 old_pending; - old_pending = pg_atomic_fetch_or_u32(MyPendingInterrupts, 1 << reason); + old_pending = pg_atomic_fetch_or_u32(MyPendingInterrupts, interruptMask); /* * If the process is currently blocked waiting for an interrupt to arrive, * and the interrupt wasn't already pending, wake it up. */ - if ((old_pending & (1 << reason | 1 << SLEEPING_ON_INTERRUPTS)) == 1 << SLEEPING_ON_INTERRUPTS) + if ((old_pending & (interruptMask | SLEEPING_ON_INTERRUPTS)) == SLEEPING_ON_INTERRUPTS) WakeupMyProc(); } @@ -110,7 +111,7 @@ RaiseInterrupt(InterruptType reason) * trust the contents of shared memory. */ void -SendInterrupt(InterruptType reason, ProcNumber pgprocno) +SendInterrupt(uint32 interruptMask, ProcNumber pgprocno) { PGPROC *proc; uint32 old_pending; @@ -120,13 +121,15 @@ SendInterrupt(InterruptType reason, ProcNumber pgprocno) Assert(pgprocno < ProcGlobal->allProcCount); proc = &ProcGlobal->allProcs[pgprocno]; - old_pending = pg_atomic_fetch_or_u32(&proc->pendingInterrupts, 1 << reason); + old_pending = pg_atomic_fetch_or_u32(&proc->pendingInterrupts, interruptMask); + + /* need a memory barrier here? Or is WakeupOtherProc() sufficient? */ /* * If the process is currently blocked waiting for an interrupt to arrive, * and the interrupt wasn't already pending, wake it up. */ - if ((old_pending & (1 << reason | 1 << SLEEPING_ON_INTERRUPTS)) == 1 << SLEEPING_ON_INTERRUPTS) + if ((old_pending & (interruptMask | SLEEPING_ON_INTERRUPTS)) == SLEEPING_ON_INTERRUPTS) WakeupOtherProc(proc); } diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c index e4d5b944e12..e8b293f4fff 100644 --- a/src/backend/storage/ipc/ipc.c +++ b/src/backend/storage/ipc/ipc.c @@ -28,6 +28,7 @@ #include "postmaster/autovacuum.h" #endif #include "storage/dsm.h" +#include "storage/interrupt.h" #include "storage/ipc.h" #include "tcop/tcopprot.h" @@ -175,9 +176,8 @@ proc_exit_prepare(int code) * close up shop already. Note that the signal handlers will not set * these flags again, now that proc_exit_inprogress is set. */ - InterruptPending = false; - ProcDiePending = false; - QueryCancelPending = false; + ClearInterrupt(INTERRUPT_DIE); + ClearInterrupt(INTERRUPT_QUERY_CANCEL); InterruptHoldoffCount = 1; CritSectionCount = 0; diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 2e54c11f880..a877984b8c3 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -58,6 +58,7 @@ #include "miscadmin.h" #include "pgstat.h" #include "port/pg_lfind.h" +#include "storage/interrupt.h" #include "storage/proc.h" #include "storage/procarray.h" #include "utils/acl.h" @@ -3488,13 +3489,13 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid) * Returns pid of the process signaled, or 0 if not found. */ pid_t -CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode) +CancelVirtualTransaction(VirtualTransactionId vxid, InterruptType reason) { - return SignalVirtualTransaction(vxid, sigmode, true); + return SignalVirtualTransaction(vxid, reason, true); } pid_t -SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode, +SignalVirtualTransaction(VirtualTransactionId vxid, InterruptType reason, bool conflictPending) { ProcArrayStruct *arrayP = procArray; @@ -3522,7 +3523,7 @@ SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode, * Kill the pid if it's still here. If not, that's what we * wanted so ignore any errors. */ - (void) SendProcSignal(pid, sigmode, vxid.procNumber); + SendInterrupt(reason, vxid.procNumber); } break; } @@ -3656,7 +3657,7 @@ CountDBConnections(Oid databaseid) * CancelDBBackends --- cancel backends that are using specified database */ void -CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending) +CancelDBBackends(Oid databaseid, InterruptType reason, bool conflictPending) { ProcArrayStruct *arrayP = procArray; int index; @@ -3671,20 +3672,17 @@ CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending) if (databaseid == InvalidOid || proc->databaseId == databaseid) { - VirtualTransactionId procvxid; pid_t pid; - GET_VXID_FROM_PGPROC(procvxid, *proc); - proc->recoveryConflictPending = conflictPending; pid = proc->pid; if (pid != 0) { /* - * Kill the pid if it's still here. If not, that's what we - * wanted so ignore any errors. + * Cancel the backend if it's still here. If not, that's what + * we wanted anyway. */ - (void) SendProcSignal(pid, sigmode, procvxid.procNumber); + SendInterrupt(reason, pgprocno); } } } diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c index 91557a7927e..82ea9e5cb8d 100644 --- a/src/backend/storage/ipc/procsignal.c +++ b/src/backend/storage/ipc/procsignal.c @@ -27,6 +27,7 @@ #include "storage/condition_variable.h" #include "storage/interrupt.h" #include "storage/ipc.h" +#include "storage/procsignal.h" #include "storage/shmem.h" #include "storage/sinval.h" #include "storage/smgr.h" @@ -34,38 +35,30 @@ #include "utils/memutils.h" /* - * The SIGUSR1 signal is multiplexed to support signaling multiple event - * types. The specific reason is communicated via flags in shared memory. - * We keep a boolean flag for each possible "reason", so that different - * reasons can be signaled to a process concurrently. (However, if the same - * reason is signaled more than once nearly simultaneously, the process may - * observe it only once.) + * State for the ProcSignalBarrier mechanism and query cancellation. * - * Each process that wants to receive signals registers its process ID - * in the ProcSignalSlots array. The array is indexed by ProcNumber to make - * slot allocation simple, and to avoid having to search the array when you - * know the ProcNumber of the process you're signaling. (We do support - * signaling without ProcNumber, but it's a bit less efficient.) + * Each process that wants to participate in barriers or query cancellation + * registers its process ID in the ProcSignalSlots array. The array is indexed + * by ProcNumber to make slot allocation simple. * * The fields in each slot are protected by a spinlock, pss_mutex. pss_pid can * also be read without holding the spinlock, as a quick preliminary check * when searching for a particular PID in the array. * - * pss_signalFlags are intended to be set in cases where we don't need to - * keep track of whether or not the target process has handled the signal, - * but sometimes we need confirmation, as when making a global state change - * that cannot be considered complete until all backends have taken notice - * of it. For such use cases, we set a bit in pss_barrierCheckMask and then + * Plain interrupts are intended to be set in cases where we don't need to + * keep track of whether or not the target process has handled the signal, but + * sometimes we need confirmation, as when making a global state change that + * cannot be considered complete until all backends have taken notice of + * it. For such use cases, we set a bit in pss_barrierCheckMask and then * increment the current "barrier generation"; when the new barrier generation - * (or greater) appears in the pss_barrierGeneration flag of every process, - * we know that the message has been received everywhere. + * (or greater) appears in the pss_barrierGeneration flag of every process, we + * know that the message has been received everywhere. */ typedef struct { pg_atomic_uint32 pss_pid; bool pss_cancel_key_valid; int32 pss_cancel_key; - volatile sig_atomic_t pss_signalFlags[NUM_PROCSIGNALS]; slock_t pss_mutex; /* protects the above fields */ /* Barrier-related fields (not protected by pss_mutex) */ @@ -104,7 +97,6 @@ struct ProcSignalHeader NON_EXEC_STATIC ProcSignalHeader *ProcSignal = NULL; static ProcSignalSlot *MyProcSignalSlot = NULL; -static bool CheckProcSignal(ProcSignalReason reason); static void CleanupProcSignalState(int status, Datum arg); static void ResetProcSignalBarrierBits(uint32 flags); @@ -150,7 +142,6 @@ ProcSignalShmemInit(void) pg_atomic_init_u32(&slot->pss_pid, 0); slot->pss_cancel_key_valid = false; slot->pss_cancel_key = 0; - MemSet(slot->pss_signalFlags, 0, sizeof(slot->pss_signalFlags)); pg_atomic_init_u64(&slot->pss_barrierGeneration, PG_UINT64_MAX); pg_atomic_init_u32(&slot->pss_barrierCheckMask, 0); ConditionVariableInit(&slot->pss_barrierCV); @@ -180,9 +171,6 @@ ProcSignalInit(bool cancel_key_valid, int32 cancel_key) /* Value used for sanity check below */ old_pss_pid = pg_atomic_read_u32(&slot->pss_pid); - /* Clear out any leftover signal reasons */ - MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t)); - /* * Initialize barrier state. Since we're a brand-new process, there * shouldn't be any leftover backend-private state that needs to be @@ -230,9 +218,10 @@ CleanupProcSignalState(int status, Datum arg) ProcSignalSlot *slot = MyProcSignalSlot; /* - * Clear MyProcSignalSlot, so that a SIGUSR1 received after this point - * won't try to access it after it's no longer ours (and perhaps even - * after we've unmapped the shared memory segment). + * Clear MyProcSignalSlot, so that if INTERRUPT_BARRIER is received after + * this point, ProcessProcSignalBarrier() won't try to access it after + * it's no longer ours (and perhaps even after we've unmapped the shared + * memory segment). */ Assert(MyProcSignalSlot != NULL); MyProcSignalSlot = NULL; @@ -268,73 +257,6 @@ CleanupProcSignalState(int status, Datum arg) ConditionVariableBroadcast(&slot->pss_barrierCV); } -/* - * SendProcSignal - * Send a signal to a Postgres process - * - * Providing procNumber is optional, but it will speed up the operation. - * - * On success (a signal was sent), zero is returned. - * On error, -1 is returned, and errno is set (typically to ESRCH or EPERM). - * - * Not to be confused with ProcSendSignal - */ -int -SendProcSignal(pid_t pid, ProcSignalReason reason, ProcNumber procNumber) -{ - volatile ProcSignalSlot *slot; - - if (procNumber != INVALID_PROC_NUMBER) - { - Assert(procNumber < NumProcSignalSlots); - slot = &ProcSignal->psh_slot[procNumber]; - - SpinLockAcquire(&slot->pss_mutex); - if (pg_atomic_read_u32(&slot->pss_pid) == pid) - { - /* Atomically set the proper flag */ - slot->pss_signalFlags[reason] = true; - SpinLockRelease(&slot->pss_mutex); - /* Send signal */ - return kill(pid, SIGUSR1); - } - SpinLockRelease(&slot->pss_mutex); - } - else - { - /* - * procNumber not provided, so search the array using pid. We search - * the array back to front so as to reduce search overhead. Passing - * INVALID_PROC_NUMBER means that the target is most likely an - * auxiliary process, which will have a slot near the end of the - * array. - */ - int i; - - for (i = NumProcSignalSlots - 1; i >= 0; i--) - { - slot = &ProcSignal->psh_slot[i]; - - if (pg_atomic_read_u32(&slot->pss_pid) == pid) - { - SpinLockAcquire(&slot->pss_mutex); - if (pg_atomic_read_u32(&slot->pss_pid) == pid) - { - /* Atomically set the proper flag */ - slot->pss_signalFlags[reason] = true; - SpinLockRelease(&slot->pss_mutex); - /* Send signal */ - return kill(pid, SIGUSR1); - } - SpinLockRelease(&slot->pss_mutex); - } - } - } - - errno = ESRCH; - return -1; -} - /* * EmitProcSignalBarrier * Send a signal to every Postgres process @@ -379,8 +301,8 @@ EmitProcSignalBarrier(ProcSignalBarrierType type) pg_atomic_add_fetch_u64(&ProcSignal->psh_barrierGeneration, 1); /* - * Signal all the processes, so that they update their advertised barrier - * generation. + * Send an interrupt to all the processes, so that they update their + * advertised barrier generation. * * Concurrency is not a problem here. Backends that have exited don't * matter, and new backends that have joined since we entered this @@ -391,26 +313,8 @@ EmitProcSignalBarrier(ProcSignalBarrierType type) * backends that need to update state - but they won't actually need to * change any state. */ - for (int i = NumProcSignalSlots - 1; i >= 0; i--) - { - volatile ProcSignalSlot *slot = &ProcSignal->psh_slot[i]; - pid_t pid = pg_atomic_read_u32(&slot->pss_pid); - - if (pid != 0) - { - SpinLockAcquire(&slot->pss_mutex); - pid = pg_atomic_read_u32(&slot->pss_pid); - if (pid != 0) - { - /* see SendProcSignal for details */ - slot->pss_signalFlags[PROCSIG_BARRIER] = true; - SpinLockRelease(&slot->pss_mutex); - kill(pid, SIGUSR1); - } - else - SpinLockRelease(&slot->pss_mutex); - } - } + for (ProcNumber pgprocno = 0; pgprocno < NumProcSignalSlots; pgprocno++) + SendInterrupt(INTERRUPT_BARRIER, pgprocno); return generation; } @@ -469,23 +373,6 @@ WaitForProcSignalBarrier(uint64 generation) pg_memory_barrier(); } -/* - * Handle receipt of an interrupt indicating a global barrier event. - * - * All the actual work is deferred to ProcessProcSignalBarrier(), because we - * cannot safely access the barrier generation inside the signal handler as - * 64bit atomics might use spinlock based emulation, even for reads. As this - * routine only gets called when PROCSIG_BARRIER is sent that won't cause a - * lot of unnecessary work. - */ -static void -HandleProcSignalBarrierInterrupt(void) -{ - InterruptPending = true; - ProcSignalBarrierPending = true; - /* interrupt will be raised by procsignal_sigusr1_handler */ -} - /* * Perform global barrier related interrupt checking. * @@ -501,12 +388,12 @@ ProcessProcSignalBarrier(void) uint64 shared_gen; volatile uint32 flags; - Assert(MyProcSignalSlot); - /* Exit quickly if there's no work to do. */ - if (!ProcSignalBarrierPending) + if (!ConsumeInterrupt(INTERRUPT_BARRIER)) + return; + + if (MyProcSignalSlot == NULL) return; - ProcSignalBarrierPending = false; /* * It's not unlikely to process multiple barriers at once, before the @@ -635,86 +522,7 @@ static void ResetProcSignalBarrierBits(uint32 flags) { pg_atomic_fetch_or_u32(&MyProcSignalSlot->pss_barrierCheckMask, flags); - ProcSignalBarrierPending = true; - InterruptPending = true; -} - -/* - * CheckProcSignal - check to see if a particular reason has been - * signaled, and clear the signal flag. Should be called after receiving - * SIGUSR1. - */ -static bool -CheckProcSignal(ProcSignalReason reason) -{ - volatile ProcSignalSlot *slot = MyProcSignalSlot; - - if (slot != NULL) - { - /* - * Careful here --- don't clear flag if we haven't seen it set. - * pss_signalFlags is of type "volatile sig_atomic_t" to allow us to - * read it here safely, without holding the spinlock. - */ - if (slot->pss_signalFlags[reason]) - { - slot->pss_signalFlags[reason] = false; - return true; - } - } - - return false; -} - -/* - * procsignal_sigusr1_handler - handle SIGUSR1 signal. - */ -void -procsignal_sigusr1_handler(SIGNAL_ARGS) -{ - if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT)) - HandleCatchupInterrupt(); - - if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT)) - HandleNotifyInterrupt(); - - if (CheckProcSignal(PROCSIG_PARALLEL_MESSAGE)) - HandleParallelMessageInterrupt(); - - if (CheckProcSignal(PROCSIG_WALSND_INIT_STOPPING)) - HandleWalSndInitStopping(); - - if (CheckProcSignal(PROCSIG_BARRIER)) - HandleProcSignalBarrierInterrupt(); - - if (CheckProcSignal(PROCSIG_LOG_MEMORY_CONTEXT)) - HandleLogMemoryContextInterrupt(); - - if (CheckProcSignal(PROCSIG_PARALLEL_APPLY_MESSAGE)) - HandleParallelApplyMessageInterrupt(); - - if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE)) - HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE); - - if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE)) - HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE); - - if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK)) - HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK); - - if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT)) - HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT); - - if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT)) - HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT); - - if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK)) - HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); - - if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN)) - HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); - - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_BARRIER); } /* diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c index b752dccee1d..2b177c52cf3 100644 --- a/src/backend/storage/ipc/shm_mq.c +++ b/src/backend/storage/ipc/shm_mq.c @@ -4,9 +4,10 @@ * single-reader, single-writer shared memory message queue * * Both the sender and the receiver must have a PGPROC; their respective - * process interrupts are used for synchronization. Only the sender may send, - * and only the receiver may receive. This is intended to allow a user - * backend to communicate with worker backends that it has registered. + * INTERRUPT_GENERAL interrupts are used for synchronization. Only the + * sender may send, and only the receiver may receive. This is intended to + * allow a user backend to communicate with worker backends that it has + * registered. * * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -49,7 +50,7 @@ * 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? + * XXX: No explicit memory barriers in SendInterrupt/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. @@ -343,16 +344,15 @@ shm_mq_send(shm_mq_handle *mqh, Size nbytes, const void *data, bool nowait, * Write a message into a shared message queue, gathered from multiple * addresses. * - * When nowait = false, we'll wait on our process interrupt when the ring - * buffer fills up, and then continue writing once the receiver has drained - * some data. The process interrupt is cleared after each wait. + * When nowait = false, we'll wait when the ring buffer fills up, and then + * continue writing once the receiver has drained some data. * - * When nowait = true, we do not manipulate the state of the process interrupt; - * instead, if the buffer becomes full, we return SHM_MQ_WOULD_BLOCK. In - * this case, the caller should call this function again, with the same - * arguments, each time the process interrupt is set. (Once begun, the sending - * of a message cannot be aborted except by detaching from the queue; changing - * the length or payload will corrupt the queue.) + * When nowait = true, if the buffer becomes full, we return + * SHM_MQ_WOULD_BLOCK. In this case, the caller should call this function + * again, with the same arguments, each time the INTERRUPT_GENERAL interrupt + * is set. (Once begun, the sending of a message cannot be aborted except by + * detaching from the queue; changing the length or payload will corrupt the + * queue.) * * When force_flush = true, we immediately update the shm_mq's mq_bytes_written * and notify the receiver (if it is already attached). Otherwise, we don't @@ -559,16 +559,15 @@ shm_mq_sendv(shm_mq_handle *mqh, shm_mq_iovec *iov, int iovcnt, bool nowait, * while still allowing longer messages. In either case, the return value * remains valid until the next receive operation is performed on the queue. * - * When nowait = false, we'll wait on our process interrupt when the ring - * buffer is empty and we have not yet received a full message. The sender - * will set our process interrupt after more data has been written, and we'll - * resume processing. Each call will therefore return a complete message - * (unless the sender detaches the queue). + * When nowait = false, we'll when the ring buffer is empty and we have not + * yet received a full message. The sender will send us INTERRUPT_GENERAL + * after more data has been written, and we'll resume processing. Each call + * will therefore return a complete message (unless the sender detaches the + * queue). * - * When nowait = true, we do not manipulate the state of the process - * interrupt; instead, whenever the buffer is empty and we need to read from - * it, we return SHM_MQ_WOULD_BLOCK. In this case, the caller should call - * this function again after the process interrupt has been set. + * When nowait = true, we do not wait when the buffer is empty and return + * SHM_MQ_WOULD_BLOCK instead. In this case, the caller should call this + * function again after INTERRUPT_GENERAL has been set. */ shm_mq_result shm_mq_receive(shm_mq_handle *mqh, Size *nbytesp, void **datap, bool nowait) @@ -1017,7 +1016,8 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data, * interrupt at top of loop, because setting an already-set * interrupt is much cheaper than setting one that has been reset. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, WAIT_EVENT_MESSAGE_QUEUE_SEND); /* Clear the interrupt so we don't spin. */ @@ -1163,14 +1163,15 @@ shm_mq_receive_bytes(shm_mq_handle *mqh, Size bytes_needed, bool nowait, * loop, because setting an already-set interrupt is much cheaper than * setting one that has been cleared. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, WAIT_EVENT_MESSAGE_QUEUE_RECEIVE); + /* Handle standard interrupts that may have occurred while we were waiting. */ + CHECK_FOR_INTERRUPTS(); + /* Clear the interrupt so we don't spin. */ ClearInterrupt(INTERRUPT_GENERAL); - - /* An interrupt may have occurred while we were waiting. */ - CHECK_FOR_INTERRUPTS(); } } @@ -1252,14 +1253,15 @@ shm_mq_wait_internal(shm_mq *mq, PGPROC **ptr, BackgroundWorkerHandle *handle) } /* Wait to be signaled. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, WAIT_EVENT_MESSAGE_QUEUE_INTERNAL); + /* Handle standard interrupts that may have occurred while we were waiting. */ + CHECK_FOR_INTERRUPTS(); + /* Clear the interrupt so we don't spin. */ ClearInterrupt(INTERRUPT_GENERAL); - - /* An interrupt may have occurred while we were waiting. */ - CHECK_FOR_INTERRUPTS(); } return result; diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c index d77b4f44ad5..265a14684f7 100644 --- a/src/backend/storage/ipc/signalfuncs.c +++ b/src/backend/storage/ipc/signalfuncs.c @@ -205,7 +205,7 @@ pg_wait_until_termination(int pid, int64 timeout) /* Process interrupts, if any, before waiting */ CHECK_FOR_INTERRUPTS(); - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, waittime, WAIT_EVENT_BACKEND_TERMINATION); diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c index 3c112daf001..7df37fca0b3 100644 --- a/src/backend/storage/ipc/sinval.c +++ b/src/backend/storage/ipc/sinval.c @@ -20,25 +20,8 @@ #include "storage/sinvaladt.h" #include "utils/inval.h" - uint64 SharedInvalidMessageCounter; - -/* - * Because backends sitting idle will not be reading sinval events, we - * need a way to give an idle backend a swift kick in the rear and make - * it catch up before the sinval queue overflows and forces it to go - * through a cache reset exercise. This is done by sending - * PROCSIG_CATCHUP_INTERRUPT to any backend that gets too far behind. - * - * The signal handler will set an interrupt pending flag and raise the - * INTERRUPT_GENERAL. Whenever starting to read from the client, or when - * interrupted while doing so, ProcessClientReadInterrupt() will call - * ProcessCatchupEvent(). - */ -volatile sig_atomic_t catchupInterruptPending = false; - - /* * SendSharedInvalidMessages * Add shared-cache-invalidation message(s) to the global SI message queue. @@ -132,38 +115,13 @@ ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *m * catchup signal this way avoids creating spikes in system load for what * should be just a background maintenance activity. */ - if (catchupInterruptPending) + if (ConsumeInterrupt(INTERRUPT_SINVAL_CATCHUP)) { - catchupInterruptPending = false; elog(DEBUG4, "sinval catchup complete, cleaning queue"); SICleanupQueue(false, 0); } } - -/* - * HandleCatchupInterrupt - * - * This is called when PROCSIG_CATCHUP_INTERRUPT is received. - * - * We used to directly call ProcessCatchupEvent directly when idle. These days - * we just set a flag to do it later and notify the process of that fact by - * raising INTERRUPT_GENERAL. - */ -void -HandleCatchupInterrupt(void) -{ - /* - * Note: this is called by a SIGNAL HANDLER. You must be very wary what - * you do here. - */ - - catchupInterruptPending = true; - - /* make sure the event is processed in due course */ - RaiseInterrupt(INTERRUPT_GENERAL); -} - /* * ProcessCatchupInterrupt * @@ -173,12 +131,12 @@ HandleCatchupInterrupt(void) void ProcessCatchupInterrupt(void) { - while (catchupInterruptPending) + while (InterruptPending(INTERRUPT_SINVAL_CATCHUP)) { /* * What we need to do here is cause ReceiveSharedInvalidMessages() to * run, which will do the necessary work and also reset the - * catchupInterruptPending flag. If we are inside a transaction we + * INTERRUPT_SINVAL_CATCHUP flag. If we are inside a transaction we * can just call AcceptInvalidationMessages() to do this. If we * aren't, we start and immediately end a transaction; the call to * AcceptInvalidationMessages() happens down inside transaction start. @@ -190,12 +148,12 @@ ProcessCatchupInterrupt(void) */ if (IsTransactionOrTransactionBlock()) { - elog(DEBUG4, "ProcessCatchupEvent inside transaction"); + elog(DEBUG4, "ProcessCatchupInterrupt inside transaction"); AcceptInvalidationMessages(); } else { - elog(DEBUG4, "ProcessCatchupEvent outside transaction"); + elog(DEBUG4, "ProcessCatchupInterrupt outside transaction"); StartTransactionCommand(); CommitTransactionCommand(); } diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c index 2da91738c32..880e776633d 100644 --- a/src/backend/storage/ipc/sinvaladt.c +++ b/src/backend/storage/ipc/sinvaladt.c @@ -18,10 +18,10 @@ #include #include "miscadmin.h" +#include "storage/interrupt.h" #include "storage/ipc.h" #include "storage/proc.h" #include "storage/procnumber.h" -#include "storage/procsignal.h" #include "storage/shmem.h" #include "storage/sinvaladt.h" #include "storage/spin.h" @@ -118,7 +118,7 @@ * we exceed CLEANUP_MIN. Should be a power of 2 for speed. * * SIG_THRESHOLD: the minimum number of messages a backend must have fallen - * behind before we'll send it PROCSIG_CATCHUP_INTERRUPT. + * behind before we'll send it INTERRUPT_SINVAL_CATCHUP. * * WRITE_QUANTUM: the max number of messages to push into the buffer per * iteration of SIInsertDataEntries. Noncritical but should be less than @@ -564,7 +564,7 @@ SIGetDataEntries(SharedInvalidationMessage *data, int datasize) * minFree is the minimum number of message slots to make free. * * Possible side effects of this routine include marking one or more - * backends as "reset" in the array, and sending PROCSIG_CATCHUP_INTERRUPT + * backends as "reset" in the array, and sending INTERRUPT_SINVAL_CATCHUP * to some backend that seems to be getting too far behind. We signal at * most one backend at a time, for reasons explained at the top of the file. * @@ -657,8 +657,8 @@ SICleanupQueue(bool callerHasWriteLock, int minFree) segP->nextThreshold = (numMsgs / CLEANUP_QUANTUM + 1) * CLEANUP_QUANTUM; /* - * Lastly, signal anyone who needs a catchup interrupt. Since - * SendProcSignal() might not be fast, we don't want to hold locks while + * Lastly, signal anyone who needs a catchup interrupt. SendInterrupt() + * is pretty fast, but we nevertheless don't want to hold locks while * executing it. */ if (needSig) @@ -669,8 +669,8 @@ SICleanupQueue(bool callerHasWriteLock, int minFree) needSig->signaled = true; LWLockRelease(SInvalReadLock); LWLockRelease(SInvalWriteLock); - elog(DEBUG4, "sending sinval catchup signal to PID %d", (int) his_pid); - SendProcSignal(his_pid, PROCSIG_CATCHUP_INTERRUPT, his_procNumber); + elog(DEBUG4, "sending sinval catchup interrupt to backend PID %d", (int) his_pid); + SendInterrupt(INTERRUPT_SINVAL_CATCHUP, his_procNumber); if (callerHasWriteLock) LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE); } diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index 76b2996f327..73ce6daaade 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -71,13 +71,13 @@ static volatile sig_atomic_t got_standby_delay_timeout = false; static volatile sig_atomic_t got_standby_lock_timeout = false; static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, - ProcSignalReason reason, + InterruptType reason, uint32 wait_event_info, bool report_waiting); -static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason); +static void SendRecoveryConflictWithBufferPin(InterruptType reason); static XLogRecPtr LogCurrentRunningXacts(RunningTransactions CurrRunningXacts); static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks); -static const char *get_recovery_conflict_desc(ProcSignalReason reason); +static const char *get_recovery_conflict_desc(InterruptType reason); /* * InitRecoveryTransactionEnvironment @@ -271,7 +271,7 @@ WaitExceedsMaxStandbyDelay(uint32 wait_event_info) * to be resolved or not. */ void -LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start, +LogRecoveryConflict(InterruptType reason, TimestampTz wait_start, TimestampTz now, VirtualTransactionId *wait_list, bool still_waiting) { @@ -358,7 +358,7 @@ LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start, */ static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, - ProcSignalReason reason, uint32 wait_event_info, + InterruptType reason, uint32 wait_event_info, bool report_waiting) { TimestampTz waitStart = 0; @@ -489,7 +489,7 @@ ResolveRecoveryConflictWithSnapshot(TransactionId snapshotConflictHorizon, backends = GetConflictingVirtualXIDs(snapshotConflictHorizon, locator.dbOid); ResolveRecoveryConflictWithVirtualXIDs(backends, - PROCSIG_RECOVERY_CONFLICT_SNAPSHOT, + INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT, WAIT_EVENT_RECOVERY_CONFLICT_SNAPSHOT, true); @@ -560,7 +560,7 @@ ResolveRecoveryConflictWithTablespace(Oid tsid) temp_file_users = GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid); ResolveRecoveryConflictWithVirtualXIDs(temp_file_users, - PROCSIG_RECOVERY_CONFLICT_TABLESPACE, + INTERRUPT_RECOVERY_CONFLICT_TABLESPACE, WAIT_EVENT_RECOVERY_CONFLICT_TABLESPACE, true); } @@ -581,7 +581,7 @@ ResolveRecoveryConflictWithDatabase(Oid dbid) */ while (CountDBBackends(dbid) > 0) { - CancelDBBackends(dbid, PROCSIG_RECOVERY_CONFLICT_DATABASE, true); + CancelDBBackends(dbid, INTERRUPT_RECOVERY_CONFLICT_DATABASE, true); /* * Wait awhile for them to die so that we avoid flooding an @@ -665,7 +665,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict) * because the caller, WaitOnLock(), has already reported that. */ ResolveRecoveryConflictWithVirtualXIDs(backends, - PROCSIG_RECOVERY_CONFLICT_LOCK, + INTERRUPT_RECOVERY_CONFLICT_LOCK, PG_WAIT_LOCK | locktag.locktag_type, false); } @@ -697,10 +697,11 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict) } /* Wait to be signaled by the release of the Relation Lock */ - WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, PG_WAIT_LOCK | locktag.locktag_type); - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); /* * Exit if ltime is reached. Then all the backends holding conflicting @@ -727,7 +728,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict) while (VirtualTransactionIdIsValid(*backends)) { SignalVirtualTransaction(*backends, - PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK, + INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK, false); backends++; } @@ -749,10 +750,11 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict) * until the relation locks are released or ltime is reached. */ got_standby_deadlock_timeout = false; - WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + WaitInterrupt(INTERRUPT_CFI_MASK | + INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, PG_WAIT_LOCK | locktag.locktag_type); - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); } cleanup: @@ -778,9 +780,10 @@ cleanup: * We either resolve conflicts immediately or set a timeout to wake us at * the limit of our patience. * - * Resolve conflicts by sending a PROCSIG signal to all backends to check if - * they hold one of the buffer pins that is blocking Startup process. If so, - * those backends will take an appropriate error action, ERROR or FATAL. + * Resolve conflicts by sending INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN to all + * backends to check if they hold one of the buffer pins that is blocking + * Startup process. If so, those backends will take an appropriate error + * action, ERROR or FATAL. * * We also must check for deadlocks. Deadlocks occur because if queries * wait on a lock, that must be behind an AccessExclusiveLock, which can only @@ -809,7 +812,7 @@ ResolveRecoveryConflictWithBufferPin(void) /* * We're already behind, so clear a path as quickly as possible. */ - SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); + SendRecoveryConflictWithBufferPin(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN); } else { @@ -844,17 +847,20 @@ ResolveRecoveryConflictWithBufferPin(void) * We assume that only UnpinBuffer() and the timeout requests established * above can wake us up here. WakeupRecovery() called by walreceiver or * SIGHUP signal handler, etc cannot do that because it uses the different - * interrupt flag. FIXME: seems like a shaky assumption. WakeupRecovery() - * indeed uses a different interrupt flag (different latch earlier), but - * the signal handler?? + * interrupt flag. + * + * FIXME: seems like a shaky assumption. WakeupRecovery() indeed uses a + * different interrupt flag (different latch earlier), but the signal + * handler?? */ - WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, WAIT_EVENT_BUFFER_PIN); - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); if (got_standby_delay_timeout) - SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); + SendRecoveryConflictWithBufferPin(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN); else if (got_standby_deadlock_timeout) { /* @@ -870,7 +876,7 @@ ResolveRecoveryConflictWithBufferPin(void) * not be so harmful because the period that the buffer is kept pinned * is basically no so long. But we should fix this? */ - SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); + SendRecoveryConflictWithBufferPin(INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK); } /* @@ -885,10 +891,10 @@ ResolveRecoveryConflictWithBufferPin(void) } static void -SendRecoveryConflictWithBufferPin(ProcSignalReason reason) +SendRecoveryConflictWithBufferPin(InterruptType reason) { - Assert(reason == PROCSIG_RECOVERY_CONFLICT_BUFFERPIN || - reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); + Assert(reason == INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN || + reason == INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK); /* * We send signal to all backends to ask them if they are holding the @@ -947,6 +953,7 @@ void StandbyDeadLockHandler(void) { got_standby_deadlock_timeout = true; + RaiseInterrupt(INTERRUPT_GENERAL); } /* @@ -956,6 +963,7 @@ void StandbyTimeoutHandler(void) { got_standby_delay_timeout = true; + RaiseInterrupt(INTERRUPT_GENERAL); } /* @@ -965,6 +973,7 @@ void StandbyLockTimeoutHandler(void) { got_standby_lock_timeout = true; + RaiseInterrupt(INTERRUPT_GENERAL); } /* @@ -1489,31 +1498,31 @@ LogStandbyInvalidations(int nmsgs, SharedInvalidationMessage *msgs, /* Return the description of recovery conflict */ static const char * -get_recovery_conflict_desc(ProcSignalReason reason) +get_recovery_conflict_desc(InterruptType reason) { const char *reasonDesc = _("unknown reason"); switch (reason) { - case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN: + case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN: reasonDesc = _("recovery conflict on buffer pin"); break; - case PROCSIG_RECOVERY_CONFLICT_LOCK: + case INTERRUPT_RECOVERY_CONFLICT_LOCK: reasonDesc = _("recovery conflict on lock"); break; - case PROCSIG_RECOVERY_CONFLICT_TABLESPACE: + case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE: reasonDesc = _("recovery conflict on tablespace"); break; - case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT: + case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT: reasonDesc = _("recovery conflict on snapshot"); break; - case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT: + case INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT: reasonDesc = _("recovery conflict on replication slot"); break; - case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK: + case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK: reasonDesc = _("recovery conflict on buffer deadlock"); break; - case PROCSIG_RECOVERY_CONFLICT_DATABASE: + case INTERRUPT_RECOVERY_CONFLICT_DATABASE: reasonDesc = _("recovery conflict on database"); break; default: diff --git a/src/backend/storage/ipc/waiteventset.c b/src/backend/storage/ipc/waiteventset.c index 819dce62252..2fe74e3fe70 100644 --- a/src/backend/storage/ipc/waiteventset.c +++ b/src/backend/storage/ipc/waiteventset.c @@ -1101,7 +1101,7 @@ WaitEventSetWait(WaitEventSet *set, long timeout, * synchronization so that if an interrupt bit is set after this, * the setter will wake us up. */ - old_mask = pg_atomic_fetch_or_u32(MyPendingInterrupts, 1 << SLEEPING_ON_INTERRUPTS); + old_mask = pg_atomic_fetch_or_u32(MyPendingInterrupts, SLEEPING_ON_INTERRUPTS); already_pending = ((old_mask & set->interrupt_mask) != 0); /* remember to clear the SLEEPING_ON_INTERRUPTS flag later */ @@ -1176,7 +1176,7 @@ WaitEventSetWait(WaitEventSet *set, long timeout, /* If we set the SLEEPING_ON_INTERRUPTS flag, reset it again */ if (sleeping_flag_armed) - pg_atomic_fetch_and_u32(MyPendingInterrupts, ~((uint32) 1 << SLEEPING_ON_INTERRUPTS)); + pg_atomic_fetch_and_u32(MyPendingInterrupts, ~((uint32) SLEEPING_ON_INTERRUPTS)); #ifndef WIN32 waiting = false; diff --git a/src/backend/storage/lmgr/condition_variable.c b/src/backend/storage/lmgr/condition_variable.c index ef792103835..35cacc06b5b 100644 --- a/src/backend/storage/lmgr/condition_variable.c +++ b/src/backend/storage/lmgr/condition_variable.c @@ -161,7 +161,8 @@ ConditionVariableTimedSleep(ConditionVariable *cv, long timeout, * Wait for interrupt. (If we're awakened for some other reason, the * code below will cope anyway.) */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, wait_events, cur_timeout, wait_event_info); + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + wait_events, cur_timeout, wait_event_info); /* Clear the flag before examining the state of the wait list. */ ClearInterrupt(INTERRUPT_GENERAL); diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index 11ae421ad68..53b90cc3492 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -1577,10 +1577,11 @@ GetSafeSnapshot(Snapshot origSnapshot) SxactIsROUnsafe(MySerializableXact))) { LWLockRelease(SerializableXactHashLock); - WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, WAIT_EVENT_SAFE_SNAPSHOT); - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE); } MySerializableXact->flags &= ~SXACT_FLAG_DEFERRABLE_WAITING; diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 34a525ae607..6a15138d8f8 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -1393,7 +1393,7 @@ ProcSleep(LOCALLOCK *locallock) * because the startup process here has already waited * longer than deadlock_timeout. */ - LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_LOCK, + LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_LOCK, standbyWaitStart, now, cnt > 0 ? vxids : NULL, true); logged_recovery_conflict = true; @@ -1402,9 +1402,9 @@ ProcSleep(LOCALLOCK *locallock) } else { - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, PG_WAIT_LOCK | locallock->tag.lock.locktag_type); - ClearInterrupt(INTERRUPT_GENERAL); /* check for deadlocks first, as that's probably log-worthy */ if (got_deadlock_timeout) { @@ -1412,6 +1412,7 @@ ProcSleep(LOCALLOCK *locallock) got_deadlock_timeout = false; } CHECK_FOR_INTERRUPTS(); + ClearInterrupt(INTERRUPT_GENERAL); } /* @@ -1658,7 +1659,7 @@ ProcSleep(LOCALLOCK *locallock) /* * Disable the timers, if they are still running. As in LockErrorCleanup, * we must preserve the LOCK_TIMEOUT indicator flag: if a lock timeout has - * already caused QueryCancelPending to become set, we want the cancel to + * already raised INTERRUPT_QUERY_CANCEL, we want the cancel to * be reported as a lock timeout, not a user cancel. */ if (!InHotStandby) @@ -1682,7 +1683,7 @@ ProcSleep(LOCALLOCK *locallock) * startup process waited longer than deadlock_timeout for it. */ if (InHotStandby && logged_recovery_conflict) - LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_LOCK, + LogRecoveryConflict(INTERRUPT_RECOVERY_CONFLICT_LOCK, standbyWaitStart, GetCurrentTimestamp(), NULL, false); @@ -1881,10 +1882,6 @@ CheckDeadLockAlert(void) * did. Back then got_deadlock_timeout wasn't yet set... It's unlikely * that this ever would be a problem, but raising an interrupt again is * cheap. - * - * Note that, when this function runs inside procsignal_sigusr1_handler(), - * the handler function raises the interrupt again after the interrupt is - * raised here. */ RaiseInterrupt(INTERRUPT_GENERAL); errno = save_errno; diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 9a8db57195e..e943a841651 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -67,6 +67,7 @@ #include "storage/proc.h" #include "storage/procsignal.h" #include "storage/sinval.h" +#include "storage/standby.h" #include "tcop/fastpath.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" @@ -154,10 +155,6 @@ static const char *userDoption = NULL; /* -D switch */ static bool EchoQuery = false; /* -E switch */ static bool UseSemiNewlineNewline = false; /* -j switch */ -/* whether or not, and why, we were canceled by conflict with recovery */ -static volatile sig_atomic_t RecoveryConflictPending = false; -static volatile sig_atomic_t RecoveryConflictPendingReasons[NUM_PROCSIGNALS]; - /* reused buffer to pass to SendRowDescriptionMessage() */ static MemoryContext row_description_context = NULL; static StringInfoData row_description_buf; @@ -496,42 +493,64 @@ ReadCommand(StringInfo inBuf) * false if about to read or done reading. * * Must preserve errno! + * + * Returns interrupt mask to use for waiting */ -void +uint32 ProcessClientReadInterrupt(bool blocked) { int save_errno = errno; + uint32 interruptMask = 0; if (DoingCommandRead) { /* Check for general interrupts that arrived before/while reading */ + interruptMask |= INTERRUPT_CFI_MASK; CHECK_FOR_INTERRUPTS(); /* Process sinval catchup interrupts, if any */ - if (catchupInterruptPending) + interruptMask |= INTERRUPT_SINVAL_CATCHUP; + if (InterruptPending(INTERRUPT_SINVAL_CATCHUP)) ProcessCatchupInterrupt(); - /* Process notify interrupts, if any */ - if (notifyInterruptPending) - ProcessNotifyInterrupt(true); - } - else if (ProcDiePending) - { /* - * We're dying. If there is no data available to read, then it's safe - * (and sane) to handle that now. If we haven't tried to read yet, - * make sure the interrupt flag is set, so that if there is no data - * then we'll come back here and die. If we're done reading, also - * make sure the interrupt flag is set, as we might've undesirably - * cleared it while reading. + * If we are truly idle, ie. *not* inside a transaction block matching + * the conditions in PostgresMain(), there are a few interrupts things + * that we process immediately. */ - if (blocked) - CHECK_FOR_INTERRUPTS(); - else - RaiseInterrupt(INTERRUPT_GENERAL); + if (!IsTransactionOrTransactionBlock()) + { + interruptMask |= INTERRUPT_ASYNC_NOTIFY; + if (InterruptPending(INTERRUPT_ASYNC_NOTIFY)) + ProcessNotifyInterrupt(true); + + interruptMask |= INTERRUPT_IDLE_STATS_TIMEOUT; + if (InterruptPending(INTERRUPT_IDLE_STATS_TIMEOUT)) + { + ClearInterrupt(INTERRUPT_IDLE_STATS_TIMEOUT); + pgstat_report_stat(true); + } + } + } + else + { + interruptMask |= INTERRUPT_DIE; + if (InterruptPending(INTERRUPT_DIE)) + { + /* + * We're dying. If there is no data available to read, then it's safe + * (and sane) to handle that now. If we haven't tried to read yet, + * don't clear the interrupt flag is set, so that if there is no data + * then we'll come back here and die. + */ + if (blocked) + CHECK_FOR_INTERRUPTS(); + } } errno = save_errno; + + return interruptMask; } /* @@ -543,12 +562,12 @@ ProcessClientReadInterrupt(bool blocked) * * Must preserve errno! */ -void +uint32 ProcessClientWriteInterrupt(bool blocked) { int save_errno = errno; - if (ProcDiePending) + if (InterruptPending(INTERRUPT_DIE)) { /* * We're dying. If it's not possible to write, then we should handle @@ -579,11 +598,11 @@ ProcessClientWriteInterrupt(bool blocked) CHECK_FOR_INTERRUPTS(); } } - else - RaiseInterrupt(INTERRUPT_GENERAL); } errno = save_errno; + + return INTERRUPT_DIE; } /* @@ -2528,29 +2547,29 @@ errdetail_abort(void) * Add an errdetail() line showing conflict source. */ static int -errdetail_recovery_conflict(ProcSignalReason reason) +errdetail_recovery_conflict(InterruptType reason) { switch (reason) { - case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN: + case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN: errdetail("User was holding shared buffer pin for too long."); break; - case PROCSIG_RECOVERY_CONFLICT_LOCK: + case INTERRUPT_RECOVERY_CONFLICT_LOCK: errdetail("User was holding a relation lock for too long."); break; - case PROCSIG_RECOVERY_CONFLICT_TABLESPACE: + case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE: errdetail("User was or might have been using tablespace that must be dropped."); break; - case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT: + case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT: errdetail("User query might have needed to see row versions that must be removed."); break; - case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT: + case INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT: errdetail("User was using a logical replication slot that must be invalidated."); break; - case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK: + case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK: errdetail("User transaction caused buffer deadlock with recovery."); break; - case PROCSIG_RECOVERY_CONFLICT_DATABASE: + case INTERRUPT_RECOVERY_CONFLICT_DATABASE: errdetail("User was connected to a database that must be dropped."); break; default: @@ -3005,17 +3024,11 @@ die(SIGNAL_ARGS) { /* Don't joggle the elbow of proc_exit */ if (!proc_exit_inprogress) - { - InterruptPending = true; - ProcDiePending = true; - } + RaiseInterrupt(INTERRUPT_DIE); /* for the cumulative stats system */ pgStatSessionEndCause = DISCONNECT_KILLED; - /* If we're still here, waken anything waiting on the interrupt */ - RaiseInterrupt(INTERRUPT_GENERAL); - /* * If we're in single user mode, we want to quit immediately - we can't * rely on interrupts as they wouldn't work when stdin/stdout is a file. @@ -3033,17 +3046,7 @@ die(SIGNAL_ARGS) void StatementCancelHandler(SIGNAL_ARGS) { - /* - * Don't joggle the elbow of proc_exit - */ - if (!proc_exit_inprogress) - { - InterruptPending = true; - QueryCancelPending = true; - } - - /* If we're still here, waken anything waiting on the interrupt */ - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_QUERY_CANCEL); } /* signal handler for floating point exception */ @@ -3060,27 +3063,14 @@ FloatExceptionHandler(SIGNAL_ARGS) } /* - * Tell the next CHECK_FOR_INTERRUPTS() to check for a particular type of - * recovery conflict. Runs in a SIGUSR1 handler. - */ -void -HandleRecoveryConflictInterrupt(ProcSignalReason reason) -{ - RecoveryConflictPendingReasons[reason] = true; - RecoveryConflictPending = true; - InterruptPending = true; - /* INTERRUPT_GENERAL will be raised by procsignal_sigusr1_handler */ -} - -/* - * Check one individual conflict reason. + * Process one individual conflict reason. */ static void -ProcessRecoveryConflictInterrupt(ProcSignalReason reason) +ProcessRecoveryConflictInterrupt(InterruptType reason) { switch (reason) { - case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK: + case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK: /* * If we aren't waiting for a lock we can never deadlock. @@ -3091,21 +3081,21 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason) /* Intentional fall through to check wait for pin */ /* FALLTHROUGH */ - case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN: + case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN: /* - * If PROCSIG_RECOVERY_CONFLICT_BUFFERPIN is requested but we + * If INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN is requested but we * aren't blocking the Startup process there is nothing more to * do. * - * When PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK is requested, + * When INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK is requested, * if we're waiting for locks and the startup process is not * waiting for buffer pin (i.e., also waiting for locks), we set * the flag so that ProcSleep() will check for deadlocks. */ if (!HoldingBufferPinThatDelaysRecovery()) { - if (reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK && + if (reason == INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK && GetStartupBufferPinWaitBufId() < 0) CheckDeadLockAlert(); return; @@ -3116,9 +3106,9 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason) /* Intentional fall through to error handling */ /* FALLTHROUGH */ - case PROCSIG_RECOVERY_CONFLICT_LOCK: - case PROCSIG_RECOVERY_CONFLICT_TABLESPACE: - case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT: + case INTERRUPT_RECOVERY_CONFLICT_LOCK: + case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE: + case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT: /* * If we aren't in a transaction any longer then ignore. @@ -3128,14 +3118,14 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason) /* FALLTHROUGH */ - case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT: + case INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT: /* * If we're not in a subtransaction then we are OK to throw an * ERROR to resolve the conflict. Otherwise drop through to the * FATAL case. * - * PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT is a special case that + * RECOVERY_CONFLICT_LOGICALSLOT is a special case that * always throws an ERROR (ie never promotes to FATAL), though it * still has to respect QueryCancelHoldoffCount, so it shares this * code path. Logical decoding slots are only acquired while @@ -3145,17 +3135,17 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason) * intercept an error before the replication slot is released. * * XXX other times that we can throw just an ERROR *may* be - * PROCSIG_RECOVERY_CONFLICT_LOCK if no locks are held in parent + * RECOVERY_CONFLICT_LOCK if no locks are held in parent * transactions * - * PROCSIG_RECOVERY_CONFLICT_SNAPSHOT if no snapshots are held by + * RECOVERY_CONFLICT_SNAPSHOT if no snapshots are held by * parent transactions and the transaction is not * transaction-snapshot mode * - * PROCSIG_RECOVERY_CONFLICT_TABLESPACE if no temp files or + * RECOVERY_CONFLICT_TABLESPACE if no temp files or * cursors open in parent transactions */ - if (reason == PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT || + if (reason == INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT || !IsSubTransaction()) { /* @@ -3179,12 +3169,10 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason) if (QueryCancelHoldoffCount != 0) { /* - * Re-arm and defer this interrupt until later. See - * similar code in ProcessInterrupts(). + * Leave the interrupt set to defer this interrupt + * until later. See similar code in + * ProcessInterrupts(). */ - RecoveryConflictPendingReasons[reason] = true; - RecoveryConflictPending = true; - InterruptPending = true; return; } @@ -3207,7 +3195,7 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason) /* Intentional fall through to session cancel */ /* FALLTHROUGH */ - case PROCSIG_RECOVERY_CONFLICT_DATABASE: + case INTERRUPT_RECOVERY_CONFLICT_DATABASE: /* * Retrying is not possible because the database is dropped, or we @@ -3216,7 +3204,7 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason) */ pgstat_report_recovery_conflict(reason); ereport(FATAL, - (errcode(reason == PROCSIG_RECOVERY_CONFLICT_DATABASE ? + (errcode(reason == INTERRUPT_RECOVERY_CONFLICT_DATABASE ? ERRCODE_DATABASE_DROPPED : ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("terminating connection due to conflict with recovery"), @@ -3230,60 +3218,37 @@ ProcessRecoveryConflictInterrupt(ProcSignalReason reason) } } -/* - * Check each possible recovery conflict reason. - */ -static void -ProcessRecoveryConflictInterrupts(void) -{ - /* - * We don't need to worry about joggling the elbow of proc_exit, because - * proc_exit_prepare() holds interrupts, so ProcessInterrupts() won't call - * us. - */ - Assert(!proc_exit_inprogress); - Assert(InterruptHoldoffCount == 0); - Assert(RecoveryConflictPending); - - RecoveryConflictPending = false; - - for (ProcSignalReason reason = PROCSIG_RECOVERY_CONFLICT_FIRST; - reason <= PROCSIG_RECOVERY_CONFLICT_LAST; - reason++) - { - if (RecoveryConflictPendingReasons[reason]) - { - RecoveryConflictPendingReasons[reason] = false; - ProcessRecoveryConflictInterrupt(reason); - } - } -} - /* * ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro * * If an interrupt condition is pending, and it's safe to service it, * then clear the flag and accept the interrupt. Called only when - * InterruptPending is true. + * INTERRUPTS_PENDING_CONDITION(CheckForInterruptsMask) is true. * - * Note: if INTERRUPTS_CAN_BE_PROCESSED() is true, then ProcessInterrupts - * is guaranteed to clear the InterruptPending flag before returning. - * (This is not the same as guaranteeing that it's still clear when we - * return; another interrupt could have arrived. But we promise that + * Note: if INTERRUPTS_CAN_BE_PROCESSED() is true, then ProcessInterrupts is + * guaranteed to clear the interrupt bits in CheckForInterruptsMask before + * returning. (This is not the same as guaranteeing that it's still clear + * when we return; another interrupt could have arrived. But we promise that * any pre-existing one will have been serviced.) */ void ProcessInterrupts(void) { /* OK to accept any interrupts now? */ - if (InterruptHoldoffCount != 0 || CritSectionCount != 0) - return; - InterruptPending = false; + Assert (InterruptHoldoffCount == 0); + Assert (CritSectionCount == 0); + + /* + * TODO: it'd be good for performance to read the atomic + * MyPendingInterrupts variable just once in this function + */ + + if (ConsumeInterrupt(INTERRUPT_WALSND_INIT_STOPPING)) + ProcessWalSndInitStopping(); - if (ProcDiePending) + if (ConsumeInterrupt(INTERRUPT_DIE)) { - ProcDiePending = false; - QueryCancelPending = false; /* ProcDie trumps QueryCancel */ + ClearInterrupt(INTERRUPT_QUERY_CANCEL); /* ProcDie trumps QueryCancel */ LockErrorCleanup(); /* As in quickdie, don't risk sending to client during auth */ if (ClientAuthInProgress && whereToSendOutput == DestRemote) @@ -3322,10 +3287,8 @@ ProcessInterrupts(void) errmsg("terminating connection due to administrator command"))); } - if (CheckClientConnectionPending) + if (ConsumeInterrupt(INTERRUPT_CLIENT_CHECK_TIMEOUT)) { - CheckClientConnectionPending = false; - /* * Check for lost connection and re-arm, if still configured, but not * if we've arrived back at DoingCommandRead state. We don't want to @@ -3335,16 +3298,16 @@ ProcessInterrupts(void) if (!DoingCommandRead && client_connection_check_interval > 0) { if (!pq_check_connection()) - ClientConnectionLost = true; + RaiseInterrupt(INTERRUPT_CLIENT_CONNECTION_LOST); else enable_timeout_after(CLIENT_CONNECTION_CHECK_TIMEOUT, client_connection_check_interval); } } - if (ClientConnectionLost) + if (InterruptPending(INTERRUPT_CLIENT_CONNECTION_LOST)) { - QueryCancelPending = false; /* lost connection trumps QueryCancel */ + ClearInterrupt(INTERRUPT_QUERY_CANCEL); /* lost connection trumps QueryCancel */ LockErrorCleanup(); /* don't send to client, we already know the connection to be dead. */ whereToSendOutput = DestNone; @@ -3361,25 +3324,18 @@ ProcessInterrupts(void) * * See similar logic in ProcessRecoveryConflictInterrupts(). */ - if (QueryCancelPending && QueryCancelHoldoffCount != 0) + if (InterruptPending(INTERRUPT_QUERY_CANCEL) && QueryCancelHoldoffCount != 0) { /* - * Re-arm InterruptPending so that we process the cancel request as - * soon as we're done reading the message. (XXX this is seriously - * ugly: it complicates INTERRUPTS_CAN_BE_PROCESSED(), and it means we - * can't use that macro directly as the initial test in this function, - * meaning that this code also creates opportunities for other bugs to - * appear.) + * Leave the interrupt set so that we process the cancel request as + * soon as we're done reading the message. */ - InterruptPending = true; } - else if (QueryCancelPending) + else if (ConsumeInterrupt(INTERRUPT_QUERY_CANCEL)) { bool lock_timeout_occurred; bool stmt_timeout_occurred; - QueryCancelPending = false; - /* * If LOCK_TIMEOUT and STATEMENT_TIMEOUT indicators are both set, we * need to clear both, so always fetch both. @@ -3433,10 +3389,28 @@ ProcessInterrupts(void) } } - if (RecoveryConflictPending) - ProcessRecoveryConflictInterrupts(); + if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_DATABASE)) + ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_DATABASE); + + if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_TABLESPACE)) + ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_TABLESPACE); + + if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_LOCK)) + ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_LOCK); + + if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT)) + ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT); - if (IdleInTransactionSessionTimeoutPending) + if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT)) + ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT); + + if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN)) + ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN); + + if (ConsumeInterrupt(INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK)) + ProcessRecoveryConflictInterrupt(INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK); + + if (ConsumeInterrupt(INTERRUPT_IDLE_IN_TRANSACTION_SESSION_TIMEOUT)) { /* * If the GUC has been reset to zero, ignore the signal. This is @@ -3444,7 +3418,6 @@ ProcessInterrupts(void) * interrupt. We need to unset the flag before the injection point, * otherwise we could loop in interrupts checking. */ - IdleInTransactionSessionTimeoutPending = false; if (IdleInTransactionSessionTimeout > 0) { INJECTION_POINT("idle-in-transaction-session-timeout"); @@ -3454,10 +3427,9 @@ ProcessInterrupts(void) } } - if (TransactionTimeoutPending) + if (ConsumeInterrupt(INTERRUPT_TRANSACTION_TIMEOUT)) { /* As above, ignore the signal if the GUC has been reset to zero. */ - TransactionTimeoutPending = false; if (TransactionTimeout > 0) { INJECTION_POINT("transaction-timeout"); @@ -3467,10 +3439,9 @@ ProcessInterrupts(void) } } - if (IdleSessionTimeoutPending) + if (ConsumeInterrupt(INTERRUPT_IDLE_SESSION_TIMEOUT)) { /* As above, ignore the signal if the GUC has been reset to zero. */ - IdleSessionTimeoutPending = false; if (IdleSessionTimeout > 0) { INJECTION_POINT("idle-session-timeout"); @@ -3480,28 +3451,17 @@ ProcessInterrupts(void) } } - /* - * If there are pending stats updates and we currently are truly idle - * (matching the conditions in PostgresMain(), report stats now. - */ - if (IdleStatsUpdateTimeoutPending && - DoingCommandRead && !IsTransactionOrTransactionBlock()) - { - IdleStatsUpdateTimeoutPending = false; - pgstat_report_stat(true); - } - - if (ProcSignalBarrierPending) + if (InterruptPending(INTERRUPT_BARRIER)) ProcessProcSignalBarrier(); - if (ParallelMessagePending) + if (ConsumeInterrupt(INTERRUPT_PARALLEL_MESSAGE)) ProcessParallelMessages(); - if (LogMemoryContextPending) - ProcessLogMemoryContextInterrupt(); - - if (ParallelApplyMessagePending) + if (ConsumeInterrupt(INTERRUPT_PARALLEL_APPLY_MESSAGE)) ProcessParallelApplyMessages(); + + if (ConsumeInterrupt(INTERRUPT_LOG_MEMORY_CONTEXT)) + ProcessLogMemoryContextInterrupt(); } /* @@ -4204,7 +4164,7 @@ PostgresMain(const char *dbname, const char *username) * midst of output during who-knows-what operation... */ pqsignal(SIGPIPE, SIG_IGN); - pqsignal(SIGUSR1, procsignal_sigusr1_handler); + pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); pqsignal(SIGFPE, FloatExceptionHandler); @@ -4378,7 +4338,7 @@ PostgresMain(const char *dbname, const char *username) * forgetting a timeout cancel. */ disable_all_timeouts(false); /* do first to avoid race condition */ - QueryCancelPending = false; + ClearInterrupt(INTERRUPT_QUERY_CANCEL); idle_in_transaction_timeout_enabled = false; idle_session_timeout_enabled = false; @@ -4563,7 +4523,7 @@ PostgresMain(const char *dbname, const char *username) * were received during the just-finished transaction, they'll * be seen by the client before ReadyForQuery is. */ - if (notifyInterruptPending) + if (InterruptPending(INTERRUPT_ASYNC_NOTIFY)) ProcessNotifyInterrupt(false); /* @@ -4649,7 +4609,7 @@ PostgresMain(const char *dbname, const char *username) * * Query cancel is supposed to be a no-op when there is no query in * progress, so if a query cancel arrived while we were idle, just - * reset QueryCancelPending. ProcessInterrupts() has that effect when + * reset INTERRUPT_QUERY_CANCEL. ProcessInterrupts() has that effect when * it's called when DoingCommandRead is set, so check for interrupts * before resetting DoingCommandRead. */ @@ -4660,11 +4620,8 @@ PostgresMain(const char *dbname, const char *username) * (6) check for any other interesting events that happened while we * slept. */ - if (ConfigReloadPending) - { - ConfigReloadPending = false; + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) ProcessConfigFile(PGC_SIGHUP); - } /* * (7) process the command. But ignore it if we're skipping till diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c index 05a8ccfdb75..b19489706ba 100644 --- a/src/backend/utils/activity/pgstat_database.c +++ b/src/backend/utils/activity/pgstat_database.c @@ -17,7 +17,7 @@ #include "postgres.h" -#include "storage/procsignal.h" +#include "storage/interrupt.h" #include "utils/pgstat_internal.h" #include "utils/timestamp.h" @@ -78,7 +78,7 @@ pgstat_report_autovac(Oid dboid) * Report a Hot Standby recovery conflict. */ void -pgstat_report_recovery_conflict(int reason) +pgstat_report_recovery_conflict(InterruptType reason) { PgStat_StatDBEntry *dbentry; @@ -90,31 +90,33 @@ pgstat_report_recovery_conflict(int reason) switch (reason) { - case PROCSIG_RECOVERY_CONFLICT_DATABASE: + case INTERRUPT_RECOVERY_CONFLICT_DATABASE: /* * Since we drop the information about the database as soon as it * replicates, there is no point in counting these conflicts. */ break; - case PROCSIG_RECOVERY_CONFLICT_TABLESPACE: + case INTERRUPT_RECOVERY_CONFLICT_TABLESPACE: dbentry->conflict_tablespace++; break; - case PROCSIG_RECOVERY_CONFLICT_LOCK: + case INTERRUPT_RECOVERY_CONFLICT_LOCK: dbentry->conflict_lock++; break; - case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT: + case INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT: dbentry->conflict_snapshot++; break; - case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN: + case INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN: dbentry->conflict_bufferpin++; break; - case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT: + case INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT: dbentry->conflict_logicalslot++; break; - case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK: + case INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK: dbentry->conflict_startup_deadlock++; break; + default: + elog(LOG, "unexpected recovery conflict reason %u", (unsigned int) reason); } } diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c index 396c2f223b4..7aa6fa304c1 100644 --- a/src/backend/utils/adt/mcxtfuncs.c +++ b/src/backend/utils/adt/mcxtfuncs.c @@ -265,7 +265,6 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS) { int pid = PG_GETARG_INT32(0); PGPROC *proc; - ProcNumber procNumber = INVALID_PROC_NUMBER; /* * See if the process with given pid is a backend or an auxiliary process. @@ -294,14 +293,7 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); } - procNumber = GetNumberFromPGProc(proc); - if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, procNumber) < 0) - { - /* Again, just a warning to allow loops */ - ereport(WARNING, - (errmsg("could not send signal to process %d: %m", pid))); - PG_RETURN_BOOL(false); - } + SendInterrupt(INTERRUPT_LOG_MEMORY_CONTEXT, GetNumberFromPGProc(proc)); PG_RETURN_BOOL(true); } diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 160ea59d94b..5b33ec8d762 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -403,11 +403,10 @@ pg_sleep(PG_FUNCTION_ARGS) else break; - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, delay_ms, WAIT_EVENT_PG_SLEEP); - ClearInterrupt(INTERRUPT_GENERAL); } PG_RETURN_VOID(); diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index 79bc999cf26..7a7090a23b9 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -28,21 +28,12 @@ ProtocolVersion FrontendProtocol; -volatile sig_atomic_t InterruptPending = false; -volatile sig_atomic_t QueryCancelPending = false; -volatile sig_atomic_t ProcDiePending = false; -volatile sig_atomic_t CheckClientConnectionPending = false; -volatile sig_atomic_t ClientConnectionLost = false; -volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false; -volatile sig_atomic_t TransactionTimeoutPending = false; -volatile sig_atomic_t IdleSessionTimeoutPending = false; -volatile sig_atomic_t ProcSignalBarrierPending = false; -volatile sig_atomic_t LogMemoryContextPending = false; -volatile sig_atomic_t IdleStatsUpdateTimeoutPending = false; volatile uint32 InterruptHoldoffCount = 0; volatile uint32 QueryCancelHoldoffCount = 0; volatile uint32 CritSectionCount = 0; +volatile uint32 CheckForInterruptsMask = 0; + int MyProcPid; pg_time_t MyStartTime; TimestampTz MyStartTimestamp; diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index e54b5b39396..0147549f525 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -1382,41 +1382,31 @@ LockTimeoutHandler(void) static void TransactionTimeoutHandler(void) { - TransactionTimeoutPending = true; - InterruptPending = true; - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_TRANSACTION_TIMEOUT); } static void IdleInTransactionSessionTimeoutHandler(void) { - IdleInTransactionSessionTimeoutPending = true; - InterruptPending = true; - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_IDLE_IN_TRANSACTION_SESSION_TIMEOUT); } static void IdleSessionTimeoutHandler(void) { - IdleSessionTimeoutPending = true; - InterruptPending = true; - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_IDLE_SESSION_TIMEOUT); } static void IdleStatsUpdateTimeoutHandler(void) { - IdleStatsUpdateTimeoutPending = true; - InterruptPending = true; - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_IDLE_STATS_TIMEOUT); } static void ClientCheckTimeoutHandler(void) { - CheckClientConnectionPending = true; - InterruptPending = true; - RaiseInterrupt(INTERRUPT_GENERAL); + RaiseInterrupt(INTERRUPT_CLIENT_CHECK_TIMEOUT); } /* diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c index 93994190c71..d3220e419e6 100644 --- a/src/backend/utils/misc/timeout.c +++ b/src/backend/utils/misc/timeout.c @@ -370,12 +370,6 @@ handle_sig_alarm(SIGNAL_ARGS) */ HOLD_INTERRUPTS(); - /* - * SIGALRM is always cause for waking anything waiting on - * INTERRUPT_GENERAL. - */ - RaiseInterrupt(INTERRUPT_GENERAL); - /* * Always reset signal_pending, even if !alarm_enabled, since indeed no * signal is now pending. diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c index 5233a65aaa0..63cee06a12a 100644 --- a/src/backend/utils/mmgr/mcxt.c +++ b/src/backend/utils/mmgr/mcxt.c @@ -1260,27 +1260,11 @@ MemoryContextAllocExtended(MemoryContext context, Size size, int flags) return ret; } -/* - * HandleLogMemoryContextInterrupt - * Handle receipt of an interrupt indicating logging of memory - * contexts. - * - * All the actual work is deferred to ProcessLogMemoryContextInterrupt(), - * because we cannot safely emit a log message inside the signal handler. - */ -void -HandleLogMemoryContextInterrupt(void) -{ - InterruptPending = true; - LogMemoryContextPending = true; - /* INTERRUPT_GENERAL will be raised by procsignal_sigusr1_handler */ -} - /* * ProcessLogMemoryContextInterrupt * Perform logging of memory contexts of this backend process. * - * Any backend that participates in ProcSignal signaling must arrange + * Any backend that participates in standard interrupt handling must arrange * to call this function if we see LogMemoryContextPending set. * It is called from CHECK_FOR_INTERRUPTS(), which is enough because * the target process for logging of memory contexts is a backend. @@ -1288,7 +1272,7 @@ HandleLogMemoryContextInterrupt(void) void ProcessLogMemoryContextInterrupt(void) { - LogMemoryContextPending = false; + ClearInterrupt(INTERRUPT_LOG_MEMORY_CONTEXT); /* * Use LOG_SERVER_ONLY to prevent this message from being sent to the diff --git a/src/include/access/parallel.h b/src/include/access/parallel.h index a257d0a8ee6..b7bdada3646 100644 --- a/src/include/access/parallel.h +++ b/src/include/access/parallel.h @@ -14,8 +14,6 @@ #ifndef PARALLEL_H #define PARALLEL_H -#include - #include "access/xlogdefs.h" #include "lib/ilist.h" #include "postmaster/bgworker.h" @@ -55,7 +53,6 @@ typedef struct ParallelWorkerContext shm_toc *toc; } ParallelWorkerContext; -extern PGDLLIMPORT volatile sig_atomic_t ParallelMessagePending; extern PGDLLIMPORT int ParallelWorkerNumber; extern PGDLLIMPORT bool InitializingParallelWorker; @@ -72,7 +69,6 @@ extern void WaitForParallelWorkersToFinish(ParallelContext *pcxt); extern void DestroyParallelContext(ParallelContext *pcxt); extern bool ParallelContextActive(void); -extern void HandleParallelMessageInterrupt(void); extern void ProcessParallelMessages(void); extern void AtEOXact_Parallel(bool isCommit); extern void AtEOSubXact_Parallel(bool isCommit, SubTransactionId mySubId); diff --git a/src/include/commands/async.h b/src/include/commands/async.h index f75c3df9556..50773ddb0e2 100644 --- a/src/include/commands/async.h +++ b/src/include/commands/async.h @@ -13,11 +13,8 @@ #ifndef ASYNC_H #define ASYNC_H -#include - extern PGDLLIMPORT bool Trace_notify; extern PGDLLIMPORT int max_notify_queue_pages; -extern PGDLLIMPORT volatile sig_atomic_t notifyInterruptPending; extern Size AsyncShmemSize(void); extern void AsyncShmemInit(void); @@ -40,9 +37,6 @@ extern void AtSubCommit_Notify(void); extern void AtSubAbort_Notify(void); extern void AtPrepare_Notify(void); -/* signal handler for inbound notifies (PROCSIG_NOTIFY_INTERRUPT) */ -extern void HandleNotifyInterrupt(void); - /* process interrupts */ extern void ProcessNotifyInterrupt(bool flush); diff --git a/src/include/libpq/libpq-be-fe-helpers.h b/src/include/libpq/libpq-be-fe-helpers.h index f88ae8137d6..e76a90c83af 100644 --- a/src/include/libpq/libpq-be-fe-helpers.h +++ b/src/include/libpq/libpq-be-fe-helpers.h @@ -209,7 +209,7 @@ libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info) else io_flag = WL_SOCKET_WRITEABLE; - rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL, + rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK, WL_EXIT_ON_PM_DEATH | WL_INTERRUPT | io_flag, PQsocket(conn), 0, @@ -217,10 +217,7 @@ libpqsrv_connect_internal(PGconn *conn, uint32 wait_event_info) /* Interrupted? */ if (rc & WL_INTERRUPT) - { - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); - } /* If socket is ready, advance the libpq state machine */ if (rc & io_flag) @@ -341,7 +338,7 @@ libpqsrv_get_result(PGconn *conn, uint32 wait_event_info) { int rc; - rc = WaitInterruptOrSocket(1 << INTERRUPT_GENERAL, + rc = WaitInterruptOrSocket(INTERRUPT_CFI_MASK, WL_EXIT_ON_PM_DEATH | WL_INTERRUPT | WL_SOCKET_READABLE, PQsocket(conn), @@ -350,10 +347,7 @@ libpqsrv_get_result(PGconn *conn, uint32 wait_event_info) /* Interrupted? */ if (rc & WL_INTERRUPT) - { - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); - } /* Consume whatever data is available from the socket */ if (PQconsumeInput(conn) == 0) @@ -436,12 +430,10 @@ libpqsrv_cancel(PGconn *conn, TimestampTz endtime) } /* Sleep until there's something to do */ - WaitInterruptOrSocket(1 << INTERRUPT_GENERAL, waitEvents, + WaitInterruptOrSocket(INTERRUPT_CFI_MASK, waitEvents, PQcancelSocket(cancel_conn), cur_timeout, PG_WAIT_CLIENT); - ClearInterrupt(INTERRUPT_GENERAL); - CHECK_FOR_INTERRUPTS(); } exit: ; diff --git a/src/include/libpq/pqmq.h b/src/include/libpq/pqmq.h index 348b4494333..ae02aacfaa1 100644 --- a/src/include/libpq/pqmq.h +++ b/src/include/libpq/pqmq.h @@ -17,7 +17,7 @@ #include "storage/shm_mq.h" extern void pq_redirect_to_shm_mq(dsm_segment *seg, shm_mq_handle *mqh); -extern void pq_set_parallel_leader(pid_t pid, ProcNumber procNumber); +extern void pq_set_parallel_leader(ProcNumber procNumber); extern void pq_parse_errornotice(StringInfo msg, ErrorData *edata); diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index f5b773b79e5..949b5a59252 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -28,6 +28,9 @@ #include "datatype/timestamp.h" /* for TimestampTz */ #include "pgtime.h" /* for pg_time_t */ +#ifndef FRONTEND +#include "storage/interrupt.h" +#endif #define InvalidPid (-1) @@ -59,7 +62,7 @@ * * Note that ProcessInterrupts() has also acquired a number of tasks that * do not necessarily cause a query-cancel-or-die response. Hence, it's - * possible that it will just clear InterruptPending and return. + * possible that it will just clear InterruptPending and return. XXX * * INTERRUPTS_PENDING_CONDITION() can be checked to see whether an * interrupt needs to be serviced, without trying to do so immediately. @@ -86,64 +89,70 @@ *****************************************************************************/ /* in globals.c */ -/* these are marked volatile because they are set by signal handlers: */ -extern PGDLLIMPORT volatile sig_atomic_t InterruptPending; -extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending; -extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending; -extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending; -extern PGDLLIMPORT volatile sig_atomic_t TransactionTimeoutPending; -extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending; -extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending; -extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending; -extern PGDLLIMPORT volatile sig_atomic_t IdleStatsUpdateTimeoutPending; - -extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending; -extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost; - /* these are marked volatile because they are examined by signal handlers: */ +/* FIXME: is that still true, do these still need to be volatile? */ extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount; extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount; extern PGDLLIMPORT volatile uint32 CritSectionCount; +/* If you would call ProcessInterrupts now, it could handle these interrupts */ +/* XXX: volatile still needed? */ +extern PGDLLIMPORT volatile uint32 CheckForInterruptsMask; + /* in tcop/postgres.c */ extern void ProcessInterrupts(void); /* Test whether an interrupt is pending */ #ifndef WIN32 -#define INTERRUPTS_PENDING_CONDITION() \ - (unlikely(InterruptPending)) +#define INTERRUPTS_PENDING_CONDITION(mask) \ + (unlikely(InterruptPending(mask))) #else -#define INTERRUPTS_PENDING_CONDITION() \ +#define INTERRUPTS_PENDING_CONDITION(mask) \ (unlikely(UNBLOCKED_SIGNAL_QUEUE()) ? pgwin32_dispatch_queued_signals() : 0, \ - unlikely(InterruptPending)) + unlikely(InterruptPending(mask))) #endif /* Service interrupt, if one is pending and it's safe to service it now */ #define CHECK_FOR_INTERRUPTS() \ do { \ - if (INTERRUPTS_PENDING_CONDITION()) \ + if (INTERRUPTS_PENDING_CONDITION(CheckForInterruptsMask) && CritSectionCount == 0) \ ProcessInterrupts(); \ } while(0) -/* Is ProcessInterrupts() guaranteed to clear InterruptPending? */ -#define INTERRUPTS_CAN_BE_PROCESSED() \ - (InterruptHoldoffCount == 0 && CritSectionCount == 0 && \ - QueryCancelHoldoffCount == 0) +/* Is ProcessInterrupts() guaranteed to clear all the bits in 'mask'? */ +#define INTERRUPTS_CAN_BE_PROCESSED(mask) \ + (((mask) & ~CheckForInterruptsMask) == 0 && CritSectionCount == 0) -#define HOLD_INTERRUPTS() (InterruptHoldoffCount++) +#define HOLD_INTERRUPTS() \ +do { \ + CheckForInterruptsMask &= ~INTERRUPT_CFI_MASK; \ + InterruptHoldoffCount++; \ +} while(0) #define RESUME_INTERRUPTS() \ do { \ Assert(InterruptHoldoffCount > 0); \ InterruptHoldoffCount--; \ + if (InterruptHoldoffCount == 0) \ + { \ + CheckForInterruptsMask |= INTERRUPT_CFI_MASK; \ + if (QueryCancelHoldoffCount > 0) \ + CheckForInterruptsMask &= ~INTERRUPT_QUERY_CANCEL; \ + } \ } while(0) -#define HOLD_CANCEL_INTERRUPTS() (QueryCancelHoldoffCount++) +#define HOLD_CANCEL_INTERRUPTS() \ +do { \ + CheckForInterruptsMask &= ~INTERRUPT_QUERY_CANCEL; \ + QueryCancelHoldoffCount++; \ +} while(0) #define RESUME_CANCEL_INTERRUPTS() \ do { \ Assert(QueryCancelHoldoffCount > 0); \ QueryCancelHoldoffCount--; \ + if (QueryCancelHoldoffCount == 0 && InterruptHoldoffCount == 0) \ + CheckForInterruptsMask |= INTERRUPT_QUERY_CANCEL; \ } while(0) #define START_CRIT_SECTION() (CritSectionCount++) diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 4aad10b0b6d..be523d1ab3e 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -605,7 +605,7 @@ extern bool pgstat_tracks_io_op(BackendType bktype, IOObject io_object, extern void pgstat_drop_database(Oid databaseid); extern void pgstat_report_autovac(Oid dboid); -extern void pgstat_report_recovery_conflict(int reason); +extern void pgstat_report_recovery_conflict(InterruptType reason); extern void pgstat_report_deadlock(void); extern void pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount); extern void pgstat_report_checksum_failure(void); diff --git a/src/include/postmaster/interrupt_handlers.h b/src/include/postmaster/interrupt_handlers.h index b3a8c38353e..c6f75516c67 100644 --- a/src/include/postmaster/interrupt_handlers.h +++ b/src/include/postmaster/interrupt_handlers.h @@ -19,11 +19,6 @@ #ifndef INTERRUPT_HANDLERS_H #define INTERRUPT_HANDLERS_H -#include - -extern PGDLLIMPORT volatile sig_atomic_t ConfigReloadPending; -extern PGDLLIMPORT volatile sig_atomic_t ShutdownRequestPending; - extern void ProcessMainLoopInterrupts(void); extern void SignalHandlerForConfigReload(SIGNAL_ARGS); extern void SignalHandlerForCrashExit(SIGNAL_ARGS); diff --git a/src/include/replication/logicalworker.h b/src/include/replication/logicalworker.h index 88912606e4d..8900d1ed92f 100644 --- a/src/include/replication/logicalworker.h +++ b/src/include/replication/logicalworker.h @@ -12,10 +12,6 @@ #ifndef LOGICALWORKER_H #define LOGICALWORKER_H -#include - -extern PGDLLIMPORT volatile sig_atomic_t ParallelApplyMessagePending; - extern void ApplyWorkerMain(Datum main_arg); extern void ParallelApplyWorkerMain(Datum main_arg); extern void TablesyncWorkerMain(Datum main_arg); @@ -23,7 +19,6 @@ extern void TablesyncWorkerMain(Datum main_arg); extern bool IsLogicalWorker(void); extern bool IsLogicalParallelApplyWorker(void); -extern void HandleParallelApplyMessageInterrupt(void); extern void ProcessParallelApplyMessages(void); extern void LogicalRepWorkersWakeupAtCommit(Oid subid); diff --git a/src/include/replication/slot.h b/src/include/replication/slot.h index f5a24ccfbf2..b0ac9f70ae2 100644 --- a/src/include/replication/slot.h +++ b/src/include/replication/slot.h @@ -160,8 +160,8 @@ typedef struct ReplicationSlot /* is this slot defined */ bool in_use; - /* Who is streaming out changes for this slot? 0 in unused slots. */ - pid_t active_pid; + /* Who is streaming out changes for this slot? INVALID_PROC_NUMBER in unused slots. */ + ProcNumber active_proc; /* any outstanding modifications? */ bool just_dirtied; @@ -187,7 +187,7 @@ typedef struct ReplicationSlot /* is somebody performing io on this slot? */ LWLock io_in_progress_lock; - /* Condition variable signaled when active_pid changes */ + /* Condition variable signaled when active_proc changes */ ConditionVariable active_cv; /* all the remaining data is only used for logical slots */ diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h index c3e8e191339..24026fcfe5c 100644 --- a/src/include/replication/walsender.h +++ b/src/include/replication/walsender.h @@ -46,7 +46,7 @@ extern void WalSndShmemInit(void); extern void WalSndWakeup(bool physical, bool logical); extern void WalSndInitStopping(void); extern void WalSndWaitStopping(void); -extern void HandleWalSndInitStopping(void); +extern void ProcessWalSndInitStopping(void); extern void WalSndRqstFileReload(void); /* diff --git a/src/include/replication/walsender_private.h b/src/include/replication/walsender_private.h index 814b812432a..3f07fed7a35 100644 --- a/src/include/replication/walsender_private.h +++ b/src/include/replication/walsender_private.h @@ -40,6 +40,7 @@ typedef enum WalSndState */ typedef struct WalSnd { + ProcNumber pgprocno; /* this walsender's ProcNumber, or invalid if not active */ pid_t pid; /* this walsender's PID, or 0 if not active */ WalSndState state; /* this walsender's state */ diff --git a/src/include/replication/worker_internal.h b/src/include/replication/worker_internal.h index 30b2775952c..6781e9f6a6a 100644 --- a/src/include/replication/worker_internal.h +++ b/src/include/replication/worker_internal.h @@ -78,9 +78,10 @@ typedef struct LogicalRepWorker FileSet *stream_fileset; /* - * PID of leader apply worker if this slot is used for a parallel apply - * worker, InvalidPid otherwise. + * Leader apply worker if this slot is used for a parallel apply worker, + * INVALID_PROC_NUMBER/InvalidPid otherwise. */ + ProcNumber leader_pgprocno; pid_t leader_pid; /* Indicates whether apply can be performed in parallel. */ diff --git a/src/include/storage/interrupt.h b/src/include/storage/interrupt.h index 8a880cd4c29..234be78bdd2 100644 --- a/src/include/storage/interrupt.h +++ b/src/include/storage/interrupt.h @@ -12,18 +12,40 @@ * 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. + * + * CHECK_FOR_INTERRUPTS() + * ---------------------- * * The "standard" set of interrupts is handled by CHECK_FOR_INTERRUPTS(), and - * consists of tasks that are safe to perform at most times. They can be - * suppressed by HOLD_INTERRUPTS()/RESUME_INTERRUPTS(). + * consists of tasks that are safe to perform at most times. It includes + * things like query cancellation and 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. They can be suppressed + * by HOLD_INTERRUPTS()/RESUME_INTERRUPTS(). + * + * Other special interrupts are checked for explicitly. + * + * Standard Signal handlers + * ------------------------ + * + * Responses to signals that are translated to interrupts are fairly varied + * and many types of backends have their own implementations, but we provide a + * few generic signal handlers and interrupt checks in + * postmaster/interrupt_handlers.c to facilitate code reuse. * + * Multiplexed INTERRUPT_GENERAL + * ----------------------------- + * + * The INTERRUPT_GENERAL interrupt is multiplexed for many different purposes + * that don't warrant a dedicated interrupt bit. Because it's reused for + * different purposes, waiters must tolerate receiving spurious interrupt + * wakeups. + * + * Waiting on an interrupt + * ----------------------- * * The correct pattern to wait for event(s) using INTERRUPT_GENERAL is: * @@ -32,11 +54,11 @@ * ClearInterrupt(INTERRUPT_GENERAL); * if (work to do) * Do Stuff(); - * WaitInterrupt(1 << INTERRUPT_GENERAL, ...); + * WaitInterrupt(INTERRUPT_GENERAL, ...); * } * * It's important to clear the interrupt *before* checking if there's work to - * do. Otherwise, if someone sets the interrupt between the check and the + * 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: @@ -45,7 +67,7 @@ * { * if (work to do) * Do Stuff(); // in particular, exit loop if some condition satisfied - * WaitInterrupt(1 << INTERRUPT_GENERAL, ...); + * WaitInterrupt(INTERRUPT_GENERAL, ...); * ClearInterrupt(INTERRUPT_GENERAL); * } * @@ -62,6 +84,7 @@ * 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 * @@ -76,9 +99,7 @@ #include "port/atomics.h" #include "storage/procnumber.h" -#include "storage/waiteventset.h" - -#include +#include "storage/waiteventset.h" /* WL_* are defined in waiteventset.h */ extern PGDLLIMPORT pg_atomic_uint32 *MyPendingInterrupts; @@ -89,80 +110,219 @@ extern PGDLLIMPORT pg_atomic_uint32 *MyPendingInterrupts; 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. + * INTERRUPT_GENERAL is used as a general-purpose wakeup, multiplexed for + * many reasons. + */ + INTERRUPT_GENERAL = 1 << 0, + + /* + * Because backends sitting idle will not be reading sinval events, we + * need a way to give an idle backend a swift kick in the rear and make it + * catch up before the sinval queue overflows and forces it to go through + * a cache reset exercise. This is done by sending + * INTERRUPT_SINVAL_CATCHUP to any backend that gets too far behind. + * + * The interrupt is processed whenever starting to read from the client, + * or when interrupted while doing so, ProcessClientReadInterrupt() will + * call ProcessCatchupInterrupt(). */ - SLEEPING_ON_INTERRUPTS = 0, + INTERRUPT_SINVAL_CATCHUP = 1 << 1, /* - * 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_ASYNC_NOTIFY is sent to notify backends that have registered + * to LISTEN on any channels that they might have messages they need to + * deliver to the frontent. It is also processed whenever starting to read + * from the client or while doing so, but only when there is no + * transaction in progress. */ - INTERRUPT_GENERAL, + INTERRUPT_ASYNC_NOTIFY = 1 << 2, + + /* Raised by timer while idle, to send a stats update */ + INTERRUPT_IDLE_STATS_TIMEOUT = 1 << 3, + + /* Config file reload is requested */ + INTERRUPT_CONFIG_RELOAD = 1 << 4, /* - * INTERRUPT_RECOVERY_CONTINUE is used to wake up 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 is used to wake up the startup process, to + * tell it that it should continue WAL replay. It's sent by WAL receiver + * when more WAL arrives, or when promotion is requested. We don't reuse + * INTERRUPT_GENERAL for this, so that more WAL arriving doesn't wake up + * the startup process excessively when we're waiting in other places, + * like for recovery conflicts. */ - INTERRUPT_RECOVERY_CONTINUE, + INTERRUPT_RECOVERY_CONTINUE = 1 << 5, /* sent to logical replication launcher, when a subscription changes */ - INTERRUPT_SUBSCRIPTION_CHANGE, + INTERRUPT_SUBSCRIPTION_CHANGE = 1 << 6, + + /* + * Many aux processes don't want to react to INTERRUPT_DIE in + * CHECK_FOR_INTERRUPTS(), so they use a separate flag when shutdown is + * requested. + * + * TODO: perhaps use INTERRUPT_DIE, but teach CHECK_FOR_INTERRUPTS() to + * ignore it in aux processes, and remove it from CheckForInterruptsMask. + * That would save one interrupt bit, and would make things more + * consistent. + */ + INTERRUPT_SHUTDOWN_AUX = 1 << 7, + + /* + * Perform one last checkpoint, then shut down. Only used in the checkpointer + * process. + */ + INTERRUPT_SHUTDOWN_XLOG = 1 << 8, + + /*---- Interrupts handled by CHECK_FOR_INTERRUPTS() ----*/ + + /* + * Backend has been requested to terminate + * + * This is raised by the SIGTERM signal handler, or can be sent directly + * by another backend e.g. with pg_terminate_backend(). + */ + INTERRUPT_DIE = 1 << 9, + + /* + * Cancel current query, if any. + * + * This is raised by the SIGTERM signal handler, or can be sent directly + * by another backend e.g. with pg_cancel_backend(), or in response to a + * query cancellation packet. + */ + INTERRUPT_QUERY_CANCEL = 1 << 10, + + /* ask walsenders to prepare for shutdown */ + INTERRUPT_WALSND_INIT_STOPPING = 1 << 11, + + /* + * Recovery conflict reasons. These are sent by the startup process in hot + * standby mode when a backend holds back the WAL replay for too long. + */ + INTERRUPT_RECOVERY_CONFLICT_DATABASE = 1 << 12, + INTERRUPT_RECOVERY_CONFLICT_TABLESPACE = 1 << 13, + INTERRUPT_RECOVERY_CONFLICT_LOCK = 1 << 14, + INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT = 1 << 15, + INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN = 1 << 16, + INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK = 1 << 17, + INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT = 1 << 18, + + /* Raised by timers */ + INTERRUPT_TRANSACTION_TIMEOUT = 1 << 19, + INTERRUPT_IDLE_SESSION_TIMEOUT = 1 << 20, + INTERRUPT_IDLE_IN_TRANSACTION_SESSION_TIMEOUT = 1 << 21, + INTERRUPT_CLIENT_CHECK_TIMEOUT = 1 << 22, + + /* Raised synchronously when the client connection is lost */ + INTERRUPT_CLIENT_CONNECTION_LOST = 1 << 23, + + /* Ask backend to log the memory contexts */ + INTERRUPT_LOG_MEMORY_CONTEXT = 1 << 24, + + /* Message from a cooperating parallel backend */ + INTERRUPT_PARALLEL_MESSAGE = 1 << 25, + + /* Message from a parallel apply worker */ + INTERRUPT_PARALLEL_APPLY_MESSAGE = 1 << 26, + + /* procsignal global barrier interrupt */ + INTERRUPT_BARRIER = 1 << 27, + + /*---- end of interrupts handled by CHECK_FOR_INTERRUPTS() ----*/ + + /* + * NOTE: InterruptTypes must fit in a 32-bit bitmask. (If we had efficient + * 64-bit atomics on all platforms, we could easily go up to 64 bits) + */ + + /* + * SLEEPING_ON_INTERRUPTS indicates that the backend is currently blocked + * waiting for an interrupt. If it's set, the backend needs to be woken up + * when a bit in the pending interrupts mask is set. It's used internally + * by the interrupt machinery, and cannot be used directly in the public + * functions. It's named differently to distinguish it from the actual + * interrupt flags. + */ + SLEEPING_ON_INTERRUPTS = 1 << 31, + } InterruptType; -/* - * Test an interrupt flag. - */ -static inline bool -InterruptIsPending(InterruptType reason) -{ - return (pg_atomic_read_u32(MyPendingInterrupts) & (1 << reason)) != 0; -} +/* This is the set of interrupts that are processed by CHECK_FOR_INTERRUPTS */ +#define INTERRUPT_CFI_MASK ( \ + INTERRUPT_DIE | \ + INTERRUPT_QUERY_CANCEL | \ + INTERRUPT_WALSND_INIT_STOPPING | \ + INTERRUPT_RECOVERY_CONFLICT_DATABASE | \ + INTERRUPT_RECOVERY_CONFLICT_TABLESPACE | \ + INTERRUPT_RECOVERY_CONFLICT_LOCK | \ + INTERRUPT_RECOVERY_CONFLICT_SNAPSHOT | \ + INTERRUPT_RECOVERY_CONFLICT_BUFFERPIN | \ + INTERRUPT_RECOVERY_CONFLICT_STARTUP_DEADLOCK | \ + INTERRUPT_RECOVERY_CONFLICT_LOGICALSLOT | \ + INTERRUPT_TRANSACTION_TIMEOUT | \ + INTERRUPT_IDLE_SESSION_TIMEOUT | \ + INTERRUPT_IDLE_IN_TRANSACTION_SESSION_TIMEOUT | \ + INTERRUPT_CLIENT_CHECK_TIMEOUT | \ + INTERRUPT_CLIENT_CONNECTION_LOST | \ + INTERRUPT_LOG_MEMORY_CONTEXT | \ + INTERRUPT_PARALLEL_MESSAGE | \ + INTERRUPT_PARALLEL_APPLY_MESSAGE | \ + INTERRUPT_BARRIER \ + ) + +/* This is the set of interrupts that are processed by ProcessStartupProcInterrupts */ +#define INTERRUPT_STARTUP_PROC_MASK ( \ + INTERRUPT_BARRIER | \ + INTERRUPT_DIE | \ + INTERRUPT_LOG_MEMORY_CONTEXT | \ + INTERRUPT_CONFIG_RELOAD \ + ) + +/* This is the set of interrupts that are processed by ProcessMainLoopInterrupts */ +#define INTERRUPT_MAIN_LOOP_MASK ( \ + INTERRUPT_BARRIER | \ + INTERRUPT_SHUTDOWN_AUX | \ + INTERRUPT_LOG_MEMORY_CONTEXT | \ + INTERRUPT_CONFIG_RELOAD \ + ) /* - * Test an interrupt flag. + * Test an interrupt flag (or flags). */ static inline bool -InterruptsPending(uint32 mask) +InterruptPending(uint32 interruptMask) { - return (pg_atomic_read_u32(MyPendingInterrupts) & (mask)) != 0; + pg_read_barrier(); + return (pg_atomic_read_u32(MyPendingInterrupts) & interruptMask) != 0; } /* - * Clear an interrupt flag. + * Clear an interrupt flag (or flags). */ static inline void -ClearInterrupt(InterruptType reason) +ClearInterrupt(uint32 interruptMask) { - pg_atomic_fetch_and_u32(MyPendingInterrupts, ~(1 << reason)); + pg_atomic_fetch_and_u32(MyPendingInterrupts, ~interruptMask); + pg_write_barrier(); } /* - * Test and clear an interrupt flag. + * Test and clear an interrupt flag (or flags). */ static inline bool -ConsumeInterrupt(InterruptType reason) +ConsumeInterrupt(uint32 interruptMask) { - if (likely(!InterruptIsPending(reason))) + if (likely(!InterruptPending(interruptMask))) return false; - ClearInterrupt(reason); + ClearInterrupt(interruptMask); return true; } -extern void RaiseInterrupt(InterruptType reason); -extern void SendInterrupt(InterruptType reason, ProcNumber pgprocno); +extern void RaiseInterrupt(uint32 interruptMask); +extern void SendInterrupt(uint32 interruptMask, ProcNumber pgprocno); extern int WaitInterrupt(uint32 interruptMask, int wakeEvents, long timeout, uint32 wait_event_info); extern int WaitInterruptOrSocket(uint32 interruptMask, int wakeEvents, pgsocket sock, diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index ef0b733ebe8..e8b8c0a653e 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -14,6 +14,7 @@ #ifndef PROCARRAY_H #define PROCARRAY_H +#include "storage/interrupt.h" #include "storage/lock.h" #include "storage/standby.h" #include "utils/relcache.h" @@ -77,14 +78,14 @@ extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0, bool allDbs, int excludeVacuum, int *nvxids); extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid); -extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode); -extern pid_t SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode, +extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, InterruptType reason); +extern pid_t SignalVirtualTransaction(VirtualTransactionId vxid, InterruptType reason, bool conflictPending); extern bool MinimumActiveBackends(int min); extern int CountDBBackends(Oid databaseid); extern int CountDBConnections(Oid databaseid); -extern void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending); +extern void CancelDBBackends(Oid databaseid, InterruptType reason, bool conflictPending); extern int CountUserBackends(Oid roleid); extern bool CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared); diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h index 022fd8ed933..8d917f1bec4 100644 --- a/src/include/storage/procsignal.h +++ b/src/include/storage/procsignal.h @@ -14,43 +14,6 @@ #ifndef PROCSIGNAL_H #define PROCSIGNAL_H -#include "storage/procnumber.h" - - -/* - * Reasons for signaling a Postgres child process (a backend or an auxiliary - * process, like checkpointer). We can cope with concurrent signals for different - * reasons. However, if the same reason is signaled multiple times in quick - * succession, the process is likely to observe only one notification of it. - * This is okay for the present uses. - * - * Also, because of race conditions, it's important that all the signals be - * defined so that no harm is done if a process mistakenly receives one. - */ -typedef enum -{ - PROCSIG_CATCHUP_INTERRUPT, /* sinval catchup interrupt */ - PROCSIG_NOTIFY_INTERRUPT, /* listen/notify interrupt */ - PROCSIG_PARALLEL_MESSAGE, /* message from cooperating parallel backend */ - PROCSIG_WALSND_INIT_STOPPING, /* ask walsenders to prepare for shutdown */ - PROCSIG_BARRIER, /* global barrier interrupt */ - PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */ - PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */ - - /* Recovery conflict reasons */ - PROCSIG_RECOVERY_CONFLICT_FIRST, - PROCSIG_RECOVERY_CONFLICT_DATABASE = PROCSIG_RECOVERY_CONFLICT_FIRST, - PROCSIG_RECOVERY_CONFLICT_TABLESPACE, - PROCSIG_RECOVERY_CONFLICT_LOCK, - PROCSIG_RECOVERY_CONFLICT_SNAPSHOT, - PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT, - PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, - PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK, - PROCSIG_RECOVERY_CONFLICT_LAST = PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK, -} ProcSignalReason; - -#define NUM_PROCSIGNALS (PROCSIG_RECOVERY_CONFLICT_LAST + 1) - typedef enum { PROCSIGNAL_BARRIER_SMGRRELEASE, /* ask smgr to close files */ @@ -63,16 +26,12 @@ extern Size ProcSignalShmemSize(void); extern void ProcSignalShmemInit(void); extern void ProcSignalInit(bool cancel_key_valid, int32 cancel_key); -extern int SendProcSignal(pid_t pid, ProcSignalReason reason, - ProcNumber procNumber); extern void SendCancelRequest(int backendPID, int32 cancelAuthCode); extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type); extern void WaitForProcSignalBarrier(uint64 generation); extern void ProcessProcSignalBarrier(void); -extern void procsignal_sigusr1_handler(SIGNAL_ARGS); - /* ProcSignalHeader is an opaque struct, details known only within procsignal.c */ typedef struct ProcSignalHeader ProcSignalHeader; diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h index 2463c0f9fac..cc627ae4853 100644 --- a/src/include/storage/sinval.h +++ b/src/include/storage/sinval.h @@ -125,16 +125,11 @@ typedef union /* Counter of messages processed; don't worry about overflow. */ extern PGDLLIMPORT uint64 SharedInvalidMessageCounter; -extern PGDLLIMPORT volatile sig_atomic_t catchupInterruptPending; - extern void SendSharedInvalidMessages(const SharedInvalidationMessage *msgs, int n); extern void ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *msg), void (*resetFunction) (void)); -/* signal handler for catchup events (PROCSIG_CATCHUP_INTERRUPT) */ -extern void HandleCatchupInterrupt(void); - /* * enable/disable processing of catchup events directly from signal handler. * The enable routine first performs processing of any catchup events that diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h index 24e2f5082bc..960d08ca294 100644 --- a/src/include/storage/standby.h +++ b/src/include/storage/standby.h @@ -15,8 +15,8 @@ #define STANDBY_H #include "datatype/timestamp.h" +#include "storage/interrupt.h" #include "storage/lock.h" -#include "storage/procsignal.h" #include "storage/relfilelocator.h" #include "storage/standbydefs.h" @@ -43,7 +43,7 @@ extern void CheckRecoveryConflictDeadlock(void); extern void StandbyDeadLockHandler(void); extern void StandbyTimeoutHandler(void); extern void StandbyLockTimeoutHandler(void); -extern void LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start, +extern void LogRecoveryConflict(InterruptType reason, TimestampTz wait_start, TimestampTz now, VirtualTransactionId *wait_list, bool still_waiting); diff --git a/src/include/storage/waiteventset.h b/src/include/storage/waiteventset.h index 60ececb785c..c9ab8136b53 100644 --- a/src/include/storage/waiteventset.h +++ b/src/include/storage/waiteventset.h @@ -27,7 +27,6 @@ #include #include "storage/procnumber.h" -#include "utils/resowner.h" /* * Bitmasks for events that may wake-up WaitInterrupt(), @@ -82,7 +81,9 @@ extern void InitializeWaitEventSupport(void); extern HANDLE CreateSharedWakeupHandle(void); #endif -extern WaitEventSet *CreateWaitEventSet(ResourceOwner resowner, int nevents); +struct ResourceOwnerData; + +extern WaitEventSet *CreateWaitEventSet(struct ResourceOwnerData *resowner, int nevents); extern void FreeWaitEventSet(WaitEventSet *set); extern void FreeWaitEventSetAfterFork(WaitEventSet *set); extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index a62367f7793..908f39217b0 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -16,7 +16,6 @@ #include "nodes/params.h" #include "nodes/plannodes.h" -#include "storage/procsignal.h" #include "utils/guc.h" #include "utils/queryenvironment.h" @@ -72,9 +71,8 @@ extern void die(SIGNAL_ARGS); extern void quickdie(SIGNAL_ARGS) pg_attribute_noreturn(); extern void StatementCancelHandler(SIGNAL_ARGS); extern void FloatExceptionHandler(SIGNAL_ARGS) pg_attribute_noreturn(); -extern void HandleRecoveryConflictInterrupt(ProcSignalReason reason); -extern void ProcessClientReadInterrupt(bool blocked); -extern void ProcessClientWriteInterrupt(bool blocked); +extern uint32 ProcessClientReadInterrupt(bool blocked); +extern uint32 ProcessClientWriteInterrupt(bool blocked); extern void process_postgres_switches(int argc, char *argv[], GucContext ctx, const char **dbname); diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h index 8abc26abce2..5dce202fe90 100644 --- a/src/include/utils/memutils.h +++ b/src/include/utils/memutils.h @@ -101,7 +101,6 @@ extern void MemoryContextCheck(MemoryContext context); #define MemoryContextCopyAndSetIdentifier(cxt, id) \ MemoryContextSetIdentifier(cxt, MemoryContextStrdup(cxt, id)) -extern void HandleLogMemoryContextInterrupt(void); extern void ProcessLogMemoryContextInterrupt(void); /* diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c index 5f31bf7082b..aaf3a9ae199 100644 --- a/src/test/modules/test_shm_mq/setup.c +++ b/src/test/modules/test_shm_mq/setup.c @@ -263,6 +263,9 @@ wait_for_workers_to_become_ready(worker_state *wstate, { int workers_ready; + /* Clear the interrupt flag so we don't spin. */ + ClearInterrupt(INTERRUPT_GENERAL); + /* If all the workers are ready, we have succeeded. */ SpinLockAcquire(&hdr->mutex); workers_ready = hdr->workers_ready; @@ -285,12 +288,10 @@ wait_for_workers_to_become_ready(worker_state *wstate, we_bgworker_startup = WaitEventExtensionNew("TestShmMqBgWorkerStartup"); /* Wait to be signaled. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, we_bgworker_startup); - /* Clear the interrupt flag so we don't spin. */ - ClearInterrupt(INTERRUPT_GENERAL); - /* An interrupt may have occurred while we were waiting. */ CHECK_FOR_INTERRUPTS(); } diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c index ce91d74ca20..f063cab9bad 100644 --- a/src/test/modules/test_shm_mq/test.c +++ b/src/test/modules/test_shm_mq/test.c @@ -171,6 +171,8 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS) { bool wait = true; + ClearInterrupt(INTERRUPT_GENERAL); + /* * If we haven't yet sent the message the requisite number of times, * try again to send it now. Note that when shm_mq_send() returns @@ -239,9 +241,9 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS) * they have read or written data and therefore there may now be * work for us to do. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | INTERRUPT_GENERAL, + WL_INTERRUPT | WL_EXIT_ON_PM_DEATH, 0, we_message_queue); - ClearInterrupt(INTERRUPT_GENERAL); CHECK_FOR_INTERRUPTS(); } } diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c index 5736620832f..84052a992ca 100644 --- a/src/test/modules/worker_spi/worker_spi.c +++ b/src/test/modules/worker_spi/worker_spi.c @@ -217,7 +217,9 @@ worker_spi_main(Datum main_arg) * is awakened if postmaster dies. That way the background process * goes away immediately in an emergency. */ - (void) WaitInterrupt(1 << INTERRUPT_GENERAL, + (void) WaitInterrupt(INTERRUPT_CFI_MASK | + INTERRUPT_CONFIG_RELOAD | + INTERRUPT_GENERAL, WL_INTERRUPT | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, worker_spi_naptime * 1000L, worker_spi_wait_event_main); @@ -228,11 +230,8 @@ worker_spi_main(Datum main_arg) /* * In case of a SIGHUP, just reload the configuration. */ - if (ConfigReloadPending) - { - ConfigReloadPending = false; + if (ConsumeInterrupt(INTERRUPT_CONFIG_RELOAD)) ProcessConfigFile(PGC_SIGHUP); - } /* * Start a transaction on which we can run queries. Note that each -- 2.39.5