1/*-------------------------------------------------------------------------
4 * Generational allocator definitions.
6 * Generation is a custom MemoryContext implementation designed for cases of
7 * chunks with similar lifespan.
9 * Portions Copyright (c) 2017-2025, PostgreSQL Global Development Group
12 * src/backend/utils/mmgr/generation.c
15 * This memory context is based on the assumption that the chunks are freed
16 * roughly in the same order as they were allocated (FIFO), or in groups with
17 * similar lifespan (generations - hence the name of the context). This is
18 * typical for various queue-like use cases, i.e. when tuples are constructed,
19 * processed and then thrown away.
21 * The memory context uses a very simple approach to free space management.
22 * Instead of a complex global freelist, each block tracks a number
23 * of allocated and freed chunks. The block is classed as empty when the
24 * number of free chunks is equal to the number of allocated chunks. When
25 * this occurs, instead of freeing the block, we try to "recycle" it, i.e.
26 * reuse it for new allocations. This is done by setting the block in the
27 * context's 'freeblock' field. If the freeblock field is already occupied
28 * by another free block we simply return the newly empty block to malloc.
30 * This approach to free blocks requires fewer malloc/free calls for truly
31 * first allocated, first free'd allocation patterns.
33 *-------------------------------------------------------------------------
46 #define Generation_BLOCKHDRSZ MAXALIGN(sizeof(GenerationBlock))
47 #define Generation_CHUNKHDRSZ sizeof(MemoryChunk)
48 #define FIRST_BLOCKHDRSZ (MAXALIGN(sizeof(GenerationContext)) + \
49 Generation_BLOCKHDRSZ)
51 #define Generation_CHUNK_FRACTION 8
58 * GenerationContext is a simple memory context not reusing allocated chunks,
59 * and freeing blocks once all chunks are freed.
65 /* Generational context parameters */
73 * recycled, or NULL if there's no such block. */
79 * GenerationBlock is the unit of memory that is obtained by generation.c
80 * from malloc(). It contains zero or more MemoryChunks, which are the
81 * units requested by palloc() and freed by pfree(). MemoryChunks cannot
82 * be returned to malloc() individually, instead pfree() updates the free
83 * counter of the block and when all chunks in a block are free the whole
84 * block can be returned to malloc().
86 * GenerationBlock is the header data for a block --- the usable space
87 * within the block begins at the next alignment boundary.
94 int nchunks;
/* number of chunks in the block */
95 int nfree;
/* number of free chunks */
96 char *
freeptr;
/* start of free space in this block */
97 char *
endptr;
/* end of space in this block */
102 * True iff set is valid generation set.
104 #define GenerationIsValid(set) \
105 ((set) && IsA(set, GenerationContext))
108 * GenerationBlockIsValid
109 * True iff block is valid block of generation set.
111 #define GenerationBlockIsValid(block) \
112 ((block) && GenerationIsValid((block)->context))
115 * GenerationBlockIsEmpty
116 * True iff block contains no chunks
118 #define GenerationBlockIsEmpty(b) ((b)->nchunks == 0)
121 * We always store external chunks on a dedicated block. This makes fetching
122 * the block from an external chunk easy since it's always the first and only
123 * chunk on the block.
125 #define ExternalChunkGetBlock(chunk) \
126 (GenerationBlock *) ((char *) chunk - Generation_BLOCKHDRSZ)
128/* Obtain the keeper block for a generation context */
129 #define KeeperBlock(set) \
130 ((GenerationBlock *) (((char *) set) + \
131 MAXALIGN(sizeof(GenerationContext))))
133/* Check if the block is the keeper block of the given generation context */
134 #define IsKeeperBlock(set, block) ((block) == (KeeperBlock(set)))
136/* Inlined helper functions */
152 * GenerationContextCreate
153 * Create a new Generation context.
155 * parent: parent context, or NULL if top-level context
156 * name: name of context (must be statically allocated)
157 * minContextSize: minimum context size
158 * initBlockSize: initial allocation block size
159 * maxBlockSize: maximum allocation block size
173 /* ensure MemoryChunk's size is properly maxaligned */
175 "sizeof(MemoryChunk) is not maxaligned");
178 * First, validate allocation parameters. Asserts seem sufficient because
179 * nobody varies their parameters at runtime. We somewhat arbitrarily
180 * enforce a minimum 1K block size. We restrict the maximum block size to
181 * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
182 * regards to addressing the offset between the chunk and the block that
183 * the chunk is stored on. We would be unable to store the offset between
184 * the chunk and block for any chunks that were beyond
185 * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
189 initBlockSize >= 1024);
191 maxBlockSize >= initBlockSize &&
193 Assert(minContextSize == 0 ||
194 (minContextSize ==
MAXALIGN(minContextSize) &&
195 minContextSize >= 1024 &&
196 minContextSize <= maxBlockSize));
199 /* Determine size of initial block */
202 if (minContextSize != 0)
203 allocSize =
Max(allocSize, minContextSize);
205 allocSize =
Max(allocSize, initBlockSize);
208 * Allocate the initial block. Unlike other generation.c blocks, it
209 * starts with the context header and its block header follows that.
216 (
errcode(ERRCODE_OUT_OF_MEMORY),
218 errdetail(
"Failed while creating memory context \"%s\".",
223 * Avoid writing code that can fail between here and MemoryContextCreate;
224 * we'd leak the header if we ereport in this stretch.
227 /* See comments about Valgrind interactions in aset.c */
229 /* This vchunk covers the GenerationContext and the keeper block header */
234 /* Fill in the initial block's block header */
236 /* determine the block size and initialize it */
240 /* add it to the doubly-linked list of blocks */
243 /* use it as the current allocation block */
246 /* No free block, yet */
249 /* Fill in GenerationContext-specific header fields */
255 * Compute the allocation chunk size limit for this context.
257 * Limit the maximum size a non-dedicated chunk can be so that we can fit
258 * at least Generation_CHUNK_FRACTION of chunks this big onto the maximum
259 * sized block. We must further limit this value so that it's no more
260 * than MEMORYCHUNK_MAX_VALUE. We're unable to have non-external chunks
261 * larger than that value as we store the chunk size in the MemoryChunk
262 * 'value' field in the call to MemoryChunkSetHdrMask().
269 /* Finally, do the type-independent part of context creation */
283 * Frees all memory which is allocated in the given set.
285 * The initial "keeper" block (which shares a malloc chunk with the context
286 * header) is not given back to the operating system though. In this way, we
287 * don't thrash malloc() when a context is repeatedly reset after small
298#ifdef MEMORY_CONTEXT_CHECKING
299 /* Check for corruption and leaks before freeing */
300 GenerationCheck(context);
304 * NULLify the free block pointer. We must do this before calling
305 * GenerationBlockFree as that function never expects to free the
321 * Instruct Valgrind to throw away all the vchunks associated with this
322 * context, except for the one covering the GenerationContext and
323 * keeper-block header. This gets rid of the vchunks for whatever user
324 * data is getting discarded by the context reset.
328 /* set it so new allocations to make use of the keeper block */
331 /* Reset block size allocation sequence, too */
334 /* Ensure there is only 1 item in the dlist */
341 * Free all memory which is allocated in the given context.
346 /* Reset to release all releasable GenerationBlocks */
349 /* Destroy the vpool -- see notes in aset.c */
352 /* And free the context header and keeper block */
357 * Helper for GenerationAlloc() that allocates an entire block for the chunk.
359 * GenerationAlloc()'s comment explains why this is separate.
372 /* validate 'size' is within the limits for the given 'flags' */
375#ifdef MEMORY_CONTEXT_CHECKING
376 /* ensure there's always space for the sentinel byte */
388 /* Make a vchunk covering the new block's header */
393 /* block with a single (used) chunk */
399 /* the block is completely full */
404 /* mark the MemoryChunk as externally managed */
407#ifdef MEMORY_CONTEXT_CHECKING
408 chunk->requested_size = size;
409 /* set mark to catch clobber of "unused" space */
410 Assert(size < chunk_size);
413#ifdef RANDOMIZE_ALLOCATED_MEMORY
414 /* fill the allocated space with junk */
418 /* add the block to the list of allocated blocks */
421 /* Ensure any padding bytes are marked NOACCESS. */
425 /* Disallow access to the chunk header. */
432 * Small helper for allocating a new chunk from a chunk, to avoid duplicating
433 * the code between GenerationAlloc() and GenerationAllocFromNewBlock().
441 /* validate we've been given a block with enough free space */
446 /* Prepare to initialize the chunk header. */
455#ifdef MEMORY_CONTEXT_CHECKING
456 chunk->requested_size = size;
457 /* set mark to catch clobber of "unused" space */
458 Assert(size < chunk_size);
461#ifdef RANDOMIZE_ALLOCATED_MEMORY
462 /* fill the allocated space with junk */
466 /* Ensure any padding bytes are marked NOACCESS. */
470 /* Disallow access to the chunk header. */
477 * Helper for GenerationAlloc() that allocates a new block and returns a chunk
480 * GenerationAlloc()'s comment explains why this is separate.
493 * The first such block has size initBlockSize, and we double the space in
494 * each succeeding block, but not more than maxBlockSize.
501 /* we'll need space for the chunk, chunk hdr and block hdr */
504 /* round the size up to the next power of 2 */
505 if (blksize < required_size)
513 /* Make a vchunk covering the new block's header */
518 /* initialize the new block */
521 /* add it to the doubly-linked list of blocks */
524 /* make this the current block */
532 * Returns a pointer to allocated memory of given size or raises an ERROR
533 * on allocation failure, or returns NULL when flags contains
536 * No request may exceed:
537 * MAXALIGN_DOWN(SIZE_MAX) - Generation_BLOCKHDRSZ - Generation_CHUNKHDRSZ
538 * All callers use a much-lower limit.
540 * Note: when using valgrind, it doesn't matter how the returned allocation
541 * is marked, as mcxt.c will set it to UNDEFINED. In some paths we will
542 * return space that is marked NOACCESS - GenerationRealloc has to beware!
544 * This function should only contain the most common code paths. Everything
545 * else should be in pg_noinline helper functions, thus avoiding the overhead
546 * of creating a stack frame for the common cases. Allocating memory is often
547 * a bottleneck in many workloads, so avoiding stack frame setup is
548 * worthwhile. Helper functions should always directly return the newly
549 * allocated memory so that we can just return that address directly as a tail
562#ifdef MEMORY_CONTEXT_CHECKING
563 /* ensure there's always space for the sentinel byte */
570 * If requested size exceeds maximum for chunks we hand the request off to
571 * GenerationAllocLarge().
579 * Not an oversized chunk. We try to first make use of the current block,
580 * but if there's not enough space in it, instead of allocating a new
581 * block, we look to see if the empty freeblock has enough space. We
582 * don't try reusing the keeper block. If it's become empty we'll reuse
583 * that again only if the context is reset.
585 * We only try reusing the freeblock if we've no space for this allocation
586 * on the current block. When a freeblock exists, we'll switch to it once
587 * the first time we can't fit an allocation in the current block. We
588 * avoid ping-ponging between the two as we need to be careful not to
589 * fragment differently sized consecutive allocations between several
590 * blocks. Going between the two could cause fragmentation for FIFO
591 * workloads, which generation is meant to be good at.
599 /* freeblock, if set, must be empty */
602 /* check if we have a freeblock and if it's big enough */
603 if (freeblock != NULL &&
606 /* make the freeblock the current block */
608 set->
block = freeblock;
618 * No freeblock, or it's not big enough for this allocation. Make
625 /* The current block has space, so just allocate chunk there. */
630 * GenerationBlockInit
631 * Initializes 'block' assuming 'blksize'. Does not update the context's
632 * mem_allocated field.
644 block->
endptr = ((
char *) block) + blksize;
646 /* Mark unallocated space NOACCESS. */
652 * GenerationBlockMarkEmpty
653 * Set a block as empty. Does not free the block.
658#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
662#ifdef CLOBBER_FREED_MEMORY
663 wipe_mem(datastart, block->
freeptr - datastart);
665 /* wipe_mem() would have done this */
669 /* Reset the block, but don't return it to malloc */
676 * GenerationBlockFreeBytes
677 * Returns the number of bytes free in 'block'
686 * GenerationBlockFree
687 * Remove 'block' from 'set' and release the memory consumed by it.
692 /* Make sure nobody tries to free the keeper block */
694 /* We shouldn't be freeing the freeblock either */
697 /* release the block from the list of blocks */
702#ifdef CLOBBER_FREED_MEMORY
703 wipe_mem(block, block->
blksize);
706 /* As in aset.c, free block-header vchunks explicitly */
714 * Update number of chunks in the block, and consider freeing the block
715 * if it's become empty.
723#if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
724 || defined(CLOBBER_FREED_MEMORY)
728 /* Allow access to the chunk header. */
736 * Try to verify that we have a sane block pointer: the block header
737 * should reference a generation context.
740 elog(
ERROR,
"could not find block containing chunk %p", chunk);
742#if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
743 || defined(CLOBBER_FREED_MEMORY)
744 chunksize = block->
endptr - (
char *) pointer;
752 * In this path, for speed reasons we just Assert that the referenced
753 * block is good. Future field experience may show that this Assert
754 * had better become a regular runtime test-and-elog check.
758#if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
759 || defined(CLOBBER_FREED_MEMORY)
764#ifdef MEMORY_CONTEXT_CHECKING
765 /* Test for someone scribbling on unused space in chunk */
766 Assert(chunk->requested_size < chunksize);
767 if (!sentinel_ok(pointer, chunk->requested_size))
768 elog(
WARNING,
"detected write past chunk end in %s %p",
772#ifdef CLOBBER_FREED_MEMORY
773 wipe_mem(pointer, chunksize);
776#ifdef MEMORY_CONTEXT_CHECKING
777 /* Reset requested_size to InvalidAllocSize in freed chunks */
787 /* If there are still allocated chunks in the block, we're done. */
793 /*-----------------------
794 * The block this allocation was on has now become completely empty of
795 * chunks. In the general case, we can now return the memory for this
796 * block back to malloc. However, there are cases where we don't want to
799 * 1) If it's the keeper block. This block was malloc'd in the same
800 * allocation as the context itself and can't be free'd without
801 * freeing the context.
802 * 2) If it's the current block. We could free this, but doing so would
803 * leave us nothing to set the current block to, so we just mark the
804 * block as empty so new allocations can reuse it again.
805 * 3) If we have no "freeblock" set, then we save a single block for
806 * future allocations to avoid having to malloc a new block again.
807 * This is useful for FIFO workloads as it avoids continual
808 * free/malloc cycles.
824 * When handling repalloc, we simply allocate a new chunk, copy the data
825 * and discard the old one. The only exception is when the new size fits
826 * into the old chunk - in that case we just update chunk header.
837 /* Allow access to the chunk header. */
845 * Try to verify that we have a sane block pointer: the block header
846 * should reference a generation context.
849 elog(
ERROR,
"could not find block containing chunk %p", chunk);
851 oldsize = block->
endptr - (
char *) pointer;
858 * In this path, for speed reasons we just Assert that the referenced
859 * block is good. Future field experience may show that this Assert
860 * had better become a regular runtime test-and-elog check.
869#ifdef MEMORY_CONTEXT_CHECKING
870 /* Test for someone scribbling on unused space in chunk */
871 Assert(chunk->requested_size < oldsize);
872 if (!sentinel_ok(pointer, chunk->requested_size))
873 elog(
WARNING,
"detected write past chunk end in %s %p",
878 * Maybe the allocated area already big enough. (In particular, we always
879 * fall out here if the requested size is a decrease.)
881 * This memory context does not use power-of-2 chunk sizing and instead
882 * carves the chunks to be as small as possible, so most repalloc() calls
883 * will end up in the palloc/memcpy/pfree branch.
885 * XXX Perhaps we should annotate this condition with unlikely()?
887#ifdef MEMORY_CONTEXT_CHECKING
888 /* With MEMORY_CONTEXT_CHECKING, we need an extra byte for the sentinel */
894#ifdef MEMORY_CONTEXT_CHECKING
895 Size oldrequest = chunk->requested_size;
897#ifdef RANDOMIZE_ALLOCATED_MEMORY
898 /* We can only fill the extra space if we know the prior request */
899 if (size > oldrequest)
900 randomize_mem((
char *) pointer + oldrequest,
904 chunk->requested_size = size;
907 * If this is an increase, mark any newly-available part UNDEFINED.
908 * Otherwise, mark the obsolete part NOACCESS.
910 if (size > oldrequest)
917 /* set mark to catch clobber of "unused" space */
918 set_sentinel(pointer, size);
919#else /* !MEMORY_CONTEXT_CHECKING */
922 * We don't have the information to determine whether we're growing
923 * the old request or shrinking it, so we conservatively mark the
924 * entire new allocation DEFINED.
930 /* Disallow access to the chunk header. */
936 /* allocate new chunk (this also checks size is valid) */
939 /* leave immediately if request was not completed */
940 if (newPointer == NULL)
942 /* Disallow access to the chunk header. */
948 * GenerationAlloc() may have returned a region that is still NOACCESS.
949 * Change it to UNDEFINED for the moment; memcpy() will then transfer
950 * definedness from the old allocation to the new. If we know the old
951 * allocation, copy just that much. Otherwise, make the entire old chunk
952 * defined to avoid errors as we copy the currently-NOACCESS trailing
956#ifdef MEMORY_CONTEXT_CHECKING
957 oldsize = chunk->requested_size;
962 /* transfer existing data (certain to fit) */
963 memcpy(newPointer, pointer, oldsize);
972 * GenerationGetChunkContext
973 * Return the MemoryContext that 'pointer' belongs to.
981 /* Allow access to the chunk header. */
989 /* Disallow access to the chunk header. */
997 * GenerationGetChunkSpace
998 * Given a currently-allocated chunk, determine the total space
999 * it occupies (including all memory-allocation overhead).
1007 /* Allow access to the chunk header. */
1015 chunksize = block->
endptr - (
char *) pointer;
1020 /* Disallow access to the chunk header. */
1028 * Is a GenerationContext empty of any allocated space?
1051 * Compute stats about memory consumption of a Generation context.
1053 * printfunc: if not NULL, pass a human-readable stats string to this.
1054 * passthru: pass this pointer through to printfunc.
1055 * totals: if not NULL, add stats about this context into *totals.
1056 * print_to_stderr: print stats to stderr if true, elog otherwise.
1058 * XXX freespace only accounts for empty space at the end of the block, not
1059 * space of freed chunks (which is unknown).
1069 Size nfreechunks = 0;
1076 /* Include context header in totalspace */
1085 nfreechunks += block->
nfree;
1092 char stats_string[200];
1094 snprintf(stats_string,
sizeof(stats_string),
1095 "%zu total in %zu blocks (%zu chunks); %zu free (%zu chunks); %zu used",
1096 totalspace, nblocks, nchunks, freespace,
1097 nfreechunks, totalspace - freespace);
1098 printfunc(context, passthru, stats_string, print_to_stderr);
1111#ifdef MEMORY_CONTEXT_CHECKING
1115 * Walk through chunks and check consistency of memory.
1117 * NOTE: report errors as WARNING, *not* ERROR or FATAL. Otherwise you'll
1118 * find yourself in an infinite loop when trouble occurs, because this
1119 * routine will be entered again when elog cleanup tries to release memory!
1127 Size total_allocated = 0;
1129 /* walk all blocks in this context */
1136 bool has_external_chunk =
false;
1138 total_allocated += block->
blksize;
1141 * nfree > nchunks is surely wrong. Equality is allowed as the block
1142 * might completely empty if it's the freeblock.
1145 elog(
WARNING,
"problem in Generation %s: number of free chunks %d in block %p exceeds %d allocated",
1148 /* check block belongs to the correct context */
1150 elog(
WARNING,
"problem in Generation %s: bogus context link in block %p",
1153 /* Now walk through the chunks and count them. */
1158 while (ptr < block->freeptr)
1164 /* Allow access to the chunk header. */
1171 has_external_chunk =
true;
1179 /* move to the next chunk */
1184 /* chunks have both block and context pointers, so check both */
1185 if (chunkblock != block)
1186 elog(
WARNING,
"problem in Generation %s: bogus block link in block %p, chunk %p",
1187 name, block, chunk);
1190 /* is chunk allocated? */
1193 /* now make sure the chunk size is correct */
1194 if (chunksize < chunk->requested_size ||
1196 elog(
WARNING,
"problem in Generation %s: bogus chunk size in block %p, chunk %p",
1197 name, block, chunk);
1199 /* check sentinel */
1200 Assert(chunk->requested_size < chunksize);
1202 elog(
WARNING,
"problem in Generation %s: detected write past chunk end in block %p, chunk %p",
1203 name, block, chunk);
1208 /* if chunk is allocated, disallow access to the chunk header */
1214 * Make sure we got the expected number of allocated and free chunks
1215 * (as tracked in the block header).
1217 if (nchunks != block->
nchunks)
1218 elog(
WARNING,
"problem in Generation %s: number of allocated chunks %d in block %p does not match header %d",
1221 if (nfree != block->
nfree)
1222 elog(
WARNING,
"problem in Generation %s: number of free chunks %d in block %p does not match header %d",
1225 if (has_external_chunk && nchunks > 1)
1226 elog(
WARNING,
"problem in Generation %s: external chunk on non-dedicated block %p",
1234#endif /* MEMORY_CONTEXT_CHECKING */
#define StaticAssertDecl(condition, errmessage)
int errdetail(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
void * GenerationRealloc(void *pointer, Size size, int flags)
static void GenerationBlockInit(GenerationContext *context, GenerationBlock *block, Size blksize)
static pg_noinline void * GenerationAllocLarge(MemoryContext context, Size size, int flags)
#define IsKeeperBlock(set, block)
#define Generation_CHUNK_FRACTION
void GenerationReset(MemoryContext context)
static Size GenerationBlockFreeBytes(GenerationBlock *block)
static void GenerationBlockFree(GenerationContext *set, GenerationBlock *block)
void GenerationFree(void *pointer)
MemoryContext GenerationGetChunkContext(void *pointer)
Size GenerationGetChunkSpace(void *pointer)
struct GenerationContext GenerationContext
#define Generation_CHUNKHDRSZ
static void * GenerationAllocChunkFromBlock(MemoryContext context, GenerationBlock *block, Size size, Size chunk_size)
static void GenerationBlockMarkEmpty(GenerationBlock *block)
#define GenerationBlockIsValid(block)
bool GenerationIsEmpty(MemoryContext context)
void GenerationStats(MemoryContext context, MemoryStatsPrintFunc printfunc, void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
#define GenerationBlockIsEmpty(b)
#define Generation_BLOCKHDRSZ
MemoryContext GenerationContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
static pg_noinline void * GenerationAllocFromNewBlock(MemoryContext context, Size size, int flags, Size chunk_size)
void GenerationDelete(MemoryContext context)
#define GenerationIsValid(set)
void * GenerationAlloc(MemoryContext context, Size size, int flags)
#define ExternalChunkGetBlock(chunk)
Assert(PointerIsAligned(start, uint64))
#define dlist_foreach(iter, lhead)
static void dlist_init(dlist_head *head)
static bool dlist_has_next(const dlist_head *head, const dlist_node *node)
static void dlist_delete(dlist_node *node)
static void dlist_push_head(dlist_head *head, dlist_node *node)
static dlist_node * dlist_head_node(dlist_head *head)
#define dlist_foreach_modify(iter, lhead)
static bool dlist_is_empty(const dlist_head *head)
#define dlist_container(type, membername, ptr)
void MemoryContextCreate(MemoryContext node, NodeTag tag, MemoryContextMethodID method_id, MemoryContext parent, const char *name)
MemoryContext TopMemoryContext
void MemoryContextStats(MemoryContext context)
void * MemoryContextAllocationFailure(MemoryContext context, Size size, int flags)
#define VALGRIND_DESTROY_MEMPOOL(context)
#define VALGRIND_MAKE_MEM_DEFINED(addr, size)
#define VALGRIND_CREATE_MEMPOOL(context, redzones, zeroed)
#define VALGRIND_MEMPOOL_ALLOC(context, addr, size)
#define VALGRIND_MEMPOOL_TRIM(context, addr, size)
#define VALGRIND_MEMPOOL_FREE(context, addr)
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
#define VALGRIND_MAKE_MEM_UNDEFINED(addr, size)
void(* MemoryStatsPrintFunc)(MemoryContext context, void *passthru, const char *stats_string, bool print_to_stderr)
#define AllocHugeSizeIsValid(size)
static void MemoryContextCheckSize(MemoryContext context, Size size, int flags)
#define MEMORYCHUNK_MAX_BLOCKOFFSET
#define MEMORYCHUNK_MAX_VALUE
static Size MemoryChunkGetValue(MemoryChunk *chunk)
#define MemoryChunkGetPointer(c)
static bool MemoryChunkIsExternal(MemoryChunk *chunk)
static void * MemoryChunkGetBlock(MemoryChunk *chunk)
static void MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk, MemoryContextMethodID methodid)
#define PointerGetMemoryChunk(p)
static void MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block, Size value, MemoryContextMethodID methodid)
struct MemoryContextData * MemoryContext
#define pg_nextpower2_size_t
GenerationContext * context
GenerationBlock * freeblock