v4-0002-Don-t-let-latches-hide-other-WaitEventSet-events.patch
text/x-patch
Filename: v4-0002-Don-t-let-latches-hide-other-WaitEventSet-events.patch
Type: text/x-patch
Part: 1
Patch
Format: format-patch
Series: patch v4-0002
Subject: Don't let latches hide other WaitEventSet events.
| File | + | − |
|---|---|---|
| src/backend/storage/ipc/latch.c | 30 | 13 |
From 4edb07dbe87911de2ef0c30e57b766fcad022dff Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 13 Dec 2021 14:31:18 +1300
Subject: [PATCH v4 2/3] Don't let latches hide other WaitEventSet events.
Previously, if WaitEventSet() saw that a latch had been set in memory,
it would return an event immediately, hiding any other events that might
also be pending in the kernel.
With this change, latches still have higher priority than other events,
but if you ask for 3 events, you'll get up to 3 events that are
currently pending, even if the latch is set. This makes no difference
to any existing caller, because they all wait for just one event, so
they never reach the slow enter-the-kernel case.
Discussion: https://postgr.es/m/77def86b27e41f0efcba411460e929ae%40postgrespro.ru
---
src/backend/storage/ipc/latch.c | 43 +++++++++++++++++++++++----------
1 file changed, 30 insertions(+), 13 deletions(-)
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 54e928c564..30f7160c61 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -1354,9 +1354,9 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
int rc;
/*
- * Check if the latch is set already. If so, leave the loop
- * immediately, avoid blocking again. We don't attempt to report any
- * other events that might also be satisfied.
+ * Check if the latch is set already first. We don't have to enter the
+ * kernel if it's already set, unless the caller wants more than one
+ * event.
*
* If someone sets the latch between this and the
* WaitEventSetWaitBlock() below, the setter will write a byte to the
@@ -1401,7 +1401,26 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
/* could have been set above */
set->latch->maybe_sleeping = false;
- break;
+ if (returned_events == nevents)
+ {
+ /*
+ * Already fully satisfied by this latch event, so there's no
+ * need to enter the kernel.
+ */
+ break;
+ }
+ else
+ {
+ /*
+ * The caller wants to poll other events too. Set timeout to
+ * zero, because we already have an event to report to the
+ * caller. WaitEventSetWaitBlock() won't double-report the
+ * latch, because we cleared latch->maybe_sleeping.
+ */
+ Assert(returned_events == 1);
+ Assert(nevents > 1);
+ cur_timeout = 0;
+ }
}
/*
@@ -1410,18 +1429,16 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
* to retry, everything >= 1 is the number of returned events.
*/
rc = WaitEventSetWaitBlock(set, cur_timeout,
- occurred_events, nevents);
+ occurred_events,
+ nevents - returned_events);
if (set->latch)
- {
- Assert(set->latch->maybe_sleeping);
set->latch->maybe_sleeping = false;
- }
if (rc == -1)
break; /* timeout occurred */
else
- returned_events = rc;
+ returned_events += rc;
/* If we're not done, update cur_timeout for next iteration */
if (returned_events == 0 && timeout >= 0)
@@ -1509,7 +1526,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
/* Drain the signalfd. */
drain();
- if (set->latch && set->latch->is_set)
+ if (set->latch && set->latch->maybe_sleeping && set->latch->is_set)
{
occurred_events->fd = PGINVALID_SOCKET;
occurred_events->events = WL_LATCH_SET;
@@ -1667,7 +1684,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
if (cur_event->events == WL_LATCH_SET &&
cur_kqueue_event->filter == EVFILT_SIGNAL)
{
- if (set->latch && set->latch->is_set)
+ if (set->latch && set->latch->maybe_sleeping && set->latch->is_set)
{
occurred_events->fd = PGINVALID_SOCKET;
occurred_events->events = WL_LATCH_SET;
@@ -1792,7 +1809,7 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
/* There's data in the self-pipe, clear it. */
drain();
- if (set->latch && set->latch->is_set)
+ if (set->latch && set->latch->maybe_sleeping && set->latch->is_set)
{
occurred_events->fd = PGINVALID_SOCKET;
occurred_events->events = WL_LATCH_SET;
@@ -1974,7 +1991,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->latch && set->latch->is_set)
+ if (set->latch && set->latch->maybe_sleeping && set->latch->is_set)
{
occurred_events->fd = PGINVALID_SOCKET;
occurred_events->events = WL_LATCH_SET;
--
2.30.2