fix-lwlock-queue.diff
text/x-diff
Filename: fix-lwlock-queue.diff
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/storage/lmgr/lwlock.c | 12 | 13 |
| src/include/storage/proc.h | 2 | 1 |
diff --git i/src/include/storage/proc.h w/src/include/storage/proc.h
index 8d096fdeeb1..9a2615666a1 100644
--- i/src/include/storage/proc.h
+++ w/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 i/src/backend/storage/lmgr/lwlock.c w/src/backend/storage/lmgr/lwlock.c
index d274c9b1dc9..ffb852c91d7 100644
--- i/src/backend/storage/lmgr/lwlock.c
+++ w/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;