postgres.git / summary / log / commit / refs

commit    f2e6a2ccf1247bc15f6c03895c936b28cdeff181
Author:   Robert Haas <rhaas@postgresql.org>
Date:     Fri Nov 04 13:27:49 2016 +0000

    Add API to check if an existing exclusive lock allows cleanup.
    
    LockBufferForCleanup() acquires a cleanup lock unconditionally, and
    ConditionalLockBufferForCleanup() acquires a cleanup lock if it is
    possible to do so without waiting; this patch adds a new API,
    IsBufferCleanupOK(), which tests whether an exclusive lock already
    held happens to be a cleanup lock.  This is possible because a cleanup
    lock simply means an exclusive lock plus the assurance any other pins
    on the buffer are newer than our own pin.  Therefore, just as the
    existing functions decide that the exclusive lock that they've just
    taken is a cleanup lock if they observe the pin count to be 1, this
    new function allows us to observe that the pin count is 1 on a buffer
    we've already locked.
    
    This is useful in situations where a backend definitely wishes to
    modify the buffer and also wishes to perform cleanup operations if
    possible.  The patch to eliminate heavyweight locking by hash indexes
    uses this, and it may have other applications as well.
    
    Amit Kapila, per a suggestion from me.  Some comment adjustments by me
    as well.


src/backend/storage/buffer/bufmgr.c | 49 +++++++++++++++++++++++++++++++++++++ src/include/storage/bufmgr.h | 1 + 2 files changed, 50 insertions(+) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index df4c9d71094..58b0a975c0f 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -3745,6 +3745,55 @@ ConditionalLockBufferForCleanup(Buffer buffer) return false; } +/* + * IsBufferCleanupOK - as above, but we already have the lock + * + * Check whether it's OK to perform cleanup on a buffer we've already + * locked. If we observe that the pin count is 1, our exclusive lock + * happens to be a cleanup lock, and we can proceed with anything that + * would have been allowable had we sought a cleanup lock originally. + */ +bool +IsBufferCleanupOK(Buffer buffer) +{ + BufferDesc *bufHdr; + uint32 buf_state; + + Assert(BufferIsValid(buffer)); + + if (BufferIsLocal(buffer)) + { + /* There should be exactly one pin */ + if (LocalRefCount[-buffer - 1] != 1) + return false; + /* Nobody else to wait for */ + return true; + } + + /* There should be exactly one local pin */ + if (GetPrivateRefCount(buffer) != 1) + return false; + + bufHdr = GetBufferDescriptor(buffer - 1); + + /* caller must hold exclusive lock on buffer */ + Assert(LWLockHeldByMeInMode(BufferDescriptorGetContentLock(bufHdr), + LW_EXCLUSIVE)); + + buf_state = LockBufHdr(bufHdr); + + Assert(BUF_STATE_GET_REFCOUNT(buf_state) > 0); + if (BUF_STATE_GET_REFCOUNT(buf_state) == 1) + { + /* pincount is OK. */ + UnlockBufHdr(bufHdr, buf_state); + return true; + } + + UnlockBufHdr(bufHdr, buf_state); + return false; +} + /* * Functions for buffer I/O handling diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index 7b6ba960001..821bee5ecea 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -227,6 +227,7 @@ extern void LockBuffer(Buffer buffer, int mode); extern bool ConditionalLockBuffer(Buffer buffer); extern void LockBufferForCleanup(Buffer buffer); extern bool ConditionalLockBufferForCleanup(Buffer buffer); +extern bool IsBufferCleanupOK(Buffer buffer); extern bool HoldingBufferPinThatDelaysRecovery(void); extern void AbortBufferIO(void); [parent: 7016e4c4f59c]