diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index dba19eb..87446cb 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -955,6 +955,88 @@ retry: StrategyFreeBuffer(buf); } + +/* + * MoveBufferToFreeList -- mark a shared buffer invalid and return it to the + * freelist. which is similar to InvalidateBuffer function. + */ +static void +MoveBufferToFreeList(volatile BufferDesc *buf) +{ + BufferTag oldTag; + uint32 oldHash; /* hash value for oldTag */ + LWLockId oldPartitionLock; /* buffer partition lock for it */ + BufFlags oldFlags; + + /* Save the original buffer tag before dropping the spinlock */ + oldTag = buf->tag; + + UnlockBufHdr(buf); + + /* + * Need to compute the old tag's hashcode and partition lock ID. XXX is it + * worth storing the hashcode in BufferDesc so we need not recompute it + * here? Probably not. + */ + oldHash = BufTableHashCode(&oldTag); + oldPartitionLock = BufMappingPartitionLock(oldHash); + + + /* + * Acquire exclusive mapping lock in preparation for changing the buffer's + * association. + */ + LWLockAcquire(oldPartitionLock, LW_EXCLUSIVE); + + /* Re-lock the buffer header */ + LockBufHdr(buf); + + /* If it's changed while we were waiting for lock, do nothing */ + if (!BUFFERTAGS_EQUAL(buf->tag, oldTag)) + { + UnlockBufHdr(buf); + LWLockRelease(oldPartitionLock); + return; + } + + /* + * Validate wheather we can add the buffer into freelist or not + */ + if ((buf->refcount != 0) || (buf->usage_count != 0)) + { + UnlockBufHdr(buf); + LWLockRelease(oldPartitionLock); + return; + } + + /* + * Clear out the buffer's tag and flags. We must do this to ensure that + * linear scans of the buffer array don't think the buffer is valid. + */ + oldFlags = buf->flags; + CLEAR_BUFFERTAG(buf->tag); + buf->flags = 0; + buf->usage_count = 0; + + UnlockBufHdr(buf); + + /* + * Remove the buffer from the lookup hashtable, if it was in there. + */ + if (oldFlags & BM_TAG_VALID) + BufTableDelete(&oldTag, oldHash); + + /* + * Done with mapping lock. + */ + LWLockRelease(oldPartitionLock); + + /* + * Insert the buffer at the head of the list of free buffers. + */ + StrategyFreeBuffer(buf); +} + /* * MarkBufferDirty * @@ -1658,12 +1740,29 @@ SyncOneBuffer(int buf_id, bool skip_recently_used) return result; } - if (!(bufHdr->flags & BM_VALID) || !(bufHdr->flags & BM_DIRTY)) + if (!(bufHdr->flags & BM_VALID)) { /* It's clean, so nothing to do */ UnlockBufHdr(bufHdr); return result; } + else if (!(bufHdr->flags & BM_DIRTY)) + { + /* + * It's clean, so nothing to flush; + * if buffer is unused then move it to freelist + */ + if ((bufHdr->refcount == 0 && bufHdr->usage_count == 0) + && (bufHdr->freeNext == FREENEXT_NOT_IN_LIST)) + { + MoveBufferToFreeList (bufHdr); + } + else + { + UnlockBufHdr(bufHdr); + } + return result; + } /* * Pin it, share-lock it, write it. (FlushBuffer will do nothing if the @@ -1677,6 +1776,19 @@ SyncOneBuffer(int buf_id, bool skip_recently_used) LWLockRelease(bufHdr->content_lock); UnpinBuffer(bufHdr, true); + /* + * Buffer is unused then move it to freelist + */ + LockBufHdr(bufHdr); + if (bufHdr->refcount == 0 && bufHdr->usage_count == 0) + { + MoveBufferToFreeList (bufHdr); + } + else + { + UnlockBufHdr(bufHdr); + } + return result | BUF_WRITTEN; }