Example #1
0
/*
 * 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);
	}
}
Example #2
0
/*
 * 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;
	}
}
Example #3
0
/*
 * 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];

}