From 57306a6a2a4e7300206d3a5e40a1787b427765b8 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Thu, 27 Oct 2022 10:18:13 -0700 Subject: [PATCH v1 01/12] wip: lwlock: fix quadratic behaviour with very long wait lists Author: Reviewed-by: Discussion: https://postgr.es/m/ Backpatch: --- src/include/storage/proc.h | 3 ++- src/backend/storage/lmgr/lwlock.c | 29 +++++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 8d096fdeeb1..9a2615666a1 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -217,7 +217,8 @@ struct PGPROC bool recoveryConflictPending; /* Info about LWLock the process is currently waiting for, if any. */ - bool lwWaiting; /* true if waiting for an LW lock */ + int lwWaiting; /* 0 if not waiting, 1 if on waitlist, 2 if + * waiting to be woken */ uint8 lwWaitMode; /* lwlock mode being waited for */ proclist_node lwWaitLink; /* position in LW lock wait list */ diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index d274c9b1dc9..2d1c8fae06e 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -987,6 +987,9 @@ LWLockWakeup(LWLock *lock) wokeup_somebody = true; } + /* signal that the process isn't on the wait list anymore */ + waiter->lwWaiting = 2; + /* * Once we've woken up an exclusive lock, there's no point in waking * up anybody else. @@ -1044,7 +1047,7 @@ LWLockWakeup(LWLock *lock) * another lock. */ pg_write_barrier(); - waiter->lwWaiting = false; + waiter->lwWaiting = 0; PGSemaphoreUnlock(waiter->sem); } } @@ -1073,7 +1076,7 @@ LWLockQueueSelf(LWLock *lock, LWLockMode mode) /* setting the flag is protected by the spinlock */ pg_atomic_fetch_or_u32(&lock->state, LW_FLAG_HAS_WAITERS); - MyProc->lwWaiting = true; + MyProc->lwWaiting = 1; MyProc->lwWaitMode = mode; /* LW_WAIT_UNTIL_FREE waiters are always at the front of the queue */ @@ -1101,7 +1104,6 @@ static void LWLockDequeueSelf(LWLock *lock) { bool found = false; - proclist_mutable_iter iter; #ifdef LWLOCK_STATS lwlock_stats *lwstats; @@ -1114,17 +1116,14 @@ LWLockDequeueSelf(LWLock *lock) LWLockWaitListLock(lock); /* - * Can't just remove ourselves from the list, but we need to iterate over - * all entries as somebody else could have dequeued us. + * Remove ourselves from the waitlist, unless we've already been + * removed. The removal happens with the wait list lock held, so there's + * no race in this check. */ - proclist_foreach_modify(iter, &lock->waiters, lwWaitLink) + if (MyProc->lwWaiting == 1) { - if (iter.cur == MyProc->pgprocno) - { - found = true; - proclist_delete(&lock->waiters, iter.cur, lwWaitLink); - break; - } + proclist_delete(&lock->waiters, MyProc->pgprocno, lwWaitLink); + found = true; } if (proclist_is_empty(&lock->waiters) && @@ -1138,7 +1137,7 @@ LWLockDequeueSelf(LWLock *lock) /* clear waiting state again, nice for debugging */ if (found) - MyProc->lwWaiting = false; + MyProc->lwWaiting = 0; else { int extraWaits = 0; @@ -1772,6 +1771,8 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val) proclist_delete(&lock->waiters, iter.cur, lwWaitLink); proclist_push_tail(&wakeup, iter.cur, lwWaitLink); + + waiter->lwWaiting = 2; } /* We are done updating shared state of the lock itself. */ @@ -1787,7 +1788,7 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val) proclist_delete(&wakeup, iter.cur, lwWaitLink); /* check comment in LWLockWakeup() about this barrier */ pg_write_barrier(); - waiter->lwWaiting = false; + waiter->lwWaiting = 0; PGSemaphoreUnlock(waiter->sem); } } -- 2.38.0