From 4edb07dbe87911de2ef0c30e57b766fcad022dff Mon Sep 17 00:00:00 2001 From: Thomas Munro 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