Skip to content

Commit 5673289

Browse files
committed
Refactor CHECK_FOR_INTERRUPTS() to add flexibility.
Split up CHECK_FOR_INTERRUPTS() to provide an additional macro INTERRUPTS_PENDING_CONDITION(), which just tests whether an interrupt is pending without attempting to service it. This is useful in situations where the caller knows that interrupts are blocked, and would like to find out if it's worth the trouble to unblock them. Also add INTERRUPTS_CAN_BE_PROCESSED(), which indicates whether CHECK_FOR_INTERRUPTS() can be relied on to clear the pending interrupt. This commit doesn't actually add any uses of the new macros, but a follow-on bug fix will do so. Back-patch to all supported branches to provide infrastructure for that fix. Alvaro Herrera and Tom Lane Discussion: https://postgr.es/m/20210513155351.GA7848@alvherre.pgsql
1 parent 836dda6 commit 5673289

File tree

2 files changed

+36
-14
lines changed

2 files changed

+36
-14
lines changed

src/backend/tcop/postgres.c

+13-3
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ ProcessClientWriteInterrupt(bool blocked)
577577
{
578578
/*
579579
* Don't mess with whereToSendOutput if ProcessInterrupts wouldn't
580-
* do anything.
580+
* service ProcDiePending.
581581
*/
582582
if (InterruptHoldoffCount == 0 && CritSectionCount == 0)
583583
{
@@ -2865,6 +2865,12 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
28652865
* If an interrupt condition is pending, and it's safe to service it,
28662866
* then clear the flag and accept the interrupt. Called only when
28672867
* InterruptPending is true.
2868+
*
2869+
* Note: if INTERRUPTS_CAN_BE_PROCESSED() is true, then ProcessInterrupts
2870+
* is guaranteed to clear the InterruptPending flag before returning.
2871+
* (This is not the same as guaranteeing that it's still clear when we
2872+
* return; another interrupt could have arrived. But we promise that
2873+
* any pre-existing one will have been serviced.)
28682874
*/
28692875
void
28702876
ProcessInterrupts(void)
@@ -2953,8 +2959,12 @@ ProcessInterrupts(void)
29532959
if (QueryCancelPending && QueryCancelHoldoffCount != 0)
29542960
{
29552961
/*
2956-
* Re-arm InterruptPending so that we process the cancel request
2957-
* as soon as we're done reading the message.
2962+
* Re-arm InterruptPending so that we process the cancel request as
2963+
* soon as we're done reading the message. (XXX this is seriously
2964+
* ugly: it complicates INTERRUPTS_CAN_BE_PROCESSED(), and it means we
2965+
* can't use that macro directly as the initial test in this function,
2966+
* meaning that this code also creates opportunities for other bugs to
2967+
* appear.)
29582968
*/
29592969
InterruptPending = true;
29602970
}

src/include/miscadmin.h

+23-11
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@
5858
* allowing die interrupts: HOLD_CANCEL_INTERRUPTS() and
5959
* RESUME_CANCEL_INTERRUPTS().
6060
*
61+
* Note that ProcessInterrupts() has also acquired a number of tasks that
62+
* do not necessarily cause a query-cancel-or-die response. Hence, it's
63+
* possible that it will just clear InterruptPending and return.
64+
*
65+
* INTERRUPTS_PENDING_CONDITION() can be checked to see whether an
66+
* interrupt needs to be serviced, without trying to do so immediately.
67+
* Some callers are also interested in INTERRUPTS_CAN_BE_PROCESSED(),
68+
* which tells whether ProcessInterrupts is sure to clear the interrupt.
69+
*
6170
* Special mechanisms are used to let an interrupt be accepted when we are
6271
* waiting for a lock or when we are waiting for command input (but, of
6372
* course, only if the interrupt holdoff counter is zero). See the
@@ -95,24 +104,27 @@ extern PGDLLIMPORT volatile uint32 CritSectionCount;
95104
/* in tcop/postgres.c */
96105
extern void ProcessInterrupts(void);
97106

107+
/* Test whether an interrupt is pending */
98108
#ifndef WIN32
109+
#define INTERRUPTS_PENDING_CONDITION() \
110+
(InterruptPending)
111+
#else
112+
#define INTERRUPTS_PENDING_CONDITION() \
113+
(UNBLOCKED_SIGNAL_QUEUE() ? pgwin32_dispatch_queued_signals() : 0, \
114+
InterruptPending)
115+
#endif
99116

117+
/* Service interrupt, if one is pending and it's safe to service it now */
100118
#define CHECK_FOR_INTERRUPTS() \
101119
do { \
102-
if (InterruptPending) \
103-
ProcessInterrupts(); \
104-
} while(0)
105-
#else /* WIN32 */
106-
107-
#define CHECK_FOR_INTERRUPTS() \
108-
do { \
109-
if (UNBLOCKED_SIGNAL_QUEUE()) \
110-
pgwin32_dispatch_queued_signals(); \
111-
if (InterruptPending) \
120+
if (INTERRUPTS_PENDING_CONDITION()) \
112121
ProcessInterrupts(); \
113122
} while(0)
114-
#endif /* WIN32 */
115123

124+
/* Is ProcessInterrupts() guaranteed to clear InterruptPending? */
125+
#define INTERRUPTS_CAN_BE_PROCESSED() \
126+
(InterruptHoldoffCount == 0 && CritSectionCount == 0 && \
127+
QueryCancelHoldoffCount == 0)
116128

117129
#define HOLD_INTERRUPTS() (InterruptHoldoffCount++)
118130

0 commit comments

Comments
 (0)