From 89b183622d4efbacce8027e8dd29336a7d3182b1 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Mon, 3 Mar 2025 17:11:06 +0200
Subject: [PATCH 1/1] Handle interrupts while waiting on Append's async
 subplans

We did not wake up on interrupts while waiting on async events on an
async-capable append node. For example, if you tried to cancel the
query, nothing would happen until one of the async subplans becomes
readable. To fix, add WL_LATCH_SET to the WaitEventSet.
---
 src/backend/executor/nodeAppend.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index ca0f54d676f..a03a23c18e4 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -326,6 +326,8 @@ ExecAppend(PlanState *pstate)
 	{
 		PlanState  *subnode;
 
+		ResetLatch(MyLatch);
+
 		CHECK_FOR_INTERRUPTS();
 
 		/*
@@ -1016,7 +1018,7 @@ ExecAppendAsyncRequest(AppendState *node, TupleTableSlot **result)
 static void
 ExecAppendAsyncEventWait(AppendState *node)
 {
-	int			nevents = node->as_nasyncplans + 1;
+	int			nevents = node->as_nasyncplans + 2;
 	long		timeout = node->as_syncdone ? -1 : 0;
 	WaitEvent	occurred_event[EVENT_BUFFER_SIZE];
 	int			noccurred;
@@ -1041,8 +1043,7 @@ ExecAppendAsyncEventWait(AppendState *node)
 	}
 
 	/*
-	 * No need for further processing if there are no configured events other
-	 * than the postmaster death event.
+	 * No need for further processing if none of the subplans configured any events.
 	 */
 	if (GetNumRegisteredWaitEvents(node->as_eventset) == 1)
 	{
@@ -1051,6 +1052,21 @@ ExecAppendAsyncEventWait(AppendState *node)
 		return;
 	}
 
+	/*
+	 * Add the latch to the set, so that we wake up to process the standard
+	 * interrupts with CHECK_FOR_INTERRUPTS().
+	 *
+	 * NOTE: For historical reasons, it's important that this is added to the
+	 * WaitEventSet after the ExecAsyncConfigureWait() calls.  Namely,
+	 * postgres_fdw calls "GetNumRegisteredWaitEvents(set) == 1" to check if
+	 * any other events are in the set.  That's a bad design, it's
+	 * questionable for postgres_fdw to be doing that in the first place, but
+	 * we cannot change it now.  The pattern has possibly been copied to other
+	 * extensions too.
+	 */
+	AddWaitEventToSet(node->as_eventset, WL_LATCH_SET, PGINVALID_SOCKET,
+					  MyLatch, NULL);
+
 	/* Return at most EVENT_BUFFER_SIZE events in one call. */
 	if (nevents > EVENT_BUFFER_SIZE)
 		nevents = EVENT_BUFFER_SIZE;
-- 
2.39.5

