PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
generation.c File Reference
#include "postgres.h"
#include "lib/ilist.h"
#include "port/pg_bitutils.h"
#include "utils/memdebug.h"
#include "utils/memutils.h"
#include "utils/memutils_internal.h"
#include "utils/memutils_memorychunk.h"
Include dependency graph for generation.c:

Go to the source code of this file.

Data Structures

struct  GenerationContext
 
struct  GenerationBlock
 

Macros

#define Generation_BLOCKHDRSZ   MAXALIGN(sizeof(GenerationBlock))
 
#define Generation_CHUNKHDRSZ   sizeof(MemoryChunk)
 
#define Generation_CHUNK_FRACTION   8
 
#define GenerationIsValid(set)    (PointerIsValid(set) && IsA(set, GenerationContext))
 
#define GenerationBlockIsValid(block)    (PointerIsValid(block) && GenerationIsValid((block)->context))
 
#define GenerationBlockIsEmpty(b)   ((b)->nchunks == 0)
 
#define ExternalChunkGetBlock(chunk)    (GenerationBlock *) ((char *) chunk - Generation_BLOCKHDRSZ)
 
#define KeeperBlock(set)
 
#define IsKeeperBlock(set, block)   ((block) == (KeeperBlock(set)))
 

Typedefs

typedef struct GenerationBlock GenerationBlock
 
typedef void * GenerationPointer
 
typedef struct GenerationContext GenerationContext
 

Functions

static void GenerationBlockInit (GenerationContext *context, GenerationBlock *block, Size blksize)
 
static void GenerationBlockMarkEmpty (GenerationBlock *block)
 
static Size GenerationBlockFreeBytes (GenerationBlock *block)
 
static void GenerationBlockFree (GenerationContext *set, GenerationBlock *block)
 
MemoryContext GenerationContextCreate (MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
 
void GenerationReset (MemoryContext context)
 
void GenerationDelete (MemoryContext context)
 
static pg_noinline void * GenerationAllocLarge (MemoryContext context, Size size, int flags)
 
static void * GenerationAllocChunkFromBlock (MemoryContext context, GenerationBlock *block, Size size, Size chunk_size)
 
static pg_noinline void * GenerationAllocFromNewBlock (MemoryContext context, Size size, int flags, Size chunk_size)
 
void * GenerationAlloc (MemoryContext context, Size size, int flags)
 
void GenerationFree (void *pointer)
 
void * GenerationRealloc (void *pointer, Size size, int flags)
 
MemoryContext GenerationGetChunkContext (void *pointer)
 
Size GenerationGetChunkSpace (void *pointer)
 
bool GenerationIsEmpty (MemoryContext context)
 
void GenerationStats (MemoryContext context, MemoryStatsPrintFunc printfunc, void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
 

Macro Definition Documentation

◆ ExternalChunkGetBlock

#define ExternalChunkGetBlock (   chunk)     (GenerationBlock *) ((char *) chunk - Generation_BLOCKHDRSZ)

Definition at line 123 of file generation.c.

◆ Generation_BLOCKHDRSZ

#define Generation_BLOCKHDRSZ   MAXALIGN(sizeof(GenerationBlock))

Definition at line 46 of file generation.c.

◆ Generation_CHUNK_FRACTION

#define Generation_CHUNK_FRACTION   8

Definition at line 49 of file generation.c.

◆ Generation_CHUNKHDRSZ

#define Generation_CHUNKHDRSZ   sizeof(MemoryChunk)

Definition at line 47 of file generation.c.

◆ GenerationBlockIsEmpty

#define GenerationBlockIsEmpty (   b)    ((b)->nchunks == 0)

Definition at line 116 of file generation.c.

◆ GenerationBlockIsValid

#define GenerationBlockIsValid (   block)     (PointerIsValid(block) && GenerationIsValid((block)->context))

Definition at line 109 of file generation.c.

◆ GenerationIsValid

#define GenerationIsValid (   set)     (PointerIsValid(set) && IsA(set, GenerationContext))

Definition at line 102 of file generation.c.

◆ IsKeeperBlock

#define IsKeeperBlock (   set,
  block 
)    ((block) == (KeeperBlock(set)))

Definition at line 132 of file generation.c.

◆ KeeperBlock

#define KeeperBlock (   set)
Value:
((GenerationBlock *) (((char *) set) + \
MAXALIGN(sizeof(GenerationContext))))

Definition at line 127 of file generation.c.

Typedef Documentation

◆ GenerationBlock

Definition at line 51 of file generation.c.

◆ GenerationContext

◆ GenerationPointer

typedef void* GenerationPointer

Definition at line 53 of file generation.c.

Function Documentation

◆ GenerationAlloc()

void * GenerationAlloc ( MemoryContext  context,
Size  size,
int  flags 
)

Definition at line 527 of file generation.c.

528{
529 GenerationContext *set = (GenerationContext *) context;
530 GenerationBlock *block;
531 Size chunk_size;
532 Size required_size;
533
535
536#ifdef MEMORY_CONTEXT_CHECKING
537 /* ensure there's always space for the sentinel byte */
538 chunk_size = MAXALIGN(size + 1);
539#else
540 chunk_size = MAXALIGN(size);
541#endif
542
543 /*
544 * If requested size exceeds maximum for chunks we hand the request off to
545 * GenerationAllocLarge().
546 */
547 if (chunk_size > set->allocChunkLimit)
548 return GenerationAllocLarge(context, size, flags);
549
550 required_size = chunk_size + Generation_CHUNKHDRSZ;
551
552 /*
553 * Not an oversized chunk. We try to first make use of the current block,
554 * but if there's not enough space in it, instead of allocating a new
555 * block, we look to see if the empty freeblock has enough space. We
556 * don't try reusing the keeper block. If it's become empty we'll reuse
557 * that again only if the context is reset.
558 *
559 * We only try reusing the freeblock if we've no space for this allocation
560 * on the current block. When a freeblock exists, we'll switch to it once
561 * the first time we can't fit an allocation in the current block. We
562 * avoid ping-ponging between the two as we need to be careful not to
563 * fragment differently sized consecutive allocations between several
564 * blocks. Going between the two could cause fragmentation for FIFO
565 * workloads, which generation is meant to be good at.
566 */
567 block = set->block;
568
569 if (unlikely(GenerationBlockFreeBytes(block) < required_size))
570 {
571 GenerationBlock *freeblock = set->freeblock;
572
573 /* freeblock, if set, must be empty */
574 Assert(freeblock == NULL || GenerationBlockIsEmpty(freeblock));
575
576 /* check if we have a freeblock and if it's big enough */
577 if (freeblock != NULL &&
578 GenerationBlockFreeBytes(freeblock) >= required_size)
579 {
580 /* make the freeblock the current block */
581 set->freeblock = NULL;
582 set->block = freeblock;
583
584 return GenerationAllocChunkFromBlock(context,
585 freeblock,
586 size,
587 chunk_size);
588 }
589 else
590 {
591 /*
592 * No freeblock, or it's not big enough for this allocation. Make
593 * a new block.
594 */
595 return GenerationAllocFromNewBlock(context, size, flags, chunk_size);
596 }
597 }
598
599 /* The current block has space, so just allocate chunk there. */
600 return GenerationAllocChunkFromBlock(context, block, size, chunk_size);
601}
#define MAXALIGN(LEN)
Definition: c.h:782
#define unlikely(x)
Definition: c.h:347
size_t Size
Definition: c.h:576
static pg_noinline void * GenerationAllocLarge(MemoryContext context, Size size, int flags)
Definition: generation.c:343
static Size GenerationBlockFreeBytes(GenerationBlock *block)
Definition: generation.c:654
#define Generation_CHUNKHDRSZ
Definition: generation.c:47
static void * GenerationAllocChunkFromBlock(MemoryContext context, GenerationBlock *block, Size size, Size chunk_size)
Definition: generation.c:413
#define GenerationBlockIsEmpty(b)
Definition: generation.c:116
static pg_noinline void * GenerationAllocFromNewBlock(MemoryContext context, Size size, int flags, Size chunk_size)
Definition: generation.c:461
#define GenerationIsValid(set)
Definition: generation.c:102
Assert(PointerIsAligned(start, uint64))
GenerationBlock * freeblock
Definition: generation.c:70
GenerationBlock * block
Definition: generation.c:69
uint32 allocChunkLimit
Definition: generation.c:67

References GenerationContext::allocChunkLimit, Assert(), GenerationContext::block, GenerationContext::freeblock, Generation_CHUNKHDRSZ, GenerationAllocChunkFromBlock(), GenerationAllocFromNewBlock(), GenerationAllocLarge(), GenerationBlockFreeBytes(), GenerationBlockIsEmpty, GenerationIsValid, MAXALIGN, and unlikely.

Referenced by GenerationRealloc().

◆ GenerationAllocChunkFromBlock()

static void * GenerationAllocChunkFromBlock ( MemoryContext  context,
GenerationBlock block,
Size  size,
Size  chunk_size 
)
inlinestatic

Definition at line 413 of file generation.c.

415{
416 MemoryChunk *chunk = (MemoryChunk *) (block->freeptr);
417
418 /* validate we've been given a block with enough free space */
419 Assert(block != NULL);
420 Assert((block->endptr - block->freeptr) >=
421 Generation_CHUNKHDRSZ + chunk_size);
422
423 /* Prepare to initialize the chunk header. */
425
426 block->nchunks += 1;
427 block->freeptr += (Generation_CHUNKHDRSZ + chunk_size);
428
429 Assert(block->freeptr <= block->endptr);
430
431 MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_GENERATION_ID);
432#ifdef MEMORY_CONTEXT_CHECKING
433 chunk->requested_size = size;
434 /* set mark to catch clobber of "unused" space */
435 Assert(size < chunk_size);
436 set_sentinel(MemoryChunkGetPointer(chunk), size);
437#endif
438#ifdef RANDOMIZE_ALLOCATED_MEMORY
439 /* fill the allocated space with junk */
440 randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
441#endif
442
443 /* Ensure any padding bytes are marked NOACCESS. */
445 chunk_size - size);
446
447 /* Disallow access to the chunk header. */
449
450 return MemoryChunkGetPointer(chunk);
451}
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
Definition: memdebug.h:27
#define VALGRIND_MAKE_MEM_UNDEFINED(addr, size)
Definition: memdebug.h:28
@ MCTX_GENERATION_ID
#define MemoryChunkGetPointer(c)
static void MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block, Size value, MemoryContextMethodID methodid)
char * freeptr
Definition: generation.c:94

References Assert(), GenerationBlock::endptr, GenerationBlock::freeptr, Generation_CHUNKHDRSZ, MCTX_GENERATION_ID, MemoryChunkGetPointer, MemoryChunkSetHdrMask(), GenerationBlock::nchunks, VALGRIND_MAKE_MEM_NOACCESS, and VALGRIND_MAKE_MEM_UNDEFINED.

Referenced by GenerationAlloc(), and GenerationAllocFromNewBlock().

◆ GenerationAllocFromNewBlock()

static pg_noinline void * GenerationAllocFromNewBlock ( MemoryContext  context,
Size  size,
int  flags,
Size  chunk_size 
)
static

Definition at line 461 of file generation.c.

463{
464 GenerationContext *set = (GenerationContext *) context;
465 GenerationBlock *block;
466 Size blksize;
467 Size required_size;
468
469 /*
470 * The first such block has size initBlockSize, and we double the space in
471 * each succeeding block, but not more than maxBlockSize.
472 */
473 blksize = set->nextBlockSize;
474 set->nextBlockSize <<= 1;
475 if (set->nextBlockSize > set->maxBlockSize)
476 set->nextBlockSize = set->maxBlockSize;
477
478 /* we'll need space for the chunk, chunk hdr and block hdr */
479 required_size = chunk_size + Generation_CHUNKHDRSZ + Generation_BLOCKHDRSZ;
480
481 /* round the size up to the next power of 2 */
482 if (blksize < required_size)
483 blksize = pg_nextpower2_size_t(required_size);
484
485 block = (GenerationBlock *) malloc(blksize);
486
487 if (block == NULL)
488 return MemoryContextAllocationFailure(context, size, flags);
489
490 context->mem_allocated += blksize;
491
492 /* initialize the new block */
493 GenerationBlockInit(set, block, blksize);
494
495 /* add it to the doubly-linked list of blocks */
496 dlist_push_head(&set->blocks, &block->node);
497
498 /* make this the current block */
499 set->block = block;
500
501 return GenerationAllocChunkFromBlock(context, block, size, chunk_size);
502}
static void GenerationBlockInit(GenerationContext *context, GenerationBlock *block, Size blksize)
Definition: generation.c:609
#define Generation_BLOCKHDRSZ
Definition: generation.c:46
#define malloc(a)
Definition: header.h:50
static void dlist_push_head(dlist_head *head, dlist_node *node)
Definition: ilist.h:347
void * MemoryContextAllocationFailure(MemoryContext context, Size size, int flags)
Definition: mcxt.c:1226
#define pg_nextpower2_size_t
Definition: pg_bitutils.h:441
dlist_node node
Definition: generation.c:89
uint32 maxBlockSize
Definition: generation.c:65
uint32 nextBlockSize
Definition: generation.c:66
dlist_head blocks
Definition: generation.c:72

References GenerationContext::block, GenerationContext::blocks, dlist_push_head(), Generation_BLOCKHDRSZ, Generation_CHUNKHDRSZ, GenerationAllocChunkFromBlock(), GenerationBlockInit(), malloc, GenerationContext::maxBlockSize, MemoryContextData::mem_allocated, MemoryContextAllocationFailure(), GenerationContext::nextBlockSize, GenerationBlock::node, and pg_nextpower2_size_t.

Referenced by GenerationAlloc().

◆ GenerationAllocLarge()

static pg_noinline void * GenerationAllocLarge ( MemoryContext  context,
Size  size,
int  flags 
)
static

Definition at line 343 of file generation.c.

344{
345 GenerationContext *set = (GenerationContext *) context;
346 GenerationBlock *block;
347 MemoryChunk *chunk;
348 Size chunk_size;
349 Size required_size;
350 Size blksize;
351
352 /* validate 'size' is within the limits for the given 'flags' */
353 MemoryContextCheckSize(context, size, flags);
354
355#ifdef MEMORY_CONTEXT_CHECKING
356 /* ensure there's always space for the sentinel byte */
357 chunk_size = MAXALIGN(size + 1);
358#else
359 chunk_size = MAXALIGN(size);
360#endif
361 required_size = chunk_size + Generation_CHUNKHDRSZ;
362 blksize = required_size + Generation_BLOCKHDRSZ;
363
364 block = (GenerationBlock *) malloc(blksize);
365 if (block == NULL)
366 return MemoryContextAllocationFailure(context, size, flags);
367
368 context->mem_allocated += blksize;
369
370 /* block with a single (used) chunk */
371 block->context = set;
372 block->blksize = blksize;
373 block->nchunks = 1;
374 block->nfree = 0;
375
376 /* the block is completely full */
377 block->freeptr = block->endptr = ((char *) block) + blksize;
378
379 chunk = (MemoryChunk *) (((char *) block) + Generation_BLOCKHDRSZ);
380
381 /* mark the MemoryChunk as externally managed */
383
384#ifdef MEMORY_CONTEXT_CHECKING
385 chunk->requested_size = size;
386 /* set mark to catch clobber of "unused" space */
387 Assert(size < chunk_size);
388 set_sentinel(MemoryChunkGetPointer(chunk), size);
389#endif
390#ifdef RANDOMIZE_ALLOCATED_MEMORY
391 /* fill the allocated space with junk */
392 randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
393#endif
394
395 /* add the block to the list of allocated blocks */
396 dlist_push_head(&set->blocks, &block->node);
397
398 /* Ensure any padding bytes are marked NOACCESS. */
400 chunk_size - size);
401
402 /* Disallow access to the chunk header. */
404
405 return MemoryChunkGetPointer(chunk);
406}
static void MemoryContextCheckSize(MemoryContext context, Size size, int flags)
static void MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk, MemoryContextMethodID methodid)
GenerationContext * context
Definition: generation.c:90

References Assert(), GenerationBlock::blksize, GenerationContext::blocks, GenerationBlock::context, dlist_push_head(), GenerationBlock::endptr, GenerationBlock::freeptr, Generation_BLOCKHDRSZ, Generation_CHUNKHDRSZ, malloc, MAXALIGN, MCTX_GENERATION_ID, MemoryContextData::mem_allocated, MemoryChunkGetPointer, MemoryChunkSetHdrMaskExternal(), MemoryContextAllocationFailure(), MemoryContextCheckSize(), GenerationBlock::nchunks, GenerationBlock::nfree, GenerationBlock::node, and VALGRIND_MAKE_MEM_NOACCESS.

Referenced by GenerationAlloc().

◆ GenerationBlockFree()

static void GenerationBlockFree ( GenerationContext set,
GenerationBlock block 
)
inlinestatic

Definition at line 664 of file generation.c.

665{
666 /* Make sure nobody tries to free the keeper block */
667 Assert(!IsKeeperBlock(set, block));
668 /* We shouldn't be freeing the freeblock either */
669 Assert(block != set->freeblock);
670
671 /* release the block from the list of blocks */
672 dlist_delete(&block->node);
673
674 ((MemoryContext) set)->mem_allocated -= block->blksize;
675
676#ifdef CLOBBER_FREED_MEMORY
677 wipe_mem(block, block->blksize);
678#endif
679
680 free(block);
681}
#define IsKeeperBlock(set, block)
Definition: generation.c:132
#define free(a)
Definition: header.h:65
static void dlist_delete(dlist_node *node)
Definition: ilist.h:405
struct MemoryContextData * MemoryContext
Definition: palloc.h:36

References Assert(), GenerationBlock::blksize, dlist_delete(), free, GenerationContext::freeblock, IsKeeperBlock, and GenerationBlock::node.

Referenced by GenerationFree(), and GenerationReset().

◆ GenerationBlockFreeBytes()

static Size GenerationBlockFreeBytes ( GenerationBlock block)
inlinestatic

Definition at line 654 of file generation.c.

655{
656 return (block->endptr - block->freeptr);
657}

References GenerationBlock::endptr, and GenerationBlock::freeptr.

Referenced by GenerationAlloc().

◆ GenerationBlockInit()

static void GenerationBlockInit ( GenerationContext context,
GenerationBlock block,
Size  blksize 
)
inlinestatic

Definition at line 609 of file generation.c.

611{
612 block->context = context;
613 block->blksize = blksize;
614 block->nchunks = 0;
615 block->nfree = 0;
616
617 block->freeptr = ((char *) block) + Generation_BLOCKHDRSZ;
618 block->endptr = ((char *) block) + blksize;
619
620 /* Mark unallocated space NOACCESS. */
622 blksize - Generation_BLOCKHDRSZ);
623}

References GenerationBlock::blksize, GenerationBlock::context, GenerationBlock::endptr, GenerationBlock::freeptr, Generation_BLOCKHDRSZ, GenerationBlock::nchunks, GenerationBlock::nfree, and VALGRIND_MAKE_MEM_NOACCESS.

Referenced by GenerationAllocFromNewBlock(), and GenerationContextCreate().

◆ GenerationBlockMarkEmpty()

static void GenerationBlockMarkEmpty ( GenerationBlock block)
inlinestatic

Definition at line 630 of file generation.c.

631{
632#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
633 char *datastart = ((char *) block) + Generation_BLOCKHDRSZ;
634#endif
635
636#ifdef CLOBBER_FREED_MEMORY
637 wipe_mem(datastart, block->freeptr - datastart);
638#else
639 /* wipe_mem() would have done this */
640 VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
641#endif
642
643 /* Reset the block, but don't return it to malloc */
644 block->nchunks = 0;
645 block->nfree = 0;
646 block->freeptr = ((char *) block) + Generation_BLOCKHDRSZ;
647}

References GenerationBlock::freeptr, Generation_BLOCKHDRSZ, GenerationBlock::nchunks, GenerationBlock::nfree, and VALGRIND_MAKE_MEM_NOACCESS.

Referenced by GenerationFree(), and GenerationReset().

◆ GenerationContextCreate()

MemoryContext GenerationContextCreate ( MemoryContext  parent,
const char *  name,
Size  minContextSize,
Size  initBlockSize,
Size  maxBlockSize 
)

Definition at line 160 of file generation.c.

165{
166 Size firstBlockSize;
167 Size allocSize;
169 GenerationBlock *block;
170
171 /* ensure MemoryChunk's size is properly maxaligned */
173 "sizeof(MemoryChunk) is not maxaligned");
174
175 /*
176 * First, validate allocation parameters. Asserts seem sufficient because
177 * nobody varies their parameters at runtime. We somewhat arbitrarily
178 * enforce a minimum 1K block size. We restrict the maximum block size to
179 * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
180 * regards to addressing the offset between the chunk and the block that
181 * the chunk is stored on. We would be unable to store the offset between
182 * the chunk and block for any chunks that were beyond
183 * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
184 * larger than this.
185 */
186 Assert(initBlockSize == MAXALIGN(initBlockSize) &&
187 initBlockSize >= 1024);
188 Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
189 maxBlockSize >= initBlockSize &&
190 AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
191 Assert(minContextSize == 0 ||
192 (minContextSize == MAXALIGN(minContextSize) &&
193 minContextSize >= 1024 &&
194 minContextSize <= maxBlockSize));
195 Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
196
197 /* Determine size of initial block */
198 allocSize = MAXALIGN(sizeof(GenerationContext)) +
200 if (minContextSize != 0)
201 allocSize = Max(allocSize, minContextSize);
202 else
203 allocSize = Max(allocSize, initBlockSize);
204
205 /*
206 * Allocate the initial block. Unlike other generation.c blocks, it
207 * starts with the context header and its block header follows that.
208 */
209 set = (GenerationContext *) malloc(allocSize);
210 if (set == NULL)
211 {
214 (errcode(ERRCODE_OUT_OF_MEMORY),
215 errmsg("out of memory"),
216 errdetail("Failed while creating memory context \"%s\".",
217 name)));
218 }
219
220 /*
221 * Avoid writing code that can fail between here and MemoryContextCreate;
222 * we'd leak the header if we ereport in this stretch.
223 */
224 dlist_init(&set->blocks);
225
226 /* Fill in the initial block's block header */
227 block = KeeperBlock(set);
228 /* determine the block size and initialize it */
229 firstBlockSize = allocSize - MAXALIGN(sizeof(GenerationContext));
230 GenerationBlockInit(set, block, firstBlockSize);
231
232 /* add it to the doubly-linked list of blocks */
233 dlist_push_head(&set->blocks, &block->node);
234
235 /* use it as the current allocation block */
236 set->block = block;
237
238 /* No free block, yet */
239 set->freeblock = NULL;
240
241 /* Fill in GenerationContext-specific header fields */
242 set->initBlockSize = (uint32) initBlockSize;
243 set->maxBlockSize = (uint32) maxBlockSize;
244 set->nextBlockSize = (uint32) initBlockSize;
245
246 /*
247 * Compute the allocation chunk size limit for this context.
248 *
249 * Limit the maximum size a non-dedicated chunk can be so that we can fit
250 * at least Generation_CHUNK_FRACTION of chunks this big onto the maximum
251 * sized block. We must further limit this value so that it's no more
252 * than MEMORYCHUNK_MAX_VALUE. We're unable to have non-external chunks
253 * larger than that value as we store the chunk size in the MemoryChunk
254 * 'value' field in the call to MemoryChunkSetHdrMask().
255 */
256 set->allocChunkLimit = Min(maxBlockSize, MEMORYCHUNK_MAX_VALUE);
257 while ((Size) (set->allocChunkLimit + Generation_CHUNKHDRSZ) >
259 set->allocChunkLimit >>= 1;
260
261 /* Finally, do the type-independent part of context creation */
263 T_GenerationContext,
265 parent,
266 name);
267
268 ((MemoryContext) set)->mem_allocated = firstBlockSize;
269
270 return (MemoryContext) set;
271}
#define Min(x, y)
Definition: c.h:975
#define Max(x, y)
Definition: c.h:969
uint32_t uint32
Definition: c.h:502
#define StaticAssertDecl(condition, errmessage)
Definition: c.h:907
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define Generation_CHUNK_FRACTION
Definition: generation.c:49
#define KeeperBlock(set)
Definition: generation.c:127
static void dlist_init(dlist_head *head)
Definition: ilist.h:314
void MemoryContextCreate(MemoryContext node, NodeTag tag, MemoryContextMethodID method_id, MemoryContext parent, const char *name)
Definition: mcxt.c:1175
MemoryContext TopMemoryContext
Definition: mcxt.c:165
void MemoryContextStats(MemoryContext context)
Definition: mcxt.c:845
#define AllocHugeSizeIsValid(size)
Definition: memutils.h:52
#define MEMORYCHUNK_MAX_BLOCKOFFSET
#define MEMORYCHUNK_MAX_VALUE
uint32 initBlockSize
Definition: generation.c:64
const char * name

References GenerationContext::allocChunkLimit, AllocHugeSizeIsValid, Assert(), GenerationContext::block, GenerationContext::blocks, dlist_init(), dlist_push_head(), ereport, errcode(), errdetail(), errmsg(), ERROR, GenerationContext::freeblock, Generation_BLOCKHDRSZ, Generation_CHUNK_FRACTION, Generation_CHUNKHDRSZ, GenerationBlockInit(), GenerationContext::initBlockSize, KeeperBlock, malloc, Max, MAXALIGN, GenerationContext::maxBlockSize, MCTX_GENERATION_ID, MEMORYCHUNK_MAX_BLOCKOFFSET, MEMORYCHUNK_MAX_VALUE, MemoryContextCreate(), MemoryContextStats(), Min, name, GenerationContext::nextBlockSize, GenerationBlock::node, StaticAssertDecl, and TopMemoryContext.

Referenced by gistvacuumscan(), ReorderBufferAllocate(), and tuplestore_begin_common().

◆ GenerationDelete()

void GenerationDelete ( MemoryContext  context)

Definition at line 328 of file generation.c.

329{
330 /* Reset to release all releasable GenerationBlocks */
331 GenerationReset(context);
332 /* And free the context header and keeper block */
333 free(context);
334}
void GenerationReset(MemoryContext context)
Definition: generation.c:283

References free, and GenerationReset().

◆ GenerationFree()

void GenerationFree ( void *  pointer)

Definition at line 689 of file generation.c.

690{
691 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
692 GenerationBlock *block;
694#if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
695 || defined(CLOBBER_FREED_MEMORY)
696 Size chunksize;
697#endif
698
699 /* Allow access to the chunk header. */
701
702 if (MemoryChunkIsExternal(chunk))
703 {
704 block = ExternalChunkGetBlock(chunk);
705
706 /*
707 * Try to verify that we have a sane block pointer: the block header
708 * should reference a generation context.
709 */
710 if (!GenerationBlockIsValid(block))
711 elog(ERROR, "could not find block containing chunk %p", chunk);
712
713#if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
714 || defined(CLOBBER_FREED_MEMORY)
715 chunksize = block->endptr - (char *) pointer;
716#endif
717 }
718 else
719 {
720 block = MemoryChunkGetBlock(chunk);
721
722 /*
723 * In this path, for speed reasons we just Assert that the referenced
724 * block is good. Future field experience may show that this Assert
725 * had better become a regular runtime test-and-elog check.
726 */
728
729#if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
730 || defined(CLOBBER_FREED_MEMORY)
731 chunksize = MemoryChunkGetValue(chunk);
732#endif
733 }
734
735#ifdef MEMORY_CONTEXT_CHECKING
736 /* Test for someone scribbling on unused space in chunk */
737 Assert(chunk->requested_size < chunksize);
738 if (!sentinel_ok(pointer, chunk->requested_size))
739 elog(WARNING, "detected write past chunk end in %s %p",
740 ((MemoryContext) block->context)->name, chunk);
741#endif
742
743#ifdef CLOBBER_FREED_MEMORY
744 wipe_mem(pointer, chunksize);
745#endif
746
747#ifdef MEMORY_CONTEXT_CHECKING
748 /* Reset requested_size to InvalidAllocSize in freed chunks */
749 chunk->requested_size = InvalidAllocSize;
750#endif
751
752 block->nfree += 1;
753
754 Assert(block->nchunks > 0);
755 Assert(block->nfree <= block->nchunks);
756 Assert(block != block->context->freeblock);
757
758 /* If there are still allocated chunks in the block, we're done. */
759 if (likely(block->nfree < block->nchunks))
760 return;
761
762 set = block->context;
763
764 /*-----------------------
765 * The block this allocation was on has now become completely empty of
766 * chunks. In the general case, we can now return the memory for this
767 * block back to malloc. However, there are cases where we don't want to
768 * do that:
769 *
770 * 1) If it's the keeper block. This block was malloc'd in the same
771 * allocation as the context itself and can't be free'd without
772 * freeing the context.
773 * 2) If it's the current block. We could free this, but doing so would
774 * leave us nothing to set the current block to, so we just mark the
775 * block as empty so new allocations can reuse it again.
776 * 3) If we have no "freeblock" set, then we save a single block for
777 * future allocations to avoid having to malloc a new block again.
778 * This is useful for FIFO workloads as it avoids continual
779 * free/malloc cycles.
780 */
781 if (IsKeeperBlock(set, block) || set->block == block)
782 GenerationBlockMarkEmpty(block); /* case 1 and 2 */
783 else if (set->freeblock == NULL)
784 {
785 /* case 3 */
787 set->freeblock = block;
788 }
789 else
790 GenerationBlockFree(set, block); /* Otherwise, free it */
791}
#define likely(x)
Definition: c.h:346
#define WARNING
Definition: elog.h:36
#define elog(elevel,...)
Definition: elog.h:225
static void GenerationBlockFree(GenerationContext *set, GenerationBlock *block)
Definition: generation.c:664
static void GenerationBlockMarkEmpty(GenerationBlock *block)
Definition: generation.c:630
#define GenerationBlockIsValid(block)
Definition: generation.c:109
#define ExternalChunkGetBlock(chunk)
Definition: generation.c:123
#define VALGRIND_MAKE_MEM_DEFINED(addr, size)
Definition: memdebug.h:26
#define InvalidAllocSize
Definition: memutils.h:50
static Size MemoryChunkGetValue(MemoryChunk *chunk)
static bool MemoryChunkIsExternal(MemoryChunk *chunk)
static void * MemoryChunkGetBlock(MemoryChunk *chunk)
#define PointerGetMemoryChunk(p)

References Assert(), GenerationContext::block, GenerationBlock::context, elog, GenerationBlock::endptr, ERROR, ExternalChunkGetBlock, GenerationContext::freeblock, Generation_CHUNKHDRSZ, GenerationBlockFree(), GenerationBlockIsValid, GenerationBlockMarkEmpty(), InvalidAllocSize, IsKeeperBlock, likely, MemoryChunkGetBlock(), MemoryChunkGetValue(), MemoryChunkIsExternal(), GenerationBlock::nchunks, GenerationBlock::nfree, PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and WARNING.

Referenced by GenerationRealloc().

◆ GenerationGetChunkContext()

MemoryContext GenerationGetChunkContext ( void *  pointer)

Definition at line 947 of file generation.c.

948{
949 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
950 GenerationBlock *block;
951
952 /* Allow access to the chunk header. */
954
955 if (MemoryChunkIsExternal(chunk))
956 block = ExternalChunkGetBlock(chunk);
957 else
958 block = (GenerationBlock *) MemoryChunkGetBlock(chunk);
959
960 /* Disallow access to the chunk header. */
962
964 return &block->context->header;
965}
MemoryContextData header
Definition: generation.c:61

References Assert(), GenerationBlock::context, ExternalChunkGetBlock, Generation_CHUNKHDRSZ, GenerationBlockIsValid, GenerationContext::header, MemoryChunkGetBlock(), MemoryChunkIsExternal(), PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ GenerationGetChunkSpace()

Size GenerationGetChunkSpace ( void *  pointer)

Definition at line 973 of file generation.c.

974{
975 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
976 Size chunksize;
977
978 /* Allow access to the chunk header. */
980
981 if (MemoryChunkIsExternal(chunk))
982 {
984
986 chunksize = block->endptr - (char *) pointer;
987 }
988 else
989 chunksize = MemoryChunkGetValue(chunk);
990
991 /* Disallow access to the chunk header. */
993
994 return Generation_CHUNKHDRSZ + chunksize;
995}

References Assert(), GenerationBlock::endptr, ExternalChunkGetBlock, Generation_CHUNKHDRSZ, GenerationBlockIsValid, MemoryChunkGetValue(), MemoryChunkIsExternal(), PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ GenerationIsEmpty()

bool GenerationIsEmpty ( MemoryContext  context)

Definition at line 1002 of file generation.c.

1003{
1004 GenerationContext *set = (GenerationContext *) context;
1005 dlist_iter iter;
1006
1008
1009 dlist_foreach(iter, &set->blocks)
1010 {
1011 GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
1012
1013 if (block->nchunks > 0)
1014 return false;
1015 }
1016
1017 return true;
1018}
#define dlist_foreach(iter, lhead)
Definition: ilist.h:623
#define dlist_container(type, membername, ptr)
Definition: ilist.h:593
dlist_node * cur
Definition: ilist.h:179

References Assert(), GenerationContext::blocks, dlist_iter::cur, dlist_container, dlist_foreach, GenerationIsValid, and GenerationBlock::nchunks.

◆ GenerationRealloc()

void * GenerationRealloc ( void *  pointer,
Size  size,
int  flags 
)

Definition at line 800 of file generation.c.

801{
802 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
804 GenerationBlock *block;
805 GenerationPointer newPointer;
806 Size oldsize;
807
808 /* Allow access to the chunk header. */
810
811 if (MemoryChunkIsExternal(chunk))
812 {
813 block = ExternalChunkGetBlock(chunk);
814
815 /*
816 * Try to verify that we have a sane block pointer: the block header
817 * should reference a generation context.
818 */
819 if (!GenerationBlockIsValid(block))
820 elog(ERROR, "could not find block containing chunk %p", chunk);
821
822 oldsize = block->endptr - (char *) pointer;
823 }
824 else
825 {
826 block = MemoryChunkGetBlock(chunk);
827
828 /*
829 * In this path, for speed reasons we just Assert that the referenced
830 * block is good. Future field experience may show that this Assert
831 * had better become a regular runtime test-and-elog check.
832 */
834
835 oldsize = MemoryChunkGetValue(chunk);
836 }
837
838 set = block->context;
839
840#ifdef MEMORY_CONTEXT_CHECKING
841 /* Test for someone scribbling on unused space in chunk */
842 Assert(chunk->requested_size < oldsize);
843 if (!sentinel_ok(pointer, chunk->requested_size))
844 elog(WARNING, "detected write past chunk end in %s %p",
845 ((MemoryContext) set)->name, chunk);
846#endif
847
848 /*
849 * Maybe the allocated area already big enough. (In particular, we always
850 * fall out here if the requested size is a decrease.)
851 *
852 * This memory context does not use power-of-2 chunk sizing and instead
853 * carves the chunks to be as small as possible, so most repalloc() calls
854 * will end up in the palloc/memcpy/pfree branch.
855 *
856 * XXX Perhaps we should annotate this condition with unlikely()?
857 */
858#ifdef MEMORY_CONTEXT_CHECKING
859 /* With MEMORY_CONTEXT_CHECKING, we need an extra byte for the sentinel */
860 if (oldsize > size)
861#else
862 if (oldsize >= size)
863#endif
864 {
865#ifdef MEMORY_CONTEXT_CHECKING
866 Size oldrequest = chunk->requested_size;
867
868#ifdef RANDOMIZE_ALLOCATED_MEMORY
869 /* We can only fill the extra space if we know the prior request */
870 if (size > oldrequest)
871 randomize_mem((char *) pointer + oldrequest,
872 size - oldrequest);
873#endif
874
875 chunk->requested_size = size;
876
877 /*
878 * If this is an increase, mark any newly-available part UNDEFINED.
879 * Otherwise, mark the obsolete part NOACCESS.
880 */
881 if (size > oldrequest)
882 VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + oldrequest,
883 size - oldrequest);
884 else
885 VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size,
886 oldsize - size);
887
888 /* set mark to catch clobber of "unused" space */
889 set_sentinel(pointer, size);
890#else /* !MEMORY_CONTEXT_CHECKING */
891
892 /*
893 * We don't have the information to determine whether we're growing
894 * the old request or shrinking it, so we conservatively mark the
895 * entire new allocation DEFINED.
896 */
897 VALGRIND_MAKE_MEM_NOACCESS(pointer, oldsize);
898 VALGRIND_MAKE_MEM_DEFINED(pointer, size);
899#endif
900
901 /* Disallow access to the chunk header. */
903
904 return pointer;
905 }
906
907 /* allocate new chunk (this also checks size is valid) */
908 newPointer = GenerationAlloc((MemoryContext) set, size, flags);
909
910 /* leave immediately if request was not completed */
911 if (newPointer == NULL)
912 {
913 /* Disallow access to the chunk header. */
915 return MemoryContextAllocationFailure((MemoryContext) set, size, flags);
916 }
917
918 /*
919 * GenerationAlloc() may have returned a region that is still NOACCESS.
920 * Change it to UNDEFINED for the moment; memcpy() will then transfer
921 * definedness from the old allocation to the new. If we know the old
922 * allocation, copy just that much. Otherwise, make the entire old chunk
923 * defined to avoid errors as we copy the currently-NOACCESS trailing
924 * bytes.
925 */
926 VALGRIND_MAKE_MEM_UNDEFINED(newPointer, size);
927#ifdef MEMORY_CONTEXT_CHECKING
928 oldsize = chunk->requested_size;
929#else
930 VALGRIND_MAKE_MEM_DEFINED(pointer, oldsize);
931#endif
932
933 /* transfer existing data (certain to fit) */
934 memcpy(newPointer, pointer, oldsize);
935
936 /* free old chunk */
937 GenerationFree(pointer);
938
939 return newPointer;
940}
void GenerationFree(void *pointer)
Definition: generation.c:689
void * GenerationPointer
Definition: generation.c:53
void * GenerationAlloc(MemoryContext context, Size size, int flags)
Definition: generation.c:527

References Assert(), GenerationBlock::context, elog, GenerationBlock::endptr, ERROR, ExternalChunkGetBlock, Generation_CHUNKHDRSZ, GenerationAlloc(), GenerationBlockIsValid, GenerationFree(), MemoryChunkGetBlock(), MemoryChunkGetValue(), MemoryChunkIsExternal(), MemoryContextAllocationFailure(), name, PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, VALGRIND_MAKE_MEM_NOACCESS, VALGRIND_MAKE_MEM_UNDEFINED, and WARNING.

◆ GenerationReset()

void GenerationReset ( MemoryContext  context)

Definition at line 283 of file generation.c.

284{
285 GenerationContext *set = (GenerationContext *) context;
286 dlist_mutable_iter miter;
287
289
290#ifdef MEMORY_CONTEXT_CHECKING
291 /* Check for corruption and leaks before freeing */
292 GenerationCheck(context);
293#endif
294
295 /*
296 * NULLify the free block pointer. We must do this before calling
297 * GenerationBlockFree as that function never expects to free the
298 * freeblock.
299 */
300 set->freeblock = NULL;
301
302 dlist_foreach_modify(miter, &set->blocks)
303 {
304 GenerationBlock *block = dlist_container(GenerationBlock, node, miter.cur);
305
306 if (IsKeeperBlock(set, block))
308 else
309 GenerationBlockFree(set, block);
310 }
311
312 /* set it so new allocations to make use of the keeper block */
313 set->block = KeeperBlock(set);
314
315 /* Reset block size allocation sequence, too */
316 set->nextBlockSize = set->initBlockSize;
317
318 /* Ensure there is only 1 item in the dlist */
321}
static bool dlist_has_next(const dlist_head *head, const dlist_node *node)
Definition: ilist.h:503
static dlist_node * dlist_head_node(dlist_head *head)
Definition: ilist.h:565
#define dlist_foreach_modify(iter, lhead)
Definition: ilist.h:640
static bool dlist_is_empty(const dlist_head *head)
Definition: ilist.h:336
dlist_node * cur
Definition: ilist.h:200

References Assert(), GenerationContext::block, GenerationContext::blocks, dlist_mutable_iter::cur, dlist_container, dlist_foreach_modify, dlist_has_next(), dlist_head_node(), dlist_is_empty(), GenerationContext::freeblock, GenerationBlockFree(), GenerationBlockMarkEmpty(), GenerationIsValid, GenerationContext::initBlockSize, IsKeeperBlock, KeeperBlock, and GenerationContext::nextBlockSize.

Referenced by GenerationDelete().

◆ GenerationStats()

void GenerationStats ( MemoryContext  context,
MemoryStatsPrintFunc  printfunc,
void *  passthru,
MemoryContextCounters totals,
bool  print_to_stderr 
)

Definition at line 1033 of file generation.c.

1036{
1037 GenerationContext *set = (GenerationContext *) context;
1038 Size nblocks = 0;
1039 Size nchunks = 0;
1040 Size nfreechunks = 0;
1041 Size totalspace;
1042 Size freespace = 0;
1043 dlist_iter iter;
1044
1046
1047 /* Include context header in totalspace */
1048 totalspace = MAXALIGN(sizeof(GenerationContext));
1049
1050 dlist_foreach(iter, &set->blocks)
1051 {
1052 GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
1053
1054 nblocks++;
1055 nchunks += block->nchunks;
1056 nfreechunks += block->nfree;
1057 totalspace += block->blksize;
1058 freespace += (block->endptr - block->freeptr);
1059 }
1060
1061 if (printfunc)
1062 {
1063 char stats_string[200];
1064
1065 snprintf(stats_string, sizeof(stats_string),
1066 "%zu total in %zu blocks (%zu chunks); %zu free (%zu chunks); %zu used",
1067 totalspace, nblocks, nchunks, freespace,
1068 nfreechunks, totalspace - freespace);
1069 printfunc(context, passthru, stats_string, print_to_stderr);
1070 }
1071
1072 if (totals)
1073 {
1074 totals->nblocks += nblocks;
1075 totals->freechunks += nfreechunks;
1076 totals->totalspace += totalspace;
1077 totals->freespace += freespace;
1078 }
1079}
#define snprintf
Definition: port.h:239

References Assert(), GenerationBlock::blksize, GenerationContext::blocks, dlist_iter::cur, dlist_container, dlist_foreach, GenerationBlock::endptr, MemoryContextCounters::freechunks, GenerationBlock::freeptr, MemoryContextCounters::freespace, GenerationIsValid, MAXALIGN, MemoryContextCounters::nblocks, GenerationBlock::nchunks, GenerationBlock::nfree, snprintf, and MemoryContextCounters::totalspace.