PostgreSQL Source Code git master
Loading...
Searching...
No Matches
freelist.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * freelist.c
4 * routines for managing the buffer pool's replacement strategy.
5 *
6 *
7 * Portions Copyright (c) 1996-2026, 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 "pgstat.h"
19#include "port/atomics.h"
21#include "storage/bufmgr.h"
22#include "storage/proc.h"
23#include "storage/shmem.h"
24#include "storage/subsystems.h"
25
26#define INT_ACCESS_ONCE(var) ((int)(*((volatile int *)&(var))))
27
28
29/*
30 * The shared freelist control information.
31 */
32typedef struct
33{
34 /* Spinlock: protects the values below */
36
37 /*
38 * clock-sweep hand: index of next buffer to consider grabbing. Note that
39 * this isn't a concrete buffer - we only ever increase the value. So, to
40 * get an actual buffer, it needs to be used modulo NBuffers.
41 */
43
44 /*
45 * Statistics. These counters should be wide enough that they can't
46 * overflow during a single bgwriter cycle.
47 */
48 uint32 completePasses; /* Complete cycles of the clock-sweep */
49 pg_atomic_uint32 numBufferAllocs; /* Buffers allocated since last reset */
50
51 /*
52 * Bgworker process to be notified upon activity or -1 if none. See
53 * StrategyNotifyBgWriter.
54 */
57
58/* Pointers to shared state */
60
61static void StrategyCtlShmemRequest(void *arg);
62static void StrategyCtlShmemInit(void *arg);
63
68
69/*
70 * Private (non-shared) state for managing a ring of shared buffers to re-use.
71 * This is currently the only kind of BufferAccessStrategy object, but someday
72 * we might have more kinds.
73 */
75{
76 /* Overall strategy type */
78 /* Number of elements in buffers[] array */
80
81 /*
82 * Index of the "current" slot in the ring, ie, the one most recently
83 * returned by GetBufferFromRing.
84 */
86
87 /*
88 * Array of buffer numbers. InvalidBuffer (that is, zero) indicates we
89 * have not yet selected a buffer for this ring slot. For allocation
90 * simplicity this is palloc'd together with the fixed fields of the
91 * struct.
92 */
95
96
97/* Prototypes for internal functions */
100static void AddBufferToRing(BufferAccessStrategy strategy,
101 BufferDesc *buf);
102
103/*
104 * ClockSweepTick - Helper routine for StrategyGetBuffer()
105 *
106 * Move the clock hand one buffer ahead of its current position and return the
107 * id of the buffer now under the hand.
108 */
109static inline uint32
111{
113
114 /*
115 * Atomically move hand ahead one buffer - if there's several processes
116 * doing this, this can lead to buffers being returned slightly out of
117 * apparent order.
118 */
119 victim =
121
122 if (victim >= NBuffers)
123 {
125
126 /* always wrap what we look up in BufferDescriptors */
128
129 /*
130 * If we're the one that just caused a wraparound, force
131 * completePasses to be incremented while holding the spinlock. We
132 * need the spinlock so StrategySyncStart() can return a consistent
133 * value consisting of nextVictimBuffer and completePasses.
134 */
135 if (victim == 0)
136 {
139 bool success = false;
140
142
143 while (!success)
144 {
145 /*
146 * Acquire the spinlock while increasing completePasses. That
147 * allows other readers to read nextVictimBuffer and
148 * completePasses in a consistent manner which is required for
149 * StrategySyncStart(). In theory delaying the increment
150 * could lead to an overflow of nextVictimBuffers, but that's
151 * highly unlikely and wouldn't be particularly harmful.
152 */
154
156
158 &expected, wrapped);
159 if (success)
162 }
163 }
164 }
165 return victim;
166}
167
168/*
169 * StrategyGetBuffer
170 *
171 * Called by the bufmgr to get the next candidate buffer to use in
172 * GetVictimBuffer(). The only hard requirement GetVictimBuffer() has is that
173 * the selected buffer must not currently be pinned by anyone.
174 *
175 * strategy is a BufferAccessStrategy object, or NULL for default strategy.
176 *
177 * It is the callers responsibility to ensure the buffer ownership can be
178 * tracked via TrackNewBufferPin().
179 *
180 * The buffer is pinned and marked as owned, using TrackNewBufferPin(),
181 * before returning.
182 */
185{
187 int bgwprocno;
188 int trycounter;
189
190 *from_ring = false;
191
192 /*
193 * If given a strategy object, see whether it can select a buffer. We
194 * assume strategy objects don't need buffer_strategy_lock.
195 */
196 if (strategy != NULL)
197 {
198 buf = GetBufferFromRing(strategy, buf_state);
199 if (buf != NULL)
200 {
201 *from_ring = true;
202 return buf;
203 }
204 }
205
206 /*
207 * If asked, we need to waken the bgwriter. Since we don't want to rely on
208 * a spinlock for this we force a read from shared memory once, and then
209 * set the latch based on that value. We need to go through that length
210 * because otherwise bgwprocno might be reset while/after we check because
211 * the compiler might just reread from memory.
212 *
213 * This can possibly set the latch of the wrong process if the bgwriter
214 * dies in the wrong moment. But since PGPROC->procLatch is never
215 * deallocated the worst consequence of that is that we set the latch of
216 * some arbitrary process.
217 */
219 if (bgwprocno != -1)
220 {
221 /* reset bgwprocno first, before setting the latch */
223
224 /*
225 * Not acquiring ProcArrayLock here which is slightly icky. It's
226 * actually fine because procLatch isn't ever freed, so we just can
227 * potentially set the wrong process' (or no process') latch.
228 */
229 SetLatch(&GetPGProcByNumber(bgwprocno)->procLatch);
230 }
231
232 /*
233 * We count buffer allocation requests so that the bgwriter can estimate
234 * the rate of buffer consumption. Note that buffers recycled by a
235 * strategy object are intentionally not counted here.
236 */
238
239 /* Use the "clock sweep" algorithm to find a free buffer */
241 for (;;)
242 {
245
247
248 /*
249 * Check whether the buffer can be used and pin it if so. Do this
250 * using a CAS loop, to avoid having to lock the buffer header.
251 */
253 for (;;)
254 {
256
257 /*
258 * If the buffer is pinned or has a nonzero usage_count, we cannot
259 * use it; decrement the usage_count (unless pinned) and keep
260 * scanning.
261 */
262
264 {
265 if (--trycounter == 0)
266 {
267 /*
268 * We've scanned all the buffers without making any state
269 * changes, so all the buffers are pinned (or were when we
270 * looked at them). We could hope that someone will free
271 * one eventually, but it's probably better to fail than
272 * to risk getting stuck in an infinite loop.
273 */
274 elog(ERROR, "no unpinned buffers available");
275 }
276 break;
277 }
278
279 /* See equivalent code in PinBuffer() */
281 {
283 continue;
284 }
285
287 {
289
292 {
294 break;
295 }
296 }
297 else
298 {
299 /* pin the buffer if the CAS succeeds */
301
304 {
305 /* Found a usable buffer */
306 if (strategy != NULL)
307 AddBufferToRing(strategy, buf);
309
311
312 return buf;
313 }
314 }
315 }
316 }
317}
318
319/*
320 * StrategySyncStart -- tell BgBufferSync where to start syncing
321 *
322 * The result is the buffer index of the best buffer to sync first.
323 * BgBufferSync() will proceed circularly around the buffer array from there.
324 *
325 * In addition, we return the completed-pass count (which is effectively
326 * the higher-order bits of nextVictimBuffer) and the count of recent buffer
327 * allocs if non-NULL pointers are passed. The alloc count is reset after
328 * being read.
329 */
330int
332{
333 uint32 nextVictimBuffer;
334 int result;
335
338 result = nextVictimBuffer % NBuffers;
339
340 if (complete_passes)
341 {
343
344 /*
345 * Additionally add the number of wraparounds that happened before
346 * completePasses could be incremented. C.f. ClockSweepTick().
347 */
348 *complete_passes += nextVictimBuffer / NBuffers;
349 }
350
351 if (num_buf_alloc)
352 {
354 }
356 return result;
357}
358
359/*
360 * StrategyNotifyBgWriter -- set or clear allocation notification latch
361 *
362 * If bgwprocno isn't -1, the next invocation of StrategyGetBuffer will
363 * set that latch. Pass -1 to clear the pending notification before it
364 * happens. This feature is used by the bgwriter process to wake itself up
365 * from hibernation, and is not meant for anybody else to use.
366 */
367void
369{
370 /*
371 * We acquire buffer_strategy_lock just to ensure that the store appears
372 * atomic to StrategyGetBuffer. The bgwriter should call this rather
373 * infrequently, so there's no performance penalty from being safe.
374 */
376 StrategyControl->bgwprocno = bgwprocno;
378}
379
380
381/*
382 * StrategyCtlShmemRequest -- request shared memory for the buffer
383 * cache replacement strategy.
384 */
385static void
387{
388 ShmemRequestStruct(.name = "Buffer Strategy Status",
389 .size = sizeof(BufferStrategyControl),
390 .ptr = (void **) &StrategyControl
391 );
392}
393
394/*
395 * StrategyCtlShmemInit -- initialize the buffer cache replacement strategy.
396 */
397static void
399{
401
402 /* Initialize the clock-sweep pointer */
404
405 /* Clear statistics */
408
409 /* No pending notification */
411}
412
413
414/* ----------------------------------------------------------------
415 * Backend-private buffer ring management
416 * ----------------------------------------------------------------
417 */
418
419
420/*
421 * GetAccessStrategy -- create a BufferAccessStrategy object
422 *
423 * The object is allocated in the current memory context.
424 */
427{
428 int ring_size_kb;
429
430 /*
431 * Select ring size to use. See buffer/README for rationales.
432 *
433 * Note: if you change the ring size for BAS_BULKREAD, see also
434 * SYNC_SCAN_REPORT_INTERVAL in access/heap/syncscan.c.
435 */
436 switch (btype)
437 {
438 case BAS_NORMAL:
439 /* if someone asks for NORMAL, just give 'em a "default" object */
440 return NULL;
441
442 case BAS_BULKREAD:
443 {
444 int ring_max_kb;
445
446 /*
447 * The ring always needs to be large enough to allow some
448 * separation in time between providing a buffer to the user
449 * of the strategy and that buffer being reused. Otherwise the
450 * user's pin will prevent reuse of the buffer, even without
451 * concurrent activity.
452 *
453 * We also need to ensure the ring always is large enough for
454 * SYNC_SCAN_REPORT_INTERVAL, as noted above.
455 *
456 * Thus we start out a minimal size and increase the size
457 * further if appropriate.
458 */
459 ring_size_kb = 256;
460
461 /*
462 * There's no point in a larger ring if we won't be allowed to
463 * pin sufficiently many buffers. But we never limit to less
464 * than the minimal size above.
465 */
466 ring_max_kb = GetPinLimit() * (BLCKSZ / 1024);
468
469 /*
470 * We would like the ring to additionally have space for the
471 * configured degree of IO concurrency. While being read in,
472 * buffers can obviously not yet be reused.
473 *
474 * Each IO can be up to io_combine_limit blocks large, and we
475 * want to start up to effective_io_concurrency IOs.
476 *
477 * Note that effective_io_concurrency may be 0, which disables
478 * AIO.
479 */
480 ring_size_kb += (BLCKSZ / 1024) *
482
485 break;
486 }
487 case BAS_BULKWRITE:
488 ring_size_kb = 16 * 1024;
489 break;
490 case BAS_VACUUM:
491 ring_size_kb = 2048;
492 break;
493
494 default:
495 elog(ERROR, "unrecognized buffer access strategy: %d",
496 (int) btype);
497 return NULL; /* keep compiler quiet */
498 }
499
501}
502
503/*
504 * GetAccessStrategyWithSize -- create a BufferAccessStrategy object with a
505 * number of buffers equivalent to the passed in size.
506 *
507 * If the given ring size is 0, no BufferAccessStrategy will be created and
508 * the function will return NULL. ring_size_kb must not be negative.
509 */
512{
513 int ring_buffers;
514 BufferAccessStrategy strategy;
515
516 Assert(ring_size_kb >= 0);
517
518 /* Figure out how many buffers ring_size_kb is */
519 ring_buffers = ring_size_kb / (BLCKSZ / 1024);
520
521 /* 0 means unlimited, so no BufferAccessStrategy required */
522 if (ring_buffers == 0)
523 return NULL;
524
525 /* Cap to 1/8th of shared_buffers */
527
528 /* NBuffers should never be less than 16, so this shouldn't happen */
529 Assert(ring_buffers > 0);
530
531 /* Allocate the object and initialize all elements to zeroes */
532 strategy = (BufferAccessStrategy)
534 ring_buffers * sizeof(Buffer));
535
536 /* Set fields that don't start out zero */
537 strategy->btype = btype;
538 strategy->nbuffers = ring_buffers;
539
540 return strategy;
541}
542
543/*
544 * GetAccessStrategyBufferCount -- an accessor for the number of buffers in
545 * the ring
546 *
547 * Returns 0 on NULL input to match behavior of GetAccessStrategyWithSize()
548 * returning NULL with 0 size.
549 */
550int
552{
553 if (strategy == NULL)
554 return 0;
555
556 return strategy->nbuffers;
557}
558
559/*
560 * GetAccessStrategyPinLimit -- get cap of number of buffers that should be pinned
561 *
562 * When pinning extra buffers to look ahead, users of a ring-based strategy are
563 * in danger of pinning too much of the ring at once while performing look-ahead.
564 * For some strategies, that means "escaping" from the ring, and in others it
565 * means forcing dirty data to disk very frequently with associated WAL
566 * flushing. Since external code has no insight into any of that, allow
567 * individual strategy types to expose a clamp that should be applied when
568 * deciding on a maximum number of buffers to pin at once.
569 *
570 * Callers should combine this number with other relevant limits and take the
571 * minimum.
572 */
573int
575{
576 if (strategy == NULL)
577 return NBuffers;
578
579 switch (strategy->btype)
580 {
581 case BAS_BULKREAD:
582
583 /*
584 * Since BAS_BULKREAD uses StrategyRejectBuffer(), dirty buffers
585 * shouldn't be a problem and the caller is free to pin up to the
586 * entire ring at once.
587 */
588 return strategy->nbuffers;
589
590 default:
591
592 /*
593 * Tell caller not to pin more than half the buffers in the ring.
594 * This is a trade-off between look ahead distance and deferring
595 * writeback and associated WAL traffic.
596 */
597 return strategy->nbuffers / 2;
598 }
599}
600
601/*
602 * FreeAccessStrategy -- release a BufferAccessStrategy object
603 *
604 * A simple pfree would do at the moment, but we would prefer that callers
605 * don't assume that much about the representation of BufferAccessStrategy.
606 */
607void
609{
610 /* don't crash if called on a "default" strategy */
611 if (strategy != NULL)
612 pfree(strategy);
613}
614
615/*
616 * GetBufferFromRing -- returns a buffer from the ring, or NULL if the
617 * ring is empty / not usable.
618 *
619 * The buffer is pinned and marked as owned, using TrackNewBufferPin(), before
620 * returning.
621 */
622static BufferDesc *
624{
628 uint64 local_buf_state; /* to avoid repeated (de-)referencing */
629
630
631 /* Advance to next ring slot */
632 if (++strategy->current >= strategy->nbuffers)
633 strategy->current = 0;
634
635 /*
636 * If the slot hasn't been filled yet, tell the caller to allocate a new
637 * buffer with the normal allocation strategy. He will then fill this
638 * slot by calling AddBufferToRing with the new buffer.
639 */
640 bufnum = strategy->buffers[strategy->current];
641 if (bufnum == InvalidBuffer)
642 return NULL;
643
645
646 /*
647 * Check whether the buffer can be used and pin it if so. Do this using a
648 * CAS loop, to avoid having to lock the buffer header.
649 */
651 for (;;)
652 {
654
655 /*
656 * If the buffer is pinned we cannot use it under any circumstances.
657 *
658 * If usage_count is 0 or 1 then the buffer is fair game (we expect 1,
659 * since our own previous usage of the ring element would have left it
660 * there, but it might've been decremented by clock-sweep since then).
661 * A higher usage_count indicates someone else has touched the buffer,
662 * so we shouldn't re-use it.
663 */
666 break;
667
668 /* See equivalent code in PinBuffer() */
670 {
672 continue;
673 }
674
675 /* pin the buffer if the CAS succeeds */
677
680 {
682
684 return buf;
685 }
686 }
687
688 /*
689 * Tell caller to allocate a new buffer with the normal allocation
690 * strategy. He'll then replace this ring element via AddBufferToRing.
691 */
692 return NULL;
693}
694
695/*
696 * AddBufferToRing -- add a buffer to the buffer ring
697 *
698 * Caller must hold the buffer header spinlock on the buffer. Since this
699 * is called with the spinlock held, it had better be quite cheap.
700 */
701static void
706
707/*
708 * Utility function returning the IOContext of a given BufferAccessStrategy's
709 * strategy ring.
710 */
713{
714 if (!strategy)
715 return IOCONTEXT_NORMAL;
716
717 switch (strategy->btype)
718 {
719 case BAS_NORMAL:
720
721 /*
722 * Currently, GetAccessStrategy() returns NULL for
723 * BufferAccessStrategyType BAS_NORMAL, so this case is
724 * unreachable.
725 */
727 return IOCONTEXT_NORMAL;
728 case BAS_BULKREAD:
729 return IOCONTEXT_BULKREAD;
730 case BAS_BULKWRITE:
731 return IOCONTEXT_BULKWRITE;
732 case BAS_VACUUM:
733 return IOCONTEXT_VACUUM;
734 }
735
736 elog(ERROR, "unrecognized BufferAccessStrategyType: %d", strategy->btype);
738}
739
740/*
741 * StrategyRejectBuffer -- consider rejecting a dirty buffer
742 *
743 * When a nondefault strategy is used, the buffer manager calls this function
744 * when it turns out that the buffer selected by StrategyGetBuffer needs to
745 * be written out and doing so would require flushing WAL too. This gives us
746 * a chance to choose a different victim.
747 *
748 * Returns true if buffer manager should ask for a new victim, and false
749 * if this buffer should be written and re-used.
750 */
751bool
753{
754 /* We only do this in bulkread mode */
755 if (strategy->btype != BAS_BULKREAD)
756 return false;
757
758 /* Don't muck with behavior of normal buffer-replacement strategy */
759 if (!from_ring ||
760 strategy->buffers[strategy->current] != BufferDescriptorGetBuffer(buf))
761 return false;
762
763 /*
764 * Remove the dirty buffer from the ring; necessary to prevent infinite
765 * loop if all ring members are dirty.
766 */
767 strategy->buffers[strategy->current] = InvalidBuffer;
768
769 return true;
770}
static bool pg_atomic_compare_exchange_u32(volatile pg_atomic_uint32 *ptr, uint32 *expected, uint32 newval)
Definition atomics.h:349
static bool pg_atomic_compare_exchange_u64(volatile pg_atomic_uint64 *ptr, uint64 *expected, uint64 newval)
Definition atomics.h:522
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition atomics.h:219
static uint32 pg_atomic_fetch_add_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition atomics.h:366
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition atomics.h:237
static uint32 pg_atomic_exchange_u32(volatile pg_atomic_uint32 *ptr, uint32 newval)
Definition atomics.h:330
static uint64 pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr)
Definition atomics.h:467
int Buffer
Definition buf.h:23
struct BufferAccessStrategyData * BufferAccessStrategy
Definition buf.h:44
#define InvalidBuffer
Definition buf.h:25
#define BUF_REFCOUNT_ONE
#define BM_LOCKED
#define BUF_STATE_GET_USAGECOUNT(state)
#define BUF_USAGECOUNT_ONE
#define BUF_STATE_GET_REFCOUNT(state)
static BufferDesc * GetBufferDescriptor(uint32 id)
static Buffer BufferDescriptorGetBuffer(const BufferDesc *bdesc)
pg_noinline uint64 WaitBufHdrUnlocked(BufferDesc *buf)
Definition bufmgr.c:7566
int effective_io_concurrency
Definition bufmgr.c:200
int io_combine_limit
Definition bufmgr.c:215
void TrackNewBufferPin(Buffer buf)
Definition bufmgr.c:3512
uint32 GetPinLimit(void)
Definition bufmgr.c:2686
BufferAccessStrategyType
Definition bufmgr.h:35
@ BAS_BULKREAD
Definition bufmgr.h:37
@ BAS_NORMAL
Definition bufmgr.h:36
@ BAS_VACUUM
Definition bufmgr.h:40
@ BAS_BULKWRITE
Definition bufmgr.h:39
#define Min(x, y)
Definition c.h:1091
#define Max(x, y)
Definition c.h:1085
#define Assert(condition)
Definition c.h:943
#define FLEXIBLE_ARRAY_MEMBER
Definition c.h:558
uint64_t uint64
Definition c.h:625
#define pg_unreachable()
Definition c.h:367
#define unlikely(x)
Definition c.h:438
uint32_t uint32
Definition c.h:624
uint32 result
Datum arg
Definition elog.c:1322
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
int GetAccessStrategyPinLimit(BufferAccessStrategy strategy)
Definition freelist.c:574
int StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc)
Definition freelist.c:331
const ShmemCallbacks StrategyCtlShmemCallbacks
Definition freelist.c:64
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition freelist.c:426
static BufferStrategyControl * StrategyControl
Definition freelist.c:59
BufferDesc * StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_ring)
Definition freelist.c:184
static uint32 ClockSweepTick(void)
Definition freelist.c:110
BufferAccessStrategy GetAccessStrategyWithSize(BufferAccessStrategyType btype, int ring_size_kb)
Definition freelist.c:511
static void AddBufferToRing(BufferAccessStrategy strategy, BufferDesc *buf)
Definition freelist.c:702
int GetAccessStrategyBufferCount(BufferAccessStrategy strategy)
Definition freelist.c:551
void FreeAccessStrategy(BufferAccessStrategy strategy)
Definition freelist.c:608
#define INT_ACCESS_ONCE(var)
Definition freelist.c:26
void StrategyNotifyBgWriter(int bgwprocno)
Definition freelist.c:368
IOContext IOContextForStrategy(BufferAccessStrategy strategy)
Definition freelist.c:712
static void StrategyCtlShmemRequest(void *arg)
Definition freelist.c:386
bool StrategyRejectBuffer(BufferAccessStrategy strategy, BufferDesc *buf, bool from_ring)
Definition freelist.c:752
static BufferDesc * GetBufferFromRing(BufferAccessStrategy strategy, uint64 *buf_state)
Definition freelist.c:623
static void StrategyCtlShmemInit(void *arg)
Definition freelist.c:398
int NBuffers
Definition globals.c:144
static bool success
Definition initdb.c:188
void SetLatch(Latch *latch)
Definition latch.c:290
void pfree(void *pointer)
Definition mcxt.c:1616
void * palloc0(Size size)
Definition mcxt.c:1417
static char buf[DEFAULT_XLOG_SEG_SIZE]
IOContext
Definition pgstat.h:289
@ IOCONTEXT_NORMAL
Definition pgstat.h:293
@ IOCONTEXT_VACUUM
Definition pgstat.h:294
@ IOCONTEXT_BULKREAD
Definition pgstat.h:290
@ IOCONTEXT_BULKWRITE
Definition pgstat.h:291
static int fb(int x)
#define GetPGProcByNumber(n)
Definition proc.h:504
#define ShmemRequestStruct(...)
Definition shmem.h:176
static void SpinLockRelease(volatile slock_t *lock)
Definition spin.h:62
static void SpinLockAcquire(volatile slock_t *lock)
Definition spin.h:56
static void SpinLockInit(volatile slock_t *lock)
Definition spin.h:50
BufferAccessStrategyType btype
Definition freelist.c:77
Buffer buffers[FLEXIBLE_ARRAY_MEMBER]
Definition freelist.c:93
pg_atomic_uint32 nextVictimBuffer
Definition freelist.c:42
pg_atomic_uint32 numBufferAllocs
Definition freelist.c:49
slock_t buffer_strategy_lock
Definition freelist.c:35
ShmemRequestCallback request_fn
Definition shmem.h:133
const char * name