From 5cfd98c132556aa6a95d74f7150a4048243e65e5 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan Date: Tue, 17 Mar 2026 13:25:12 -0400 Subject: [PATCH v18 07/19] Add UnlockBufferGetLSN utility function. This combines the functionality of BufferGetLSNAtomic() with UnlockReleaseBuffer, with the option of not releasing buffer pin. Author: Peter Geoghegan Discussion: https://postgr.es/m/CAH2-WzkyG01682zwqyUTwV=Zq+M_qGgi1NbXwp1H-piRSfJsgQ@mail.gmail.com --- src/include/storage/bufmgr.h | 1 + src/backend/access/index/indexbatch.c | 12 +--- src/backend/storage/buffer/bufmgr.c | 90 +++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 11 deletions(-) diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index dd41b92f9..681a3e56f 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -255,6 +255,7 @@ extern void WaitReadBuffers(ReadBuffersOperation *operation); extern void ReleaseBuffer(Buffer buffer); extern void UnlockReleaseBuffer(Buffer buffer); +extern XLogRecPtr UnlockBufferGetLSN(Buffer buffer, bool release); extern bool BufferIsLockedByMe(Buffer buffer); extern bool BufferIsLockedByMeInMode(Buffer buffer, BufferLockMode mode); extern bool BufferIsDirty(Buffer buffer); diff --git a/src/backend/access/index/indexbatch.c b/src/backend/access/index/indexbatch.c index 3abf58453..565182430 100644 --- a/src/backend/access/index/indexbatch.c +++ b/src/backend/access/index/indexbatch.c @@ -491,17 +491,7 @@ indexam_util_batch_unlock(IndexScanDesc scan, IndexScanBatch batch, Buffer buf) * Non-immediate-unguard scans retain the pin; the table AM will call * amunguardbatch to drop the interlock when ready. */ - batch->lsn = BufferGetLSNAtomic(buf); - if (scan->batchImmediateUnguard) - { - /* drop both the lock and the pin */ - UnlockReleaseBuffer(buf); - } - else - { - /* just drop the lock (hold on to interlock pin) */ - UnlockBuffer(buf); - } + batch->lsn = UnlockBufferGetLSN(buf, scan->batchImmediateUnguard); /* If we released buffer pin, batch is now unguarded */ batch->isGuarded = !scan->batchImmediateUnguard; diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index cd21ae3fc..4cdb565e4 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -5638,6 +5638,96 @@ UnlockReleaseBuffer(Buffer buffer) RESUME_INTERRUPTS(); } +/* + * UnlockBufferGetLSN + * Read a buffer's LSN, then unlock the buffer. Optionally release the + * pin too. + * + * This combines the functionality of BufferGetLSNAtomic() with + * UnlockReleaseBuffer, with the option of not releasing buffer pin. + * + * Used to unlock a buffer lock (and possibly to release its pin) when held on + * an index page. This is needed frequently enough to justify specialization. + */ +XLogRecPtr +UnlockBufferGetLSN(Buffer buffer, bool release) +{ + BufferDesc *buf_hdr; + Page page; + XLogRecPtr lsn; + int mode; + uint64 sub; + uint64 lockstate; + PrivateRefCountEntry *ref; + + Assert(BufferIsValid(buffer)); + Assert(BufferIsPinned(buffer)); + + if (BufferIsLocal(buffer)) + { + buf_hdr = GetLocalBufferDescriptor(-buffer - 1); + page = (Page) LocalBufHdrGetBlock(buf_hdr); + lsn = PageGetLSN(page); + if (release) + UnpinLocalBuffer(buffer); + return lsn; + } + + buf_hdr = GetBufferDescriptor(buffer - 1); + page = (Page) BufHdrGetBlock(buf_hdr); + +#ifdef PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY + lsn = PageGetLSN(page); +#else + lsn = BufferGetLSNAtomic(buffer); +#endif + + if (!release) + { + /* Just release the lock */ + BufferLockUnlock(buffer, buf_hdr); + return lsn; + } + + /* Release both lock and pin in one atomic op */ + ResourceOwnerForgetBuffer(CurrentResourceOwner, buffer); + + mode = BufferLockDisownInternal(buffer, buf_hdr); + + /* 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--; + + /* no more backend local pins, reduce shared pin count */ + if (likely(ref->data.refcount == 0)) + { + /* See comment in UnpinBufferNoOwner() */ + VALGRIND_MAKE_MEM_NOACCESS(BufHdrGetBlock(buf_hdr), BLCKSZ); + + sub |= BUF_REFCOUNT_ONE; + ForgetPrivateRefCountEntry(ref); + } + + /* perform the lock and pin release in one atomic op */ + lockstate = pg_atomic_sub_fetch_u64(&buf_hdr->state, sub); + + /* wake up waiters for the lock */ + BufferLockProcessRelease(buf_hdr, mode, lockstate); + + /* wake up waiter for the pin release */ + if (lockstate & BM_PIN_COUNT_WAITER) + WakePinCountWaiter(buf_hdr); + + RESUME_INTERRUPTS(); + + return lsn; +} + /* * IncrBufferRefCount * Increment the pin count on a buffer that we have *already* pinned -- 2.53.0