LCOV - code coverage report
Current view: top level - src/backend/storage/buffer - freelist.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16devel Lines: 150 157 95.5 %
Date: 2022-08-17 04:10:37 Functions: 13 13 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * freelist.c
       4             :  *    routines for managing the buffer pool's replacement strategy.
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  *
      11             :  * IDENTIFICATION
      12             :  *    src/backend/storage/buffer/freelist.c
      13             :  *
      14             :  *-------------------------------------------------------------------------
      15             :  */
      16             : #include "postgres.h"
      17             : 
      18             : #include "port/atomics.h"
      19             : #include "storage/buf_internals.h"
      20             : #include "storage/bufmgr.h"
      21             : #include "storage/proc.h"
      22             : 
      23             : #define INT_ACCESS_ONCE(var)    ((int)(*((volatile int *)&(var))))
      24             : 
      25             : 
      26             : /*
      27             :  * The shared freelist control information.
      28             :  */
      29             : typedef struct
      30             : {
      31             :     /* Spinlock: protects the values below */
      32             :     slock_t     buffer_strategy_lock;
      33             : 
      34             :     /*
      35             :      * Clock sweep hand: index of next buffer to consider grabbing. Note that
      36             :      * this isn't a concrete buffer - we only ever increase the value. So, to
      37             :      * get an actual buffer, it needs to be used modulo NBuffers.
      38             :      */
      39             :     pg_atomic_uint32 nextVictimBuffer;
      40             : 
      41             :     int         firstFreeBuffer;    /* Head of list of unused buffers */
      42             :     int         lastFreeBuffer; /* Tail of list of unused buffers */
      43             : 
      44             :     /*
      45             :      * NOTE: lastFreeBuffer is undefined when firstFreeBuffer is -1 (that is,
      46             :      * when the list is empty)
      47             :      */
      48             : 
      49             :     /*
      50             :      * Statistics.  These counters should be wide enough that they can't
      51             :      * overflow during a single bgwriter cycle.
      52             :      */
      53             :     uint32      completePasses; /* Complete cycles of the clock sweep */
      54             :     pg_atomic_uint32 numBufferAllocs;   /* Buffers allocated since last reset */
      55             : 
      56             :     /*
      57             :      * Bgworker process to be notified upon activity or -1 if none. See
      58             :      * StrategyNotifyBgWriter.
      59             :      */
      60             :     int         bgwprocno;
      61             : } BufferStrategyControl;
      62             : 
      63             : /* Pointers to shared state */
      64             : static BufferStrategyControl *StrategyControl = NULL;
      65             : 
      66             : /*
      67             :  * Private (non-shared) state for managing a ring of shared buffers to re-use.
      68             :  * This is currently the only kind of BufferAccessStrategy object, but someday
      69             :  * we might have more kinds.
      70             :  */
      71             : typedef struct BufferAccessStrategyData
      72             : {
      73             :     /* Overall strategy type */
      74             :     BufferAccessStrategyType btype;
      75             :     /* Number of elements in buffers[] array */
      76             :     int         ring_size;
      77             : 
      78             :     /*
      79             :      * Index of the "current" slot in the ring, ie, the one most recently
      80             :      * returned by GetBufferFromRing.
      81             :      */
      82             :     int         current;
      83             : 
      84             :     /*
      85             :      * True if the buffer just returned by StrategyGetBuffer had been in the
      86             :      * ring already.
      87             :      */
      88             :     bool        current_was_in_ring;
      89             : 
      90             :     /*
      91             :      * Array of buffer numbers.  InvalidBuffer (that is, zero) indicates we
      92             :      * have not yet selected a buffer for this ring slot.  For allocation
      93             :      * simplicity this is palloc'd together with the fixed fields of the
      94             :      * struct.
      95             :      */
      96             :     Buffer      buffers[FLEXIBLE_ARRAY_MEMBER];
      97             : }           BufferAccessStrategyData;
      98             : 
      99             : 
     100             : /* Prototypes for internal functions */
     101             : static BufferDesc *GetBufferFromRing(BufferAccessStrategy strategy,
     102             :                                      uint32 *buf_state);
     103             : static void AddBufferToRing(BufferAccessStrategy strategy,
     104             :                             BufferDesc *buf);
     105             : 
     106             : /*
     107             :  * ClockSweepTick - Helper routine for StrategyGetBuffer()
     108             :  *
     109             :  * Move the clock hand one buffer ahead of its current position and return the
     110             :  * id of the buffer now under the hand.
     111             :  */
     112             : static inline uint32
     113     5661102 : ClockSweepTick(void)
     114             : {
     115             :     uint32      victim;
     116             : 
     117             :     /*
     118             :      * Atomically move hand ahead one buffer - if there's several processes
     119             :      * doing this, this can lead to buffers being returned slightly out of
     120             :      * apparent order.
     121             :      */
     122             :     victim =
     123     5661102 :         pg_atomic_fetch_add_u32(&StrategyControl->nextVictimBuffer, 1);
     124             : 
     125     5661102 :     if (victim >= NBuffers)
     126             :     {
     127       45900 :         uint32      originalVictim = victim;
     128             : 
     129             :         /* always wrap what we look up in BufferDescriptors */
     130       45900 :         victim = victim % NBuffers;
     131             : 
     132             :         /*
     133             :          * If we're the one that just caused a wraparound, force
     134             :          * completePasses to be incremented while holding the spinlock. We
     135             :          * need the spinlock so StrategySyncStart() can return a consistent
     136             :          * value consisting of nextVictimBuffer and completePasses.
     137             :          */
     138       45900 :         if (victim == 0)
     139             :         {
     140             :             uint32      expected;
     141             :             uint32      wrapped;
     142       45740 :             bool        success = false;
     143             : 
     144       45740 :             expected = originalVictim + 1;
     145             : 
     146       91628 :             while (!success)
     147             :             {
     148             :                 /*
     149             :                  * Acquire the spinlock while increasing completePasses. That
     150             :                  * allows other readers to read nextVictimBuffer and
     151             :                  * completePasses in a consistent manner which is required for
     152             :                  * StrategySyncStart().  In theory delaying the increment
     153             :                  * could lead to an overflow of nextVictimBuffers, but that's
     154             :                  * highly unlikely and wouldn't be particularly harmful.
     155             :                  */
     156       45888 :                 SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
     157             : 
     158       45888 :                 wrapped = expected % NBuffers;
     159             : 
     160       45888 :                 success = pg_atomic_compare_exchange_u32(&StrategyControl->nextVictimBuffer,
     161             :                                                          &expected, wrapped);
     162       45888 :                 if (success)
     163       45740 :                     StrategyControl->completePasses++;
     164       45888 :                 SpinLockRelease(&StrategyControl->buffer_strategy_lock);
     165             :             }
     166             :         }
     167             :     }
     168     5661102 :     return victim;
     169             : }
     170             : 
     171             : /*
     172             :  * have_free_buffer -- a lockless check to see if there is a free buffer in
     173             :  *                     buffer pool.
     174             :  *
     175             :  * If the result is true that will become stale once free buffers are moved out
     176             :  * by other operations, so the caller who strictly want to use a free buffer
     177             :  * should not call this.
     178             :  */
     179             : bool
     180         352 : have_free_buffer(void)
     181             : {
     182         352 :     if (StrategyControl->firstFreeBuffer >= 0)
     183         352 :         return true;
     184             :     else
     185           0 :         return false;
     186             : }
     187             : 
     188             : /*
     189             :  * StrategyGetBuffer
     190             :  *
     191             :  *  Called by the bufmgr to get the next candidate buffer to use in
     192             :  *  BufferAlloc(). The only hard requirement BufferAlloc() has is that
     193             :  *  the selected buffer must not currently be pinned by anyone.
     194             :  *
     195             :  *  strategy is a BufferAccessStrategy object, or NULL for default strategy.
     196             :  *
     197             :  *  To ensure that no one else can pin the buffer before we do, we must
     198             :  *  return the buffer with the buffer header spinlock still held.
     199             :  */
     200             : BufferDesc *
     201     3406094 : StrategyGetBuffer(BufferAccessStrategy strategy, uint32 *buf_state)
     202             : {
     203             :     BufferDesc *buf;
     204             :     int         bgwprocno;
     205             :     int         trycounter;
     206             :     uint32      local_buf_state;    /* to avoid repeated (de-)referencing */
     207             : 
     208             :     /*
     209             :      * If given a strategy object, see whether it can select a buffer. We
     210             :      * assume strategy objects don't need buffer_strategy_lock.
     211             :      */
     212     3406094 :     if (strategy != NULL)
     213             :     {
     214     1167576 :         buf = GetBufferFromRing(strategy, buf_state);
     215     1167576 :         if (buf != NULL)
     216      540892 :             return buf;
     217             :     }
     218             : 
     219             :     /*
     220             :      * If asked, we need to waken the bgwriter. Since we don't want to rely on
     221             :      * a spinlock for this we force a read from shared memory once, and then
     222             :      * set the latch based on that value. We need to go through that length
     223             :      * because otherwise bgwprocno might be reset while/after we check because
     224             :      * the compiler might just reread from memory.
     225             :      *
     226             :      * This can possibly set the latch of the wrong process if the bgwriter
     227             :      * dies in the wrong moment. But since PGPROC->procLatch is never
     228             :      * deallocated the worst consequence of that is that we set the latch of
     229             :      * some arbitrary process.
     230             :      */
     231     2865202 :     bgwprocno = INT_ACCESS_ONCE(StrategyControl->bgwprocno);
     232     2865202 :     if (bgwprocno != -1)
     233             :     {
     234             :         /* reset bgwprocno first, before setting the latch */
     235         344 :         StrategyControl->bgwprocno = -1;
     236             : 
     237             :         /*
     238             :          * Not acquiring ProcArrayLock here which is slightly icky. It's
     239             :          * actually fine because procLatch isn't ever freed, so we just can
     240             :          * potentially set the wrong process' (or no process') latch.
     241             :          */
     242         344 :         SetLatch(&ProcGlobal->allProcs[bgwprocno].procLatch);
     243             :     }
     244             : 
     245             :     /*
     246             :      * We count buffer allocation requests so that the bgwriter can estimate
     247             :      * the rate of buffer consumption.  Note that buffers recycled by a
     248             :      * strategy object are intentionally not counted here.
     249             :      */
     250     2865202 :     pg_atomic_fetch_add_u32(&StrategyControl->numBufferAllocs, 1);
     251             : 
     252             :     /*
     253             :      * First check, without acquiring the lock, whether there's buffers in the
     254             :      * freelist. Since we otherwise don't require the spinlock in every
     255             :      * StrategyGetBuffer() invocation, it'd be sad to acquire it here -
     256             :      * uselessly in most cases. That obviously leaves a race where a buffer is
     257             :      * put on the freelist but we don't see the store yet - but that's pretty
     258             :      * harmless, it'll just get used during the next buffer acquisition.
     259             :      *
     260             :      * If there's buffers on the freelist, acquire the spinlock to pop one
     261             :      * buffer of the freelist. Then check whether that buffer is usable and
     262             :      * repeat if not.
     263             :      *
     264             :      * Note that the freeNext fields are considered to be protected by the
     265             :      * buffer_strategy_lock not the individual buffer spinlocks, so it's OK to
     266             :      * manipulate them without holding the spinlock.
     267             :      */
     268     2865202 :     if (StrategyControl->firstFreeBuffer >= 0)
     269             :     {
     270             :         while (true)
     271             :         {
     272             :             /* Acquire the spinlock to remove element from the freelist */
     273     1454232 :             SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
     274             : 
     275     1454232 :             if (StrategyControl->firstFreeBuffer < 0)
     276             :             {
     277           8 :                 SpinLockRelease(&StrategyControl->buffer_strategy_lock);
     278           8 :                 break;
     279             :             }
     280             : 
     281     1454224 :             buf = GetBufferDescriptor(StrategyControl->firstFreeBuffer);
     282             :             Assert(buf->freeNext != FREENEXT_NOT_IN_LIST);
     283             : 
     284             :             /* Unconditionally remove buffer from freelist */
     285     1454224 :             StrategyControl->firstFreeBuffer = buf->freeNext;
     286     1454224 :             buf->freeNext = FREENEXT_NOT_IN_LIST;
     287             : 
     288             :             /*
     289             :              * Release the lock so someone else can access the freelist while
     290             :              * we check out this buffer.
     291             :              */
     292     1454224 :             SpinLockRelease(&StrategyControl->buffer_strategy_lock);
     293             : 
     294             :             /*
     295             :              * If the buffer is pinned or has a nonzero usage_count, we cannot
     296             :              * use it; discard it and retry.  (This can only happen if VACUUM
     297             :              * put a valid buffer in the freelist and then someone else used
     298             :              * it before we got to it.  It's probably impossible altogether as
     299             :              * of 8.3, but we'd better check anyway.)
     300             :              */
     301     1454224 :             local_buf_state = LockBufHdr(buf);
     302     1454224 :             if (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0
     303     1454220 :                 && BUF_STATE_GET_USAGECOUNT(local_buf_state) == 0)
     304             :             {
     305     1454204 :                 if (strategy != NULL)
     306      457894 :                     AddBufferToRing(strategy, buf);
     307     1454204 :                 *buf_state = local_buf_state;
     308     1454204 :                 return buf;
     309             :             }
     310          20 :             UnlockBufHdr(buf, local_buf_state);
     311             :         }
     312             :     }
     313             : 
     314             :     /* Nothing on the freelist, so run the "clock sweep" algorithm */
     315     1410998 :     trycounter = NBuffers;
     316             :     for (;;)
     317             :     {
     318     5661102 :         buf = GetBufferDescriptor(ClockSweepTick());
     319             : 
     320             :         /*
     321             :          * If the buffer is pinned or has a nonzero usage_count, we cannot use
     322             :          * it; decrement the usage_count (unless pinned) and keep scanning.
     323             :          */
     324     5661102 :         local_buf_state = LockBufHdr(buf);
     325             : 
     326     5661102 :         if (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0)
     327             :         {
     328     5598914 :             if (BUF_STATE_GET_USAGECOUNT(local_buf_state) != 0)
     329             :             {
     330     4187916 :                 local_buf_state -= BUF_USAGECOUNT_ONE;
     331             : 
     332     4187916 :                 trycounter = NBuffers;
     333             :             }
     334             :             else
     335             :             {
     336             :                 /* Found a usable buffer */
     337     1410998 :                 if (strategy != NULL)
     338      168790 :                     AddBufferToRing(strategy, buf);
     339     1410998 :                 *buf_state = local_buf_state;
     340     1410998 :                 return buf;
     341             :             }
     342             :         }
     343       62188 :         else if (--trycounter == 0)
     344             :         {
     345             :             /*
     346             :              * We've scanned all the buffers without making any state changes,
     347             :              * so all the buffers are pinned (or were when we looked at them).
     348             :              * We could hope that someone will free one eventually, but it's
     349             :              * probably better to fail than to risk getting stuck in an
     350             :              * infinite loop.
     351             :              */
     352           0 :             UnlockBufHdr(buf, local_buf_state);
     353           0 :             elog(ERROR, "no unpinned buffers available");
     354             :         }
     355     4250104 :         UnlockBufHdr(buf, local_buf_state);
     356             :     }
     357             : }
     358             : 
     359             : /*
     360             :  * StrategyFreeBuffer: put a buffer on the freelist
     361             :  */
     362             : void
     363      170862 : StrategyFreeBuffer(BufferDesc *buf)
     364             : {
     365      170862 :     SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
     366             : 
     367             :     /*
     368             :      * It is possible that we are told to put something in the freelist that
     369             :      * is already in it; don't screw up the list if so.
     370             :      */
     371      170862 :     if (buf->freeNext == FREENEXT_NOT_IN_LIST)
     372             :     {
     373      170862 :         buf->freeNext = StrategyControl->firstFreeBuffer;
     374      170862 :         if (buf->freeNext < 0)
     375        3352 :             StrategyControl->lastFreeBuffer = buf->buf_id;
     376      170862 :         StrategyControl->firstFreeBuffer = buf->buf_id;
     377             :     }
     378             : 
     379      170862 :     SpinLockRelease(&StrategyControl->buffer_strategy_lock);
     380      170862 : }
     381             : 
     382             : /*
     383             :  * StrategySyncStart -- tell BufferSync where to start syncing
     384             :  *
     385             :  * The result is the buffer index of the best buffer to sync first.
     386             :  * BufferSync() will proceed circularly around the buffer array from there.
     387             :  *
     388             :  * In addition, we return the completed-pass count (which is effectively
     389             :  * the higher-order bits of nextVictimBuffer) and the count of recent buffer
     390             :  * allocs if non-NULL pointers are passed.  The alloc count is reset after
     391             :  * being read.
     392             :  */
     393             : int
     394       10396 : StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc)
     395             : {
     396             :     uint32      nextVictimBuffer;
     397             :     int         result;
     398             : 
     399       10396 :     SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
     400       10396 :     nextVictimBuffer = pg_atomic_read_u32(&StrategyControl->nextVictimBuffer);
     401       10396 :     result = nextVictimBuffer % NBuffers;
     402             : 
     403       10396 :     if (complete_passes)
     404             :     {
     405       10396 :         *complete_passes = StrategyControl->completePasses;
     406             : 
     407             :         /*
     408             :          * Additionally add the number of wraparounds that happened before
     409             :          * completePasses could be incremented. C.f. ClockSweepTick().
     410             :          */
     411       10396 :         *complete_passes += nextVictimBuffer / NBuffers;
     412             :     }
     413             : 
     414       10396 :     if (num_buf_alloc)
     415             :     {
     416       10396 :         *num_buf_alloc = pg_atomic_exchange_u32(&StrategyControl->numBufferAllocs, 0);
     417             :     }
     418       10396 :     SpinLockRelease(&StrategyControl->buffer_strategy_lock);
     419       10396 :     return result;
     420             : }
     421             : 
     422             : /*
     423             :  * StrategyNotifyBgWriter -- set or clear allocation notification latch
     424             :  *
     425             :  * If bgwprocno isn't -1, the next invocation of StrategyGetBuffer will
     426             :  * set that latch.  Pass -1 to clear the pending notification before it
     427             :  * happens.  This feature is used by the bgwriter process to wake itself up
     428             :  * from hibernation, and is not meant for anybody else to use.
     429             :  */
     430             : void
     431         520 : StrategyNotifyBgWriter(int bgwprocno)
     432             : {
     433             :     /*
     434             :      * We acquire buffer_strategy_lock just to ensure that the store appears
     435             :      * atomic to StrategyGetBuffer.  The bgwriter should call this rather
     436             :      * infrequently, so there's no performance penalty from being safe.
     437             :      */
     438         520 :     SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
     439         520 :     StrategyControl->bgwprocno = bgwprocno;
     440         520 :     SpinLockRelease(&StrategyControl->buffer_strategy_lock);
     441         520 : }
     442             : 
     443             : 
     444             : /*
     445             :  * StrategyShmemSize
     446             :  *
     447             :  * estimate the size of shared memory used by the freelist-related structures.
     448             :  *
     449             :  * Note: for somewhat historical reasons, the buffer lookup hashtable size
     450             :  * is also determined here.
     451             :  */
     452             : Size
     453        4996 : StrategyShmemSize(void)
     454             : {
     455        4996 :     Size        size = 0;
     456             : 
     457             :     /* size of lookup hash table ... see comment in StrategyInitialize */
     458        4996 :     size = add_size(size, BufTableShmemSize(NBuffers + NUM_BUFFER_PARTITIONS));
     459             : 
     460             :     /* size of the shared replacement strategy control block */
     461        4996 :     size = add_size(size, MAXALIGN(sizeof(BufferStrategyControl)));
     462             : 
     463        4996 :     return size;
     464             : }
     465             : 
     466             : /*
     467             :  * StrategyInitialize -- initialize the buffer cache replacement
     468             :  *      strategy.
     469             :  *
     470             :  * Assumes: All of the buffers are already built into a linked list.
     471             :  *      Only called by postmaster and only during initialization.
     472             :  */
     473             : void
     474        3328 : StrategyInitialize(bool init)
     475             : {
     476             :     bool        found;
     477             : 
     478             :     /*
     479             :      * Initialize the shared buffer lookup hashtable.
     480             :      *
     481             :      * Since we can't tolerate running out of lookup table entries, we must be
     482             :      * sure to specify an adequate table size here.  The maximum steady-state
     483             :      * usage is of course NBuffers entries, but BufferAlloc() tries to insert
     484             :      * a new entry before deleting the old.  In principle this could be
     485             :      * happening in each partition concurrently, so we could need as many as
     486             :      * NBuffers + NUM_BUFFER_PARTITIONS entries.
     487             :      */
     488        3328 :     InitBufTable(NBuffers + NUM_BUFFER_PARTITIONS);
     489             : 
     490             :     /*
     491             :      * Get or create the shared strategy control block
     492             :      */
     493        3328 :     StrategyControl = (BufferStrategyControl *)
     494        3328 :         ShmemInitStruct("Buffer Strategy Status",
     495             :                         sizeof(BufferStrategyControl),
     496             :                         &found);
     497             : 
     498        3328 :     if (!found)
     499             :     {
     500             :         /*
     501             :          * Only done once, usually in postmaster
     502             :          */
     503             :         Assert(init);
     504             : 
     505        3328 :         SpinLockInit(&StrategyControl->buffer_strategy_lock);
     506             : 
     507             :         /*
     508             :          * Grab the whole linked list of free buffers for our strategy. We
     509             :          * assume it was previously set up by InitBufferPool().
     510             :          */
     511        3328 :         StrategyControl->firstFreeBuffer = 0;
     512        3328 :         StrategyControl->lastFreeBuffer = NBuffers - 1;
     513             : 
     514             :         /* Initialize the clock sweep pointer */
     515        3328 :         pg_atomic_init_u32(&StrategyControl->nextVictimBuffer, 0);
     516             : 
     517             :         /* Clear statistics */
     518        3328 :         StrategyControl->completePasses = 0;
     519        3328 :         pg_atomic_init_u32(&StrategyControl->numBufferAllocs, 0);
     520             : 
     521             :         /* No pending notification */
     522        3328 :         StrategyControl->bgwprocno = -1;
     523             :     }
     524             :     else
     525             :         Assert(!init);
     526        3328 : }
     527             : 
     528             : 
     529             : /* ----------------------------------------------------------------
     530             :  *              Backend-private buffer ring management
     531             :  * ----------------------------------------------------------------
     532             :  */
     533             : 
     534             : 
     535             : /*
     536             :  * GetAccessStrategy -- create a BufferAccessStrategy object
     537             :  *
     538             :  * The object is allocated in the current memory context.
     539             :  */
     540             : BufferAccessStrategy
     541      203380 : GetAccessStrategy(BufferAccessStrategyType btype)
     542             : {
     543             :     BufferAccessStrategy strategy;
     544             :     int         ring_size;
     545             : 
     546             :     /*
     547             :      * Select ring size to use.  See buffer/README for rationales.
     548             :      *
     549             :      * Note: if you change the ring size for BAS_BULKREAD, see also
     550             :      * SYNC_SCAN_REPORT_INTERVAL in access/heap/syncscan.c.
     551             :      */
     552      203380 :     switch (btype)
     553             :     {
     554           0 :         case BAS_NORMAL:
     555             :             /* if someone asks for NORMAL, just give 'em a "default" object */
     556           0 :             return NULL;
     557             : 
     558      107872 :         case BAS_BULKREAD:
     559      107872 :             ring_size = 256 * 1024 / BLCKSZ;
     560      107872 :             break;
     561       85974 :         case BAS_BULKWRITE:
     562       85974 :             ring_size = 16 * 1024 * 1024 / BLCKSZ;
     563       85974 :             break;
     564        9534 :         case BAS_VACUUM:
     565        9534 :             ring_size = 256 * 1024 / BLCKSZ;
     566        9534 :             break;
     567             : 
     568           0 :         default:
     569           0 :             elog(ERROR, "unrecognized buffer access strategy: %d",
     570             :                  (int) btype);
     571             :             return NULL;        /* keep compiler quiet */
     572             :     }
     573             : 
     574             :     /* Make sure ring isn't an undue fraction of shared buffers */
     575      203380 :     ring_size = Min(NBuffers / 8, ring_size);
     576             : 
     577             :     /* Allocate the object and initialize all elements to zeroes */
     578             :     strategy = (BufferAccessStrategy)
     579      203380 :         palloc0(offsetof(BufferAccessStrategyData, buffers) +
     580             :                 ring_size * sizeof(Buffer));
     581             : 
     582             :     /* Set fields that don't start out zero */
     583      203380 :     strategy->btype = btype;
     584      203380 :     strategy->ring_size = ring_size;
     585             : 
     586      203380 :     return strategy;
     587             : }
     588             : 
     589             : /*
     590             :  * FreeAccessStrategy -- release a BufferAccessStrategy object
     591             :  *
     592             :  * A simple pfree would do at the moment, but we would prefer that callers
     593             :  * don't assume that much about the representation of BufferAccessStrategy.
     594             :  */
     595             : void
     596       20634 : FreeAccessStrategy(BufferAccessStrategy strategy)
     597             : {
     598             :     /* don't crash if called on a "default" strategy */
     599       20634 :     if (strategy != NULL)
     600       20634 :         pfree(strategy);
     601       20634 : }
     602             : 
     603             : /*
     604             :  * GetBufferFromRing -- returns a buffer from the ring, or NULL if the
     605             :  *      ring is empty.
     606             :  *
     607             :  * The bufhdr spin lock is held on the returned buffer.
     608             :  */
     609             : static BufferDesc *
     610     1167576 : GetBufferFromRing(BufferAccessStrategy strategy, uint32 *buf_state)
     611             : {
     612             :     BufferDesc *buf;
     613             :     Buffer      bufnum;
     614             :     uint32      local_buf_state;    /* to avoid repeated (de-)referencing */
     615             : 
     616             : 
     617             :     /* Advance to next ring slot */
     618     1167576 :     if (++strategy->current >= strategy->ring_size)
     619       40336 :         strategy->current = 0;
     620             : 
     621             :     /*
     622             :      * If the slot hasn't been filled yet, tell the caller to allocate a new
     623             :      * buffer with the normal allocation strategy.  He will then fill this
     624             :      * slot by calling AddBufferToRing with the new buffer.
     625             :      */
     626     1167576 :     bufnum = strategy->buffers[strategy->current];
     627     1167576 :     if (bufnum == InvalidBuffer)
     628             :     {
     629      618758 :         strategy->current_was_in_ring = false;
     630      618758 :         return NULL;
     631             :     }
     632             : 
     633             :     /*
     634             :      * If the buffer is pinned we cannot use it under any circumstances.
     635             :      *
     636             :      * If usage_count is 0 or 1 then the buffer is fair game (we expect 1,
     637             :      * since our own previous usage of the ring element would have left it
     638             :      * there, but it might've been decremented by clock sweep since then). A
     639             :      * higher usage_count indicates someone else has touched the buffer, so we
     640             :      * shouldn't re-use it.
     641             :      */
     642      548818 :     buf = GetBufferDescriptor(bufnum - 1);
     643      548818 :     local_buf_state = LockBufHdr(buf);
     644      548818 :     if (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0
     645      545714 :         && BUF_STATE_GET_USAGECOUNT(local_buf_state) <= 1)
     646             :     {
     647      540892 :         strategy->current_was_in_ring = true;
     648      540892 :         *buf_state = local_buf_state;
     649      540892 :         return buf;
     650             :     }
     651        7926 :     UnlockBufHdr(buf, local_buf_state);
     652             : 
     653             :     /*
     654             :      * Tell caller to allocate a new buffer with the normal allocation
     655             :      * strategy.  He'll then replace this ring element via AddBufferToRing.
     656             :      */
     657        7926 :     strategy->current_was_in_ring = false;
     658        7926 :     return NULL;
     659             : }
     660             : 
     661             : /*
     662             :  * AddBufferToRing -- add a buffer to the buffer ring
     663             :  *
     664             :  * Caller must hold the buffer header spinlock on the buffer.  Since this
     665             :  * is called with the spinlock held, it had better be quite cheap.
     666             :  */
     667             : static void
     668      626684 : AddBufferToRing(BufferAccessStrategy strategy, BufferDesc *buf)
     669             : {
     670      626684 :     strategy->buffers[strategy->current] = BufferDescriptorGetBuffer(buf);
     671      626684 : }
     672             : 
     673             : /*
     674             :  * StrategyRejectBuffer -- consider rejecting a dirty buffer
     675             :  *
     676             :  * When a nondefault strategy is used, the buffer manager calls this function
     677             :  * when it turns out that the buffer selected by StrategyGetBuffer needs to
     678             :  * be written out and doing so would require flushing WAL too.  This gives us
     679             :  * a chance to choose a different victim.
     680             :  *
     681             :  * Returns true if buffer manager should ask for a new victim, and false
     682             :  * if this buffer should be written and re-used.
     683             :  */
     684             : bool
     685       20076 : StrategyRejectBuffer(BufferAccessStrategy strategy, BufferDesc *buf)
     686             : {
     687             :     /* We only do this in bulkread mode */
     688       20076 :     if (strategy->btype != BAS_BULKREAD)
     689        3348 :         return false;
     690             : 
     691             :     /* Don't muck with behavior of normal buffer-replacement strategy */
     692       32852 :     if (!strategy->current_was_in_ring ||
     693       16124 :         strategy->buffers[strategy->current] != BufferDescriptorGetBuffer(buf))
     694         604 :         return false;
     695             : 
     696             :     /*
     697             :      * Remove the dirty buffer from the ring; necessary to prevent infinite
     698             :      * loop if all ring members are dirty.
     699             :      */
     700       16124 :     strategy->buffers[strategy->current] = InvalidBuffer;
     701             : 
     702       16124 :     return true;
     703             : }

Generated by: LCOV version 1.14