From 7f263a752faf5017cceb98286c248dbb395b281c Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Tue, 13 Jan 2026 20:10:32 -0500 Subject: [PATCH v11 7/7] WIP: Make UnlockReleaseBuffer() more efficient Now that the buffer content lock is implemented as part of BufferDesc.state, releasing the lock and unpinning the buffer can be implemented as a single atomic operation. Author: Reviewed-By: Discussion: https://postgr.es/m/ Backpatch: --- src/backend/access/nbtree/nbtpage.c | 22 +++++++++++- src/backend/storage/buffer/bufmgr.c | 52 ++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index 4125c185e8b..f3e3f67e1fd 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -1007,11 +1007,18 @@ _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access) Assert(BlockNumberIsValid(blkno)); if (BufferIsValid(obuf)) + { + _bt_relbuf(rel, obuf); +#if 0 + Assert(BufferGetBlockNumber(obuf) != blkno); _bt_unlockbuf(rel, obuf); - buf = ReleaseAndReadBuffer(obuf, rel, blkno); +#endif + } + buf = ReadBuffer(rel, blkno); _bt_lockbuf(rel, buf, access); _bt_checkpage(rel, buf); + return buf; } @@ -1023,8 +1030,21 @@ _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access) void _bt_relbuf(Relation rel, Buffer buf) { +#if 0 _bt_unlockbuf(rel, buf); ReleaseBuffer(buf); +#else + /* + * Buffer is pinned and locked, which means that it is expected to be + * defined and addressable. Check that proactively. + */ + VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), BLCKSZ); + + UnlockReleaseBuffer(buf); + + if (!RelationUsesLocalBuffers(rel)) + VALGRIND_MAKE_MEM_NOACCESS(BufferGetPage(buf), BLCKSZ); +#endif } /* diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 097c8fa67c5..a3a11595b5d 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -5506,13 +5506,63 @@ ReleaseBuffer(Buffer buffer) /* * UnlockReleaseBuffer -- release the content lock and pin on a buffer * - * This is just a shorthand for a common combination. + * This is just a, more efficient, shorthand for a common combination. */ void UnlockReleaseBuffer(Buffer buffer) { +#if 1 + int mode; + BufferDesc *buf; + PrivateRefCountEntry *ref; + uint64 sub; + uint64 lockstate; + + if (!BufferIsValid(buffer)) + elog(ERROR, "bad buffer ID: %d", buffer); + + if (BufferIsLocal(buffer)) + { + UnpinLocalBuffer(buffer); + return; + } + + ResourceOwnerForgetBuffer(CurrentResourceOwner, buffer); + + buf = GetBufferDescriptor(buffer - 1); + + mode = BufferLockDisownInternal(buffer, buf); + + /* compute state modification for lock release */ + sub = BufferLockReleaseSub(mode); + + /* compute state modification for pin release */ + ref = GetPrivateRefCountEntry(buffer, false); + Assert(ref != NULL); + Assert(ref->data.refcount > 0); + ref->data.refcount--; + + if (ref->data.refcount == 0) + { + sub |= BUF_REFCOUNT_ONE; + ForgetPrivateRefCountEntry(ref); + } + + /* perform the lock and pin release in one atomic op */ + lockstate = pg_atomic_sub_fetch_u64(&buf->state, sub); + + /* wake up waiters etc */ + BufferLockProcessRelease(buf, mode, lockstate); + + if (lockstate & BM_PIN_COUNT_WAITER) + WakePinCountWaiter(buf); + + RESUME_INTERRUPTS(); + +#else LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); +#endif } /* -- 2.48.1.76.g4e746b1a31.dirty