Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 66d86d4

Browse files
committed
Document more assumptions of LWLock variable changes with WAL inserts
This commit adds a few comments about what LWLockWaitForVar() relies on when a backend waits for a variable update on its LWLocks for WAL insertions up to an expected LSN. First, LWLockWaitForVar() does not include a memory barrier, relying on a spinlock taken at the beginning of WaitXLogInsertionsToFinish(). This was hidden behind two layers of routines in lwlock.c. This assumption is now documented at the top of LWLockWaitForVar(), and detailed at bit more within LWLockConflictsWithVar(). Second, document why WaitXLogInsertionsToFinish() does not include memory barriers, relying on a spinlock at its top, which is, per Andres' input, fine for two different reasons, both depending on the fact that the caller of WaitXLogInsertionsToFinish() is waiting for a LSN up to a certain value. This area's documentation and assumptions could be improved more in the future, but at least that's a beginning. Author: Bharath Rupireddy, Andres Freund Reviewed-by: Michael Paquier Discussion: https://postgr.es/m/CALj2ACVF+6jLvqKe6xhDzCCkr=rfd6upaGc3477Pji1Ke9G7Bg@mail.gmail.com
1 parent 62e9af4 commit 66d86d4

File tree

2 files changed

+19
-3
lines changed

2 files changed

+19
-3
lines changed

src/backend/access/transam/xlog.c

+11
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,17 @@ WaitXLogInsertionsToFinish(XLogRecPtr upto)
14951495
* calling LWLockUpdateVar. But if it has to sleep, it will
14961496
* advertise the insertion point with LWLockUpdateVar before
14971497
* sleeping.
1498+
*
1499+
* In this loop we are only waiting for insertions that started
1500+
* before WaitXLogInsertionsToFinish was called. The lack of
1501+
* memory barriers in the loop means that we might see locks as
1502+
* "unused" that have since become used. This is fine because
1503+
* they only can be used for later insertions that we would not
1504+
* want to wait on anyway. Not taking a lock to acquire the
1505+
* current insertingAt value means that we might see older
1506+
* insertingAt values. This is also fine, because if we read a
1507+
* value too old, we will add ourselves to the wait queue, which
1508+
* contains atomic operations.
14981509
*/
14991510
if (LWLockWaitForVar(&WALInsertLocks[i].l.lock,
15001511
&WALInsertLocks[i].l.insertingAt,

src/backend/storage/lmgr/lwlock.c

+8-3
Original file line numberDiff line numberDiff line change
@@ -1556,9 +1556,10 @@ LWLockConflictsWithVar(LWLock *lock, pg_atomic_uint64 *valptr, uint64 oldval,
15561556
/*
15571557
* Test first to see if it the slot is free right now.
15581558
*
1559-
* XXX: the caller uses a spinlock before this, so we don't need a memory
1560-
* barrier here as far as the current usage is concerned. But that might
1561-
* not be safe in general.
1559+
* XXX: the unique caller of this routine, WaitXLogInsertionsToFinish()
1560+
* via LWLockWaitForVar(), uses an implied barrier with a spinlock before
1561+
* this, so we don't need a memory barrier here as far as the current
1562+
* usage is concerned. But that might not be safe in general.
15621563
*/
15631564
mustwait = (pg_atomic_read_u32(&lock->state) & LW_VAL_EXCLUSIVE) != 0;
15641565

@@ -1601,6 +1602,10 @@ LWLockConflictsWithVar(LWLock *lock, pg_atomic_uint64 *valptr, uint64 oldval,
16011602
*
16021603
* Note: this function ignores shared lock holders; if the lock is held
16031604
* in shared mode, returns 'true'.
1605+
*
1606+
* Be aware that LWLockConflictsWithVar() does not include a memory barrier,
1607+
* hence the caller of this function may want to rely on an explicit barrier or
1608+
* an implied barrier via spinlock or LWLock to avoid memory ordering issues.
16041609
*/
16051610
bool
16061611
LWLockWaitForVar(LWLock *lock, pg_atomic_uint64 *valptr, uint64 oldval,

0 commit comments

Comments
 (0)