v10-0005-Introduce-PendingInterrupts-struct-with-separate.patch
text/x-patch
Filename: v10-0005-Introduce-PendingInterrupts-struct-with-separate.patch
Type: text/x-patch
Part: 4
Message:
Re: Interrupts vs signals
Patch
Format: format-patch
Series: patch v10-0005
Subject: Introduce PendingInterrupts struct with separate 'flags' field
| File | + | − |
|---|---|---|
| src/backend/ipc/interrupt.c | 129 | 54 |
| src/backend/storage/ipc/waiteventset.c | 23 | 14 |
| src/include/ipc/interrupt.h | 117 | 19 |
| src/include/storage/proc.h | 1 | 1 |
| src/tools/pgindent/typedefs.list | 1 | 0 |
From 6e5b9fcd7049314a113961447cc3d1545e19133e Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Wed, 18 Feb 2026 00:55:34 +0200
Subject: [PATCH v10 5/5] Introduce PendingInterrupts struct with separate
'flags' field
This does a couple of things:
- Split the interrupts mask into two 32-bit fields on
PG_HAVE_ATOMIC_U64_SIMULATION systems. This fixes the self-deadlock
risk when used from signal handlers.
- Instead of stealing bits from the interrupts mask itself, introduce
a new 'flags' field for the SLEEPING_ON_INTERRUPTS flag, and for the
new CFI_ATTENTION flag (see next bullet). One reason for this is
that it makes the PG_HAVE_ATOMIC_U64_SIMULATION codepaths less
special, since we now need to be careful about the order that the
flag and the interrupt bits are manipulated, even with 64-bit
atomics. (It's not nice that we have to be careful, of course, but
it's nice that the codepaths don't differ so much).
- Introduce a new CFI_ATTENTION flag. It is set whenever any of the
interrupt bits are set. This allows CHECK_FOR_INTERRUPTS() to be
smaller, as it can just check the CFI_ATTENTION flag and fall
through very quickly in the common case. ProcessInterrupts() and all
the places where CheckForInterruptsMask is changed need to clear and
recheck the flag. This makes CHECK_FOR_INTERRUPTS() smaller and
faster, at the cost of making SendInterrupt more complicated, and
having CHECK_FOR_INTERRUPTS() call ProcessInterrupts() unnecessarily
when disabled interrupts are set, but that seems like a good
tradeoff.
---
src/backend/ipc/interrupt.c | 183 +++++++++++++++++--------
src/backend/storage/ipc/waiteventset.c | 37 +++--
src/include/ipc/interrupt.h | 136 +++++++++++++++---
src/include/storage/proc.h | 2 +-
src/tools/pgindent/typedefs.list | 1 +
5 files changed, 271 insertions(+), 88 deletions(-)
diff --git a/src/backend/ipc/interrupt.c b/src/backend/ipc/interrupt.c
index 61d148409bc..2f39cdf4aff 100644
--- a/src/backend/ipc/interrupt.c
+++ b/src/backend/ipc/interrupt.c
@@ -53,9 +53,9 @@ static WaitEventSet *InterruptWaitSet;
#define InterruptWaitSetInterruptPos 0
#define InterruptWaitSetPostmasterDeathPos 1
-static pg_atomic_uint64 LocalPendingInterrupts;
+static PendingInterrupts LocalPendingInterrupts;
-pg_atomic_uint64 *MyPendingInterrupts = &LocalPendingInterrupts;
+PendingInterrupts *MyPendingInterrupts = &LocalPendingInterrupts;
static int nextAddinInterruptBit = BEGIN_ADDIN_INTERRUPTS;
@@ -94,10 +94,12 @@ EnableInterrupt(InterruptMask interruptMask)
Assert(interrupt_handlers[i] != NULL);
}
#endif
-
EnabledInterruptsMask |= interruptMask;
if (InterruptHoldoffCount == 0 && CritSectionCount == 0)
+ {
CheckForInterruptsMask = EnabledInterruptsMask;
+ RECHECK_CFI_ATTENTION();
+ }
}
/*
@@ -113,7 +115,10 @@ DisableInterrupt(InterruptMask interruptMask)
{
EnabledInterruptsMask &= ~interruptMask;
if (InterruptHoldoffCount == 0 && CritSectionCount == 0)
+ {
CheckForInterruptsMask = EnabledInterruptsMask;
+ RECHECK_CFI_ATTENTION();
+ }
}
/*
@@ -132,47 +137,79 @@ DisableInterrupt(InterruptMask interruptMask)
void
ProcessInterrupts(void)
{
+ uint64 pending;
InterruptMask interruptsToProcess;
- Assert(InterruptHoldoffCount == 0 && CritSectionCount == 0);
+ /*
+ * This shouldn't be called while sleeping. CHECK_FOR_INTERRUPTS() relies
+ * on there being no CHECK_FOR_INTERRUPTS() calls in the code that runs
+ * while PI_FLAG_SLEEPING_ON_INTERRUPTS is set. That assumption allows
+ * CHECK_FOR_INTERRUPTS() to check "MyPendingInterrupts->flags == 0",
+ * which is slightly less expensive than "(MyPendingInterrupts->flags &
+ * PI_FLAG_CFI_ATTENTION) == 0".
+ */
+ Assert((pg_atomic_read_u32(&MyPendingInterrupts->flags) &
+ PI_FLAG_SLEEPING_ON_INTERRUPTS) == 0);
/* Check once what interrupts are pending */
- interruptsToProcess =
- pg_atomic_read_u64(MyPendingInterrupts) & CheckForInterruptsMask;
+#ifndef PG_HAVE_ATOMIC_U64_SIMULATION
+ pending = pg_atomic_read_u64(&MyPendingInterrupts->interrupts);
+#else
+ pending = (uint64) pg_atomic_read_u32(&MyPendingInterrupts->interrupts_lo);
+ pending |= (uint64) pg_atomic_read_u32(&MyPendingInterrupts->interrupts_hi) << 32;
+#endif
+ interruptsToProcess = pending & CheckForInterruptsMask;
- for (int i = 0; i < lengthof(interrupt_handlers); i++)
+ if (interruptsToProcess != 0)
{
- if ((interruptsToProcess & UINT64_BIT(i)) != 0)
+ Assert(InterruptHoldoffCount == 0 && CritSectionCount == 0);
+
+ for (int i = 0; i < lengthof(interrupt_handlers); i++)
{
- /*
- * Clear the interrupt *before* calling the handler function, so
- * that if the interrupt is received again while the handler
- * function is being executed, we won't miss it.
- *
- * For similar reasons, we also clear the flags one by one even if
- * multiple interrupts are pending. Otherwise if one of the
- * interrupt handlers bail out with an ERROR, we would have
- * already cleared the other bits, and would miss processing them.
- */
- ClearInterrupt(UINT64_BIT(i));
-
- /* Call the handler function */
- (*interrupt_handlers[i]) ();
+ if ((interruptsToProcess & UINT64_BIT(i)) != 0)
+ {
+ /*
+ * Clear the interrupt *before* calling the handler function,
+ * so that if the interrupt is received again while the
+ * handler function is being executed, we won't miss it.
+ *
+ * For similar reasons, we also clear the flags one by one
+ * even if multiple interrupts are pending. Otherwise if one
+ * of the interrupt handlers bail out with an ERROR, we would
+ * have already cleared the other bits, and would miss
+ * processing them.
+ */
+ ClearInterrupt(UINT64_BIT(i));
+
+ /* Call the handler function */
+ (*interrupt_handlers[i]) ();
+ }
}
}
+
+ /*
+ * We can clear the CFI_ATTENTION flag now (unless new interrupts arrived
+ * while we were processing).
+ */
+ RECHECK_CFI_ATTENTION();
}
+
/*
- * Switch to local interrupts. Other backends can't send interrupts to this
- * one. Only RaiseInterrupt() can set them, from inside this process.
+ * Move all the bits from *src to *dst, clearing all the bits in *dst.
*/
-void
-SwitchToLocalInterrupts(void)
+static void
+SwitchMyPendingInterruptsPtr(PendingInterrupts *new_ptr)
{
- if (MyPendingInterrupts == &LocalPendingInterrupts)
+ PendingInterrupts *old_ptr = MyPendingInterrupts;
+
+ /* should not be called while sleeping */
+ Assert((pg_atomic_read_u32(&MyPendingInterrupts->flags) & PI_FLAG_SLEEPING_ON_INTERRUPTS) == 0);
+
+ if (new_ptr == old_ptr)
return;
- MyPendingInterrupts = &LocalPendingInterrupts;
+ MyPendingInterrupts = new_ptr;
/*
* Make sure that SIGALRM handlers that call RaiseInterrupt() are now
@@ -181,14 +218,42 @@ SwitchToLocalInterrupts(void)
pg_memory_barrier();
/*
- * Mix in the interrupts that we have received already in our shared
- * interrupt vector, while atomically clearing it. Other backends may
- * continue to set bits in it after this point, but we've atomically
+ * Mix in the interrupts that we have received already in 'new_ptr', while
+ * atomically clearing them from 'old_ptr'. Other backends may continue
+ * to set bits in 'old_ptr' after this point, but we've atomically
* transferred the existing bits to our local vector so we won't get
* duplicated interrupts later if we switch back.
*/
- pg_atomic_fetch_or_u64(MyPendingInterrupts,
- pg_atomic_exchange_u64(&MyProc->pendingInterrupts, 0));
+#ifndef PG_HAVE_ATOMIC_U64_SIMULATION
+ {
+ uint64 old_interrupts;
+
+ old_interrupts = pg_atomic_exchange_u64(&old_ptr->interrupts, 0);
+ pg_atomic_fetch_or_u64(&new_ptr->interrupts, old_interrupts);
+ }
+#else
+ {
+ uint32 old_interrupts_lo;
+ uint32 old_interrupts_hi;
+
+ old_interrupts_lo = pg_atomic_exchange_u32(&old_ptr->interrupts_lo, 0);
+ old_interrupts_hi = pg_atomic_exchange_u32(&old_ptr->interrupts_hi, 0);
+ pg_atomic_fetch_or_u32(&new_ptr->interrupts_lo, old_interrupts_lo);
+ pg_atomic_fetch_or_u32(&new_ptr->interrupts_hi, old_interrupts_hi);
+ }
+#endif
+
+ RECHECK_CFI_ATTENTION();
+}
+
+/*
+ * Switch to local interrupts. Other backends can't send interrupts to this
+ * one. Only RaiseInterrupt() can set them, from inside this process.
+ */
+void
+SwitchToLocalInterrupts(void)
+{
+ SwitchMyPendingInterruptsPtr(&LocalPendingInterrupts);
}
/*
@@ -198,20 +263,40 @@ SwitchToLocalInterrupts(void)
void
SwitchToSharedInterrupts(void)
{
- if (MyPendingInterrupts == &MyProc->pendingInterrupts)
- return;
+ SwitchMyPendingInterruptsPtr(&MyProc->pendingInterrupts);
+}
+
+static bool
+SendOrRaiseInterrupt(PendingInterrupts *ptr, InterruptMask interruptMask)
+{
+ uint64 old_pending;
+ uint32 old_flags;
+
+ /* Set the given bits, and atomically read the old ones */
+#ifndef PG_HAVE_ATOMIC_U64_SIMULATION
+ old_pending = pg_atomic_fetch_or_u64(&ptr->interrupts, interruptMask);
+#else
+ old_pending = (uint64) pg_atomic_fetch_or_u32(&ptr->interrupts_lo, (uint32) interruptMask);
+ old_pending |= (uint64) pg_atomic_fetch_or_u32(&ptr->interrupts_hi,(uint32) (interruptMask >> 32)) << 32;
+#endif
- MyPendingInterrupts = &MyProc->pendingInterrupts;
+ if ((old_pending & interruptMask) == interruptMask)
+ return false; /* no new bits were set */
/*
- * Make sure that SIGALRM handlers that call RaiseInterrupt() are now
- * seeing the new MyPendingInterrupts destination.
+ * We set some bits. Set the CFI_ATTENTION flag too, so that the
+ * CHECK_FOR_INTERRUPTS() call knows to check for the interrupt.
*/
- pg_memory_barrier();
+ old_flags = pg_atomic_fetch_or_u32(&ptr->flags, PI_FLAG_CFI_ATTENTION);
- /* Mix in any unhandled bits from LocalPendingInterrupts. */
- pg_atomic_fetch_or_u64(MyPendingInterrupts,
- pg_atomic_exchange_u64(&LocalPendingInterrupts, 0));
+ /*
+ * Furthermore, if the process is currently blocked waiting for an
+ * interrupt to arrive, wake it up.
+ */
+ if ((old_flags & PI_FLAG_SLEEPING_ON_INTERRUPTS) != 0)
+ return true;
+ else
+ return false;
}
/*
@@ -223,15 +308,7 @@ SwitchToSharedInterrupts(void)
void
RaiseInterrupt(InterruptMask interruptMask)
{
- uint64 old_pending;
-
- old_pending = pg_atomic_fetch_or_u64(MyPendingInterrupts, interruptMask);
-
- /*
- * If the process is currently blocked waiting for an interrupt to arrive,
- * and the interrupt wasn't already pending, wake it up.
- */
- if ((old_pending & (interruptMask | SLEEPING_ON_INTERRUPTS)) == SLEEPING_ON_INTERRUPTS)
+ if (SendOrRaiseInterrupt(MyPendingInterrupts, interruptMask))
WakeupMyProc();
}
@@ -248,14 +325,12 @@ void
SendInterrupt(InterruptMask interruptMask, ProcNumber pgprocno)
{
PGPROC *proc;
- uint64 old_pending;
Assert(pgprocno != INVALID_PROC_NUMBER);
Assert(pgprocno >= 0);
Assert(pgprocno < ProcGlobal->allProcCount);
proc = &ProcGlobal->allProcs[pgprocno];
- old_pending = pg_atomic_fetch_or_u64(&proc->pendingInterrupts, interruptMask);
elog(DEBUG1, "sending interrupt 0x016%" PRIx64 " to pid %d", interruptMask, proc->pid);
@@ -263,7 +338,7 @@ SendInterrupt(InterruptMask interruptMask, ProcNumber pgprocno)
* If the process is currently blocked waiting for an interrupt to arrive,
* and the interrupt wasn't already pending, wake it up.
*/
- if ((old_pending & (interruptMask | SLEEPING_ON_INTERRUPTS)) == SLEEPING_ON_INTERRUPTS)
+ if (SendOrRaiseInterrupt(&proc->pendingInterrupts, interruptMask))
WakeupOtherProc(proc);
}
diff --git a/src/backend/storage/ipc/waiteventset.c b/src/backend/storage/ipc/waiteventset.c
index 612de8b35e9..447caa87962 100644
--- a/src/backend/storage/ipc/waiteventset.c
+++ b/src/backend/storage/ipc/waiteventset.c
@@ -1072,6 +1072,15 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
pgwin32_dispatch_queued_signals();
#endif
+ /*
+ * There are no CHECK_FOR_INTERRUPTS() calls below, but hold interrupts so
+ * that if a signal is received, the signal handler doesn't try to change
+ * CheckForInterruptsMask. That trips the assertion in
+ * RECHECK_CFI_ATTENTION() that it isn't caled while the
+ * SLEEPING_ON_INTERRUPTS flag is set.
+ */
+ HOLD_INTERRUPTS();
+
/*
* Atomically check if the interrupt is already pending and advertise that
* we are about to start sleeping. If it was already pending, avoid
@@ -1084,26 +1093,24 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
*/
if (set->interrupt_mask != 0)
{
- InterruptMask old_mask;
bool already_pending = false;
/*
* Perform a plain atomic read first as a fast path for the case that
* an interrupt is already pending.
*/
- old_mask = pg_atomic_read_u64(MyPendingInterrupts);
- already_pending = ((old_mask & set->interrupt_mask) != 0);
+ already_pending = InterruptPending(set->interrupt_mask);
if (!already_pending)
{
/*
- * Atomically set the SLEEPING_ON_INTERRUPTS bit and re-check if
- * an interrupt is already pending. The atomic op provides
- * synchronization so that if an interrupt bit is set after this,
- * the setter will wake us up.
+ * Set the SLEEPING_ON_INTERRUPTS bit and re-check if an interrupt
+ * is already pending. The atomic op provides synchronization so
+ * that if an interrupt bit is set after setting the bit, the
+ * setter will wake us up.
*/
- old_mask = pg_atomic_fetch_or_u64(MyPendingInterrupts, SLEEPING_ON_INTERRUPTS);
- already_pending = ((old_mask & set->interrupt_mask) != 0);
+ (void) pg_atomic_fetch_or_u32(&MyPendingInterrupts->flags, PI_FLAG_SLEEPING_ON_INTERRUPTS);
+ already_pending = InterruptPending(set->interrupt_mask);
/* remember to clear the SLEEPING_ON_INTERRUPTS flag later */
sleeping_flag_armed = true;
@@ -1177,7 +1184,9 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
/* If we set the SLEEPING_ON_INTERRUPTS flag, reset it again */
if (sleeping_flag_armed)
- pg_atomic_fetch_and_u64(MyPendingInterrupts, ~((uint32) SLEEPING_ON_INTERRUPTS));
+ pg_atomic_fetch_and_u32(&MyPendingInterrupts->flags, ~PI_FLAG_SLEEPING_ON_INTERRUPTS);
+
+ RESUME_INTERRUPTS();
#ifndef WIN32
waiting = false;
@@ -1255,7 +1264,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
/* Drain the signalfd. */
drain();
- if (set->interrupt_mask != 0 && (pg_atomic_read_u64(MyPendingInterrupts) & set->interrupt_mask) != 0)
+ if (set->interrupt_mask != 0 && InterruptPending(set->interrupt_mask))
{
occurred_events->fd = PGINVALID_SOCKET;
occurred_events->events = WL_INTERRUPT;
@@ -1414,7 +1423,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
if (cur_event->events == WL_INTERRUPT &&
cur_kqueue_event->filter == EVFILT_SIGNAL)
{
- if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
+ if (set->interrupt_mask != 0 && InterruptPending(set->interrupt_mask))
{
occurred_events->fd = PGINVALID_SOCKET;
occurred_events->events = WL_INTERRUPT;
@@ -1539,7 +1548,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
/* There's data in the self-pipe, clear it. */
drain();
- if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
+ if (set->interrupt_mask != 0 && InterruptPending(set->interrupt_mask))
{
occurred_events->fd = PGINVALID_SOCKET;
occurred_events->events = WL_INTERRUPT;
@@ -1760,7 +1769,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
if (!ResetEvent(set->handles[cur_event->pos + 1]))
elog(ERROR, "ResetEvent failed: error code %lu", GetLastError());
- if (set->interrupt_mask != 0 && (pg_atomic_read_u32(MyPendingInterrupts) & set->interrupt_mask) != 0)
+ if (set->interrupt_mask != 0 && InterruptPending(set->interrupt_mask))
{
occurred_events->fd = PGINVALID_SOCKET;
occurred_events->events = WL_INTERRUPT;
diff --git a/src/include/ipc/interrupt.h b/src/include/ipc/interrupt.h
index 239d58f7597..6118aedb025 100644
--- a/src/include/ipc/interrupt.h
+++ b/src/include/ipc/interrupt.h
@@ -23,14 +23,63 @@
* needed which are needed by all callers of WaitInterrupt, so include it
* here.
*
- * Note: InterruptMask is defind in waiteventset.h to avoid circular dependency
+ * Note: InterruptMask is defined in waiteventset.h to avoid circular dependency
*/
#include "storage/waiteventset.h"
+/*
+ * PendingInterrupts is a bitmask representing interrupts that are currently
+ * pending for a process, and some flags to help with signaling when the
+ * interrupt bits are altered.
+ *
+ * We support up to 64 different interrupts. That way, the currently pending
+ * interrupts can be conveniently stored as on 64-bit atomic bitmask, on
+ * systems with 64-bit atomics. On other systems, it's split into two 32-bit
+ * atomic fields. That's good enough because we don't rely on atomicity
+ * between different interrupt bits. (Note that the simulated 64-bit atomics
+ * are not good enough for this, because the simulation relies on spinlocks,
+ * which creates a deadlock risk when used from signal handlers.)
+ *
+ * Two signaling flags are defined:
+ *
+ * PI_FLAG_SLEEPING_ON_INTERRUPTS indicates that the backend is currently
+ * blocked waiting for an interrupt. If set, the backend needs to be woken up
+ * when a bit in the 'interrupts' mask is set.
+ *
+ * PI_FLAG_CFI_ATTENTION indicates that some interrupt bits have been changed
+ * since the last ProcessInterupts() call. It's set whenever any 'interrupts'
+ * bit is set. It's checked by CHECK_FOR_INTERRUPTS() as a fastpath check to
+ * determine if there can be any interrupts pending that need processing, and
+ * cleared in ProcessInterrupts(). It must also be cleared / re-evaluated
+ * whenever CheckForInterruptsMask changes (see RECHECK_CFI_ATTENTION()).
+ */
+typedef struct
+{
+ pg_atomic_uint32 flags;
+
+#ifndef PG_HAVE_ATOMIC_U64_SIMULATION
+ pg_atomic_uint64 interrupts;
+#else
+ pg_atomic_uint32 interrupts_lo;
+ pg_atomic_uint32 interrupts_hi;
+#endif
+} PendingInterrupts;
+
+#define PI_FLAG_SLEEPING_ON_INTERRUPTS 0x01
+#define PI_FLAG_CFI_ATTENTION 0x02
/*
- * Flags in the pending interrupts bitmask. Each value is a different bit, so that
- * these can be conveniently OR'd together.
+ * Interrupt vector currently in use for this process. Most of the time this
+ * points to MyProc->pendingInterrupts, but in processes that have no PGPROC
+ * entry (yet), it points to a process-private variable, so that interrupts
+ * can nevertheless be used from signal handlers in the same process.
+ */
+extern PGDLLIMPORT PendingInterrupts *MyPendingInterrupts;
+
+
+/*
+ * Interrupt bits that used in PendingIntrrupts->interrupts bitmask. Each
+ * value is a different bit, so that these can be conveniently OR'd together.
*/
#define UINT64_BIT(shift) (UINT64_C(1) << (shift))
@@ -191,18 +240,7 @@
* extensions
***********************************************************************/
#define BEGIN_ADDIN_INTERRUPTS 25
-#define END_ADDIN_INTERRUPTS 63
-
-/*
- * SLEEPING_ON_INTERRUPTS indicates that the backend is currently blocked
- * waiting for an interrupt. If 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.
- */
-#define SLEEPING_ON_INTERRUPTS UINT64_BIT(63)
-
-extern PGDLLIMPORT pg_atomic_uint64 *MyPendingInterrupts;
+#define END_ADDIN_INTERRUPTS 64
/*
* Test an interrupt flag (or flags).
@@ -214,12 +252,25 @@ InterruptPending(InterruptMask interruptMask)
* Note that there is no memory barrier here. This is used in
* CHECK_FOR_INTERRUPTS(), so we want this to be as cheap as possible.
*
+ * FIXME: that's not true anymore, this is no longer called from
+ * CHECK_FOR_INTERRUPTS(). Still seems correct and a good choice to not
+ * force a barrier here. Just update the comment?
+ *
* That means that if the interrupt is concurrently set by another
* process, we might miss it. That should be OK, because the next
* WaitInterrupt() or equivalent call acts as a synchronization barrier.
* We will see the updated value before sleeping.
*/
- return (pg_atomic_read_u64(MyPendingInterrupts) & interruptMask) != 0;
+ uint64 pending;
+
+#ifndef PG_HAVE_ATOMIC_U64_SIMULATION
+ pending = pg_atomic_read_u64(&MyPendingInterrupts->interrupts);
+#else
+ pending = (uint64) pg_atomic_read_u32(&MyPendingInterrupts->interrupts_lo);
+ pending |= (uint64) pg_atomic_read_u32(&MyPendingInterrupts->interrupts_hi) << 32;
+#endif
+
+ return (pending & interruptMask) != 0;
}
/*
@@ -228,7 +279,14 @@ InterruptPending(InterruptMask interruptMask)
static inline void
ClearInterrupt(InterruptMask interruptMask)
{
- pg_atomic_fetch_and_u64(MyPendingInterrupts, ~interruptMask);
+ uint64 mask = ~interruptMask;
+
+#ifndef PG_HAVE_ATOMIC_U64_SIMULATION
+ (void) pg_atomic_fetch_and_u64(&MyPendingInterrupts->interrupts, mask);
+#else
+ (void) pg_atomic_fetch_and_u32(&MyPendingInterrupts->interrupts_lo, (uint32) mask);
+ (void) pg_atomic_fetch_and_u32(&MyPendingInterrupts->interrupts_hi, (uint32) (mask >> 32));
+#endif
}
/*
@@ -310,13 +368,47 @@ extern void ProcessInterrupts(void);
#define INTERRUPTS_CAN_BE_PROCESSED(mask) \
(((mask) & CheckForInterruptsMask) == (mask))
-/* Service interrupt, if one is pending and it's safe to service it now */
+/*
+ * Service an interrupt, if one is pending and it's safe to service it now.
+ *
+ * NB: This is called from all over the codebase, and in fairly tight loops,
+ * so this needs to be very short and fast when there is no work to do!
+ */
#define CHECK_FOR_INTERRUPTS() \
do { \
- if (INTERRUPTS_PENDING_CONDITION(CheckForInterruptsMask)) \
+ if (unlikely(pg_atomic_read_u32(&MyPendingInterrupts->flags) != 0)) \
ProcessInterrupts(); \
} while(0)
+/*
+ * Set/clear the PI_FLAG_CFI_ATTENTION according to the currently pending
+ * interrupts and CheckForInterruptsMask. This should be called every time
+ * after enabling new bits in CheckForInterruptsMask, so that the next
+ * CHECK_FOR_INTERRUPTS() will react correctly to the newly enabled
+ * interrupts.
+ */
+static inline void
+RECHECK_CFI_ATTENTION(void)
+{
+ /*
+ * This should not be called while sleeping. No other process sets the
+ * flag, so when we clear/set 'flags' below, we don't need to worry about
+ * overwriting it.
+ */
+ Assert((pg_atomic_read_u32(&MyPendingInterrupts->flags) & PI_FLAG_SLEEPING_ON_INTERRUPTS) == 0);
+
+ /* clear the flag first */
+ (void) pg_atomic_write_u32(&MyPendingInterrupts->flags, 0);
+
+ /*
+ * Ensure that if a concurrent process sets an interrupt from now on, the
+ * flag will be set again.
+ */
+ pg_memory_barrier();
+
+ if (InterruptPending(CheckForInterruptsMask))
+ (void) pg_atomic_write_u32(&MyPendingInterrupts->flags, PI_FLAG_CFI_ATTENTION);
+}
/*****************************************************************************
* Critical section and interrupt holdoff mechanism
@@ -344,7 +436,10 @@ RESUME_INTERRUPTS(void)
Assert(InterruptHoldoffCount > 0);
InterruptHoldoffCount--;
if (InterruptHoldoffCount == 0 && CritSectionCount == 0)
+ {
CheckForInterruptsMask = EnabledInterruptsMask;
+ RECHECK_CFI_ATTENTION();
+ }
else
Assert(CheckForInterruptsMask == 0);
}
@@ -362,7 +457,10 @@ END_CRIT_SECTION(void)
Assert(CritSectionCount > 0);
CritSectionCount--;
if (InterruptHoldoffCount == 0 && CritSectionCount == 0)
+ {
CheckForInterruptsMask = EnabledInterruptsMask;
+ RECHECK_CFI_ATTENTION();
+ }
else
Assert(CheckForInterruptsMask == 0);
}
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 00ea9f86422..fab892f8b3c 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -325,7 +325,7 @@ struct PGPROC
dlist_node lockGroupLink; /* my member link, if I'm a member */
/* Bit mask of pending interrupts, waiting to be processed */
- pg_atomic_uint64 pendingInterrupts;
+ PendingInterrupts pendingInterrupts;
/*
* While in hot standby mode, shows that a conflict signal has been sent
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9a5d8ac10e9..bfb8a001d81 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2184,6 +2184,7 @@ PatternInfoArray
Pattern_Prefix_Status
Pattern_Type
PendingFsyncEntry
+PendingInterrupts
PendingListenAction
PendingListenEntry
PendingRelDelete
--
2.47.3