/* * StrategyGetBuffer * * Called by the bufmgr to get the next candidate buffer to use in * BufferAlloc(). The only hard requirement BufferAlloc() has is that * the selected buffer must not currently be pinned by anyone. * * strategy is a BufferAccessStrategy object, or NULL for default strategy. * * To ensure that no one else can pin the buffer before we do, we must * return the buffer with the buffer header spinlock still held. */ volatile BufferDesc * StrategyGetBuffer(BufferAccessStrategy strategy) { volatile BufferDesc *buf; int bgwprocno; int trycounter; /* * If given a strategy object, see whether it can select a buffer. We * assume strategy objects don't need buffer_strategy_lock. */ if (strategy != NULL) { buf = GetBufferFromRing(strategy); if (buf != NULL) return buf; } /* * If asked, we need to waken the bgwriter. Since we don't want to rely on * a spinlock for this we force a read from shared memory once, and then * set the latch based on that value. We need to go through that length * because otherwise bgprocno might be reset while/after we check because * the compiler might just reread from memory. * * This can possibly set the latch of the wrong process if the bgwriter * dies in the wrong moment. But since PGPROC->procLatch is never * deallocated the worst consequence of that is that we set the latch of * some arbitrary process. */ bgwprocno = INT_ACCESS_ONCE(StrategyControl->bgwprocno); if (bgwprocno != -1) { /* reset bgwprocno first, before setting the latch */ StrategyControl->bgwprocno = -1; /* * Not acquiring ProcArrayLock here which is slightly icky. It's * actually fine because procLatch isn't ever freed, so we just can * potentially set the wrong process' (or no process') latch. */ SetLatch(&ProcGlobal->allProcs[bgwprocno].procLatch); } /* * We count buffer allocation requests so that the bgwriter can estimate * the rate of buffer consumption. Note that buffers recycled by a * strategy object are intentionally not counted here. */ pg_atomic_fetch_add_u32(&StrategyControl->numBufferAllocs, 1); /* * First check, without acquiring the lock, whether there's buffers in the * freelist. Since we otherwise don't require the spinlock in every * StrategyGetBuffer() invocation, it'd be sad to acquire it here - * uselessly in most cases. That obviously leaves a race where a buffer is * put on the freelist but we don't see the store yet - but that's pretty * harmless, it'll just get used during the next buffer acquisition. * * If there's buffers on the freelist, acquire the spinlock to pop one * buffer of the freelist. Then check whether that buffer is usable and * repeat if not. * * Note that the freeNext fields are considered to be protected by the * buffer_strategy_lock not the individual buffer spinlocks, so it's OK to * manipulate them without holding the spinlock. */ if (StrategyControl->firstFreeBuffer >= 0) { while (true) { /* Acquire the spinlock to remove element from the freelist */ SpinLockAcquire(&StrategyControl->buffer_strategy_lock); if (StrategyControl->firstFreeBuffer < 0) { SpinLockRelease(&StrategyControl->buffer_strategy_lock); break; } buf = GetBufferDescriptor(StrategyControl->firstFreeBuffer); Assert(buf->freeNext != FREENEXT_NOT_IN_LIST); /* Unconditionally remove buffer from freelist */ StrategyControl->firstFreeBuffer = buf->freeNext; buf->freeNext = FREENEXT_NOT_IN_LIST; /* * Release the lock so someone else can access the freelist while * we check out this buffer. */ SpinLockRelease(&StrategyControl->buffer_strategy_lock); /* * If the buffer is pinned or has a nonzero usage_count, we cannot * use it; discard it and retry. (This can only happen if VACUUM * put a valid buffer in the freelist and then someone else used * it before we got to it. It's probably impossible altogether as * of 8.3, but we'd better check anyway.) */ LockBufHdr(buf); if (buf->refcount == 0 && buf->usage_count == 0) { if (strategy != NULL) AddBufferToRing(strategy, buf); return buf; } UnlockBufHdr(buf); } } /* Nothing on the freelist, so run the "clock sweep" algorithm */ trycounter = NBuffers; for (;;) { buf = GetBufferDescriptor(ClockSweepTick()); /* * If the buffer is pinned or has a nonzero usage_count, we cannot use * it; decrement the usage_count (unless pinned) and keep scanning. */ LockBufHdr(buf); if (buf->refcount == 0) { if (buf->usage_count > 0) { buf->usage_count--; trycounter = NBuffers; } else { /* Found a usable buffer */ if (strategy != NULL) AddBufferToRing(strategy, buf); return buf; } } else if (--trycounter == 0) { /* * We've scanned all the buffers without making any state changes, * so all the buffers are pinned (or were when we looked at them). * We could hope that someone will free one eventually, but it's * probably better to fail than to risk getting stuck in an * infinite loop. */ UnlockBufHdr(buf); elog(ERROR, "no unpinned buffers available"); } UnlockBufHdr(buf); } }
/* * StrategyGetBuffer * * Called by the bufmgr to get the next candidate buffer to use in * BufferAlloc(). The only hard requirement BufferAlloc() has is that * the selected buffer must not currently be pinned by anyone. * * strategy is a BufferAccessStrategy object, or NULL for default strategy. * * To ensure that no one else can pin the buffer before we do, we must * return the buffer with the buffer header spinlock still held. If * *lock_held is set on exit, we have returned with the BufFreelistLock * still held, as well; the caller must release that lock once the spinlock * is dropped. We do it that way because releasing the BufFreelistLock * might awaken other processes, and it would be bad to do the associated * kernel calls while holding the buffer header spinlock. */ volatile BufferDesc * StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) { volatile BufferDesc *buf; Latch *bgwriterLatch; int trycounter; /* * If given a strategy object, see whether it can select a buffer. We * assume strategy objects don't need the BufFreelistLock. */ if (strategy != NULL) { buf = GetBufferFromRing(strategy); if (buf != NULL) { *lock_held = false; return buf; } } /* Nope, so lock the freelist */ *lock_held = true; LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); /* * We count buffer allocation requests so that the bgwriter can estimate * the rate of buffer consumption. Note that buffers recycled by a * strategy object are intentionally not counted here. */ StrategyControl->numBufferAllocs++; /* * If bgwriterLatch is set, we need to waken the bgwriter, but we should * not do so while holding BufFreelistLock; so release and re-grab. This * is annoyingly tedious, but it happens at most once per bgwriter cycle, * so the performance hit is minimal. */ bgwriterLatch = StrategyControl->bgwriterLatch; if (bgwriterLatch) { StrategyControl->bgwriterLatch = NULL; LWLockRelease(BufFreelistLock); SetLatch(bgwriterLatch); LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); } /* * Try to get a buffer from the freelist. Note that the freeNext fields * are considered to be protected by the BufFreelistLock not the * individual buffer spinlocks, so it's OK to manipulate them without * holding the spinlock. */ while (StrategyControl->firstFreeBuffer >= 0) { buf = &BufferDescriptors[StrategyControl->firstFreeBuffer]; Assert(buf->freeNext != FREENEXT_NOT_IN_LIST); /* Unconditionally remove buffer from freelist */ StrategyControl->firstFreeBuffer = buf->freeNext; buf->freeNext = FREENEXT_NOT_IN_LIST; /* * If the buffer is pinned or has a nonzero usage_count, we cannot use * it; discard it and retry. (This can only happen if VACUUM put a * valid buffer in the freelist and then someone else used it before * we got to it. It's probably impossible altogether as of 8.3, but * we'd better check anyway.) */ LockBufHdr(buf); if (buf->refcount == 0 && buf->usage_count == 0) { if (strategy != NULL) AddBufferToRing(strategy, buf); return buf; } UnlockBufHdr(buf); } if(true) { /* Now using LRU buffer replacement policy */ int currMinTimeStamp = INT_MAX; BufferStrategyControl *currTargetBuffer; for (StrategyControl->nextVictimBuffer=0;StrategyControl->nextVictimBuffer<NBuffers;StrategyControl->nextVictimBuffer++) { buf = &BufferDescriptors[StrategyControl->nextVictimBuffer]; if(StrategyControl->nextVictimBuffer == NBuffers-1 && currMinTimeStamp == INT_MAX) { StrategyControl ->completePasses++; elog(ERROR,"No available buffer frame"); } LockBufHdr(buf); if(buf->refcount==0 && buf->timer<currMinTimeStamp) { currMinTimeStamp = buf->timer; currTargetBuffer = buf; } UnlockBufHdr(buf); } return currTargetBuffer; } else { /* Nothing on the freelist, so run the "clock sweep" algorithm */ trycounter = NBuffers; for (;;) { buf = &BufferDescriptors[StrategyControl->nextVictimBuffer]; if (++StrategyControl->nextVictimBuffer >= NBuffers) { StrategyControl->nextVictimBuffer = 0; StrategyControl->completePasses++; } /* * If the buffer is pinned or has a nonzero usage_count, we cannot use * it; decrement the usage_count (unless pinned) and keep scanning. */ LockBufHdr(buf); if (buf->refcount == 0) { if (buf->usage_count > 0) { buf->usage_count--; trycounter = NBuffers; } else { /* Found a usable buffer */ if (strategy != NULL) AddBufferToRing(strategy, buf); return buf; } } else if (--trycounter == 0) { /* * We've scanned all the buffers without making any state changes, * so all the buffers are pinned (or were when we looked at them). * We could hope that someone will free one eventually, but it's * probably better to fail than to risk getting stuck in an * infinite loop. */ UnlockBufHdr(buf); elog(ERROR, "no unpinned buffers available"); } UnlockBufHdr(buf); } /* not reached */ return NULL; } }
/* * StrategyGetBuffer * * Called by the bufmgr to get the next candidate buffer to use in * BufferAlloc(). The only hard requirement BufferAlloc() has is that * the selected buffer must not currently be pinned by anyone. * * strategy is a BufferAccessStrategy object, or NULL for default strategy. * * To ensure that no one else can pin the buffer before we do, we must * return the buffer with the buffer header spinlock still held. If * *lock_held is set on exit, we have returned with the BufFreelistLock * still held, as well; the caller must release that lock once the spinlock * is dropped. We do it that way because releasing the BufFreelistLock * might awaken other processes, and it would be bad to do the associated * kernel calls while holding the buffer header spinlock. */ volatile BufferDesc * StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) { volatile BufferDesc *buf; Latch *bgwriterLatch; int trycounter; volatile int bufIndex = -1; volatile int resultIndex = -1; volatile BufferDesc *next; volatile BufferDesc *previous; /* * If given a strategy object, see whether it can select a buffer. We * assume strategy objects don't need the BufFreelistLock. */ if (strategy != NULL) { buf = GetBufferFromRing(strategy); if (buf != NULL) { *lock_held = false; return buf; } } /* Nope, so lock the freelist */ *lock_held = true; LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); /* * We count buffer allocation requests so that the bgwriter can estimate * the rate of buffer consumption. Note that buffers recycled by a * strategy object are intentionally not counted here. */ StrategyControl->numBufferAllocs++; /* * If bgwriterLatch is set, we need to waken the bgwriter, but we should * not do so while holding BufFreelistLock; so release and re-grab. This * is annoyingly tedious, but it happens at most once per bgwriter cycle, * so the performance hit is minimal. */ bgwriterLatch = StrategyControl->bgwriterLatch; if (bgwriterLatch) { StrategyControl->bgwriterLatch = NULL; LWLockRelease(BufFreelistLock); SetLatch(bgwriterLatch); LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); } /* * Try to get a buffer from the freelist. Note that the freeNext fields * are considered to be protected by the BufFreelistLock not the * individual buffer spinlocks, so it's OK to manipulate them without * holding the spinlock. */ while (StrategyControl->firstFreeBuffer >= 0) { buf = &BufferDescriptors[StrategyControl->firstFreeBuffer]; Assert(buf->freeNext != FREENEXT_NOT_IN_LIST); /* Unconditionally remove buffer from freelist */ StrategyControl->firstFreeBuffer = buf->freeNext; buf->freeNext = FREENEXT_NOT_IN_LIST; /* * If the buffer is pinned or has a nonzero usage_count, we cannot use * it; discard it and retry. (This can only happen if VACUUM put a * valid buffer in the freelist and then someone else used it before * we got to it. It's probably impossible altogether as of 8.3, but * we'd better check anyway.) */ LockBufHdr(buf); if (buf->refcount == 0 && buf->usage_count == 0) { if (strategy != NULL) AddBufferToRing(strategy, buf); return buf; } UnlockBufHdr(buf); } /* Nothing on the freelist, so run the algorithm defined in the * BufferReplacementPolicy variable*/ if (resultIndex == -1) { if (BufferReplacementPolicy == POLICY_CLOCK) { //Running the Clock Sweep Algorithm (Default postgres Algo.) trycounter = NBuffers; for (;;) { buf = &BufferDescriptors[StrategyControl->nextVictimBuffer]; /* * If the clock sweep hand has reached the end of the * buffer pool, start back at the beginning. */ if (++StrategyControl->nextVictimBuffer >= NBuffers) { StrategyControl->nextVictimBuffer = 0; StrategyControl->completePasses++; } /* * If the buffer is pinned or has a nonzero usage_count, we cannot use * it; decrement the usage_count (unless pinned) and keep scanning. */ LockBufHdr(buf); if (buf->refcount == 0) { if (buf->usage_count > 0) { buf->usage_count--; trycounter = NBuffers; } else { /* Found a usable buffer */ if (strategy != NULL) AddBufferToRing(strategy, buf); return buf; } } else if (--trycounter == 0) { /* * We've scanned all the buffers without making any state changes, * so all the buffers are pinned (or were when we looked at them). * We could hope that someone will free one eventually, but it's * probably better to fail than to risk getting stuck in an * infinite loop. */ UnlockBufHdr(buf); elog(ERROR, "no unpinned buffers available"); } UnlockBufHdr(buf); } } /* Implementation of LRU, MRU and 2Q Algorithms. * Once we've selected a buffer to evict, its index in * the BufferDescriptors array is stored in "resultIndex" */ else if (BufferReplacementPolicy == POLICY_LRU) { buf = StrategyControl->firstUnpinned; while (buf != NULL) { LockBufHdr(buf); if (buf->refcount == 0) { resultIndex = buf->buf_id; break; } else { UnlockBufHdr(buf); buf = buf->next; } } /* * We've scanned all the buffers without making any state changes, * so all the buffers are pinned (or were when we looked at them). * We could hope that someone will free one eventually, but it's * probably better to fail than to risk getting stuck in an * infinite loop. */ if (buf == NULL) { UnlockBufHdr(buf); //p added 10/24 elog(ERROR, "no unpinned buffers available"); } } else if (BufferReplacementPolicy == POLICY_MRU) { buf = StrategyControl->lastUnpinned; while (buf != NULL) { LockBufHdr(buf); if (buf->refcount == 0) { resultIndex = buf->buf_id; break; } else { UnlockBufHdr(buf); buf = buf->previous; } } /* * We've scanned all the buffers without making any state changes, * so all the buffers are pinned (or were when we looked at them). * We could hope that someone will free one eventually, but it's * probably better to fail than to risk getting stuck in an * infinite loop. */ if (buf == NULL) { UnlockBufHdr(buf); //p added 10/24 elog(ERROR, "no unpinned buffers available"); } } else if (BufferReplacementPolicy == POLICY_2Q) { int thres = NBuffers/2; int sizeA1 = 0; volatile BufferDesc *head = StrategyControl->a1Head; while (head != NULL) { head = head->next; sizeA1++; } if (sizeA1 >= thres || StrategyControl->lastUnpinned == NULL) { buf = StrategyControl->a1Head; while (buf != NULL) { if (buf->refcount == 0) { resultIndex = buf->buf_id; next = buf->next; previous = buf->previous; //adjust neighbors if (next != NULL) { if (previous != NULL) { //next and prev != null, buf is already in middle of list previous->next = next; next->previous = previous; } else { //next != null, prev == null, buf is at beginning of list next->previous = NULL; StrategyControl->a1Head = next; } } else if (previous == NULL) { //next == NULL, prev == null, buf is only item in list StrategyControl->a1Head = NULL; StrategyControl->a1Tail = NULL; } else { //buf is last item in list, next == null, prev != null StrategyControl->a1Tail = previous; previous->next = NULL; } buf->next = NULL; buf->previous = NULL; break; } else { buf = buf->next; } } if (buf == NULL) { elog(ERROR, "no unpinned buffers available"); } } else { // delete from the head of AM buf = StrategyControl->firstUnpinned; while (buf != NULL) { // LockBufHdr(buf); if (buf->refcount == 0) { resultIndex = buf->buf_id; next = buf->next; previous = buf->previous; //adjust neighbors if (next != NULL) { if (previous != NULL) { //next and prev != null, buf is already in middle of list previous->next = next; next->previous = previous; } else { //next != null, prev == null, buf is at beginning of list next->previous = NULL; StrategyControl->firstUnpinned = next; } } else if (previous == NULL) { //next == NULL, prev == null, buf is new to list StrategyControl->firstUnpinned = NULL; StrategyControl->lastUnpinned = NULL; } else { previous->next = NULL; StrategyControl->lastUnpinned = previous; } buf->next = NULL; buf->previous = NULL; break; } else { // UnlockBufHdr(buf); buf = buf->next; } // UnlockBufHdr(buf); //phoebe 10/24 } if (buf == NULL) { // UnlockBufHdr(buf); elog(ERROR, "no unpinned buffers available"); } } } else { elog(ERROR, "invalid buffer pool replacement policy %d", BufferReplacementPolicy); } } if (resultIndex == -1) { elog(ERROR, "reached end of StrategyGetBuffer() without selecting a buffer"); } return &BufferDescriptors[resultIndex]; }