mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Avoid locking when an LSN waiter is already removed
WaitLSNWakeup() removes each selected waiter from its heap and clears its inHeap flag before setting its latch. When such a waiter later calls deleteLSNWaiter(), it acquires WaitLSNLock exclusively only to discover that there is nothing left to remove. Waking many waiters can therefore make them serialize on the lock for no useful work. Check inHeap before acquiring WaitLSNLock. A lockless false value is conclusive because only the owning backend can change inHeap from false to true. A concurrent waker can only clear it. A stale true value falls through to the existing recheck under the lock. WaitLSNCleanup() performed the same lockless check before calling deleteLSNWaiter(). Drop it there, as it is now redundant. Author: Xuneng Zhou <xunengzhou@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com> Discussion: https://postgr.es/m/CABPTF7UtW_cAa%3DQh4RDfKiUqu3pJJE22ai9tbWJVERbeRyssLw%40mail.gmail.com Backpatch-through: 19
This commit is contained in:
@@ -252,6 +252,15 @@ deleteLSNWaiter(WaitLSNType lsnType)
|
||||
|
||||
Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);
|
||||
|
||||
/*
|
||||
* Avoid taking WaitLSNLock if a waker has already removed us. Only this
|
||||
* backend can set inHeap; other processes can only clear it. Therefore
|
||||
* false is conclusive, while a stale true is harmless because it is
|
||||
* rechecked under WaitLSNLock below.
|
||||
*/
|
||||
if (!procInfo->inHeap)
|
||||
return;
|
||||
|
||||
LWLockAcquire(WaitLSNLock, LW_EXCLUSIVE);
|
||||
|
||||
Assert(procInfo->lsnType == lsnType);
|
||||
@@ -370,17 +379,15 @@ WaitLSNWakeup(WaitLSNType lsnType, XLogRecPtr currentLSN)
|
||||
void
|
||||
WaitLSNCleanup(void)
|
||||
{
|
||||
/*
|
||||
* deleteLSNWaiter() starts with the same lockless inHeap check, so
|
||||
* calling it unconditionally costs nothing when this process isn't
|
||||
* waiting. Its lsnType is then unused, and reading it is harmless in any
|
||||
* case: an entry that was never used is zeroed, which is a valid
|
||||
* WaitLSNType.
|
||||
*/
|
||||
if (waitLSNState)
|
||||
{
|
||||
/*
|
||||
* We do a fast-path check of the inHeap flag without the lock. This
|
||||
* flag is set to true only by the process itself. So, it's only
|
||||
* possible to get a false positive. But that will be eliminated by a
|
||||
* recheck inside deleteLSNWaiter().
|
||||
*/
|
||||
if (waitLSNState->procInfos[MyProcNumber].inHeap)
|
||||
deleteLSNWaiter(waitLSNState->procInfos[MyProcNumber].lsnType);
|
||||
}
|
||||
deleteLSNWaiter(waitLSNState->procInfos[MyProcNumber].lsnType);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user