PostgreSQL Source Code  git master
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_memorychunk.h"
#include "utils/memutils_internal.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 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 bool GenerationBlockIsEmpty (GenerationBlock *block)
 
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)
 
void * GenerationAlloc (MemoryContext context, Size size)
 
void GenerationFree (void *pointer)
 
void * GenerationRealloc (void *pointer, Size size)
 
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 119 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.

◆ GenerationBlockIsValid

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

Definition at line 111 of file generation.c.

◆ GenerationIsValid

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

Definition at line 104 of file generation.c.

◆ IsKeeperBlock

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

Definition at line 128 of file generation.c.

◆ KeeperBlock

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

Definition at line 123 of file generation.c.

Typedef Documentation

◆ GenerationBlock

Definition at line 1 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 
)

Definition at line 345 of file generation.c.

346 {
347  GenerationContext *set = (GenerationContext *) context;
348  GenerationBlock *block;
349  MemoryChunk *chunk;
350  Size chunk_size;
351  Size required_size;
352 
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 
363  /* is it an over-sized chunk? if yes, allocate special block */
364  if (chunk_size > set->allocChunkLimit)
365  {
366  Size blksize = required_size + Generation_BLOCKHDRSZ;
367 
368  block = (GenerationBlock *) malloc(blksize);
369  if (block == NULL)
370  return NULL;
371 
372  context->mem_allocated += blksize;
373 
374  /* block with a single (used) chunk */
375  block->context = set;
376  block->blksize = blksize;
377  block->nchunks = 1;
378  block->nfree = 0;
379 
380  /* the block is completely full */
381  block->freeptr = block->endptr = ((char *) block) + blksize;
382 
383  chunk = (MemoryChunk *) (((char *) block) + Generation_BLOCKHDRSZ);
384 
385  /* mark the MemoryChunk as externally managed */
387 
388 #ifdef MEMORY_CONTEXT_CHECKING
389  chunk->requested_size = size;
390  /* set mark to catch clobber of "unused" space */
391  Assert(size < chunk_size);
392  set_sentinel(MemoryChunkGetPointer(chunk), size);
393 #endif
394 #ifdef RANDOMIZE_ALLOCATED_MEMORY
395  /* fill the allocated space with junk */
396  randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
397 #endif
398 
399  /* add the block to the list of allocated blocks */
400  dlist_push_head(&set->blocks, &block->node);
401 
402  /* Ensure any padding bytes are marked NOACCESS. */
403  VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
404  chunk_size - size);
405 
406  /* Disallow access to the chunk header. */
408 
409  return MemoryChunkGetPointer(chunk);
410  }
411 
412  /*
413  * Not an oversized chunk. We try to first make use of the current block,
414  * but if there's not enough space in it, instead of allocating a new
415  * block, we look to see if the freeblock is empty and has enough space.
416  * If not, we'll also try the same using the keeper block. The keeper
417  * block may have become empty and we have no other way to reuse it again
418  * if we don't try to use it explicitly here.
419  *
420  * We don't want to start filling the freeblock before the current block
421  * is full, otherwise we may cause fragmentation in FIFO type workloads.
422  * We only switch to using the freeblock or keeper block if those blocks
423  * are completely empty. If we didn't do that we could end up fragmenting
424  * consecutive allocations over multiple blocks which would be a problem
425  * that would compound over time.
426  */
427  block = set->block;
428 
429  if (block == NULL ||
430  GenerationBlockFreeBytes(block) < required_size)
431  {
432  Size blksize;
433  GenerationBlock *freeblock = set->freeblock;
434 
435  if (freeblock != NULL &&
436  GenerationBlockIsEmpty(freeblock) &&
437  GenerationBlockFreeBytes(freeblock) >= required_size)
438  {
439  block = freeblock;
440 
441  /*
442  * Zero out the freeblock as we'll set this to the current block
443  * below
444  */
445  set->freeblock = NULL;
446  }
447  else if (GenerationBlockIsEmpty(KeeperBlock(set)) &&
448  GenerationBlockFreeBytes(KeeperBlock(set)) >= required_size)
449  {
450  block = KeeperBlock(set);
451  }
452  else
453  {
454  /*
455  * The first such block has size initBlockSize, and we double the
456  * space in each succeeding block, but not more than maxBlockSize.
457  */
458  blksize = set->nextBlockSize;
459  set->nextBlockSize <<= 1;
460  if (set->nextBlockSize > set->maxBlockSize)
461  set->nextBlockSize = set->maxBlockSize;
462 
463  /* we'll need a block hdr too, so add that to the required size */
464  required_size += Generation_BLOCKHDRSZ;
465 
466  /* round the size up to the next power of 2 */
467  if (blksize < required_size)
468  blksize = pg_nextpower2_size_t(required_size);
469 
470  block = (GenerationBlock *) malloc(blksize);
471 
472  if (block == NULL)
473  return NULL;
474 
475  context->mem_allocated += blksize;
476 
477  /* initialize the new block */
478  GenerationBlockInit(set, block, blksize);
479 
480  /* add it to the doubly-linked list of blocks */
481  dlist_push_head(&set->blocks, &block->node);
482 
483  /* Zero out the freeblock in case it's become full */
484  set->freeblock = NULL;
485  }
486 
487  /* and also use it as the current allocation block */
488  set->block = block;
489  }
490 
491  /* we're supposed to have a block with enough free space now */
492  Assert(block != NULL);
493  Assert((block->endptr - block->freeptr) >= Generation_CHUNKHDRSZ + chunk_size);
494 
495  chunk = (MemoryChunk *) block->freeptr;
496 
497  /* Prepare to initialize the chunk header. */
499 
500  block->nchunks += 1;
501  block->freeptr += (Generation_CHUNKHDRSZ + chunk_size);
502 
503  Assert(block->freeptr <= block->endptr);
504 
505  MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_GENERATION_ID);
506 #ifdef MEMORY_CONTEXT_CHECKING
507  chunk->requested_size = size;
508  /* set mark to catch clobber of "unused" space */
509  Assert(size < chunk_size);
510  set_sentinel(MemoryChunkGetPointer(chunk), size);
511 #endif
512 #ifdef RANDOMIZE_ALLOCATED_MEMORY
513  /* fill the allocated space with junk */
514  randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
515 #endif
516 
517  /* Ensure any padding bytes are marked NOACCESS. */
518  VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
519  chunk_size - size);
520 
521  /* Disallow access to the chunk header. */
523 
524  return MemoryChunkGetPointer(chunk);
525 }
#define MAXALIGN(LEN)
Definition: c.h:800
size_t Size
Definition: c.h:594
static void GenerationBlockInit(GenerationContext *context, GenerationBlock *block, Size blksize)
Definition: generation.c:533
static Size GenerationBlockFreeBytes(GenerationBlock *block)
Definition: generation.c:588
#define KeeperBlock(set)
Definition: generation.c:123
#define Generation_CHUNKHDRSZ
Definition: generation.c:47
#define Generation_BLOCKHDRSZ
Definition: generation.c:46
static bool GenerationBlockIsEmpty(GenerationBlock *block)
Definition: generation.c:554
#define GenerationIsValid(set)
Definition: generation.c:104
#define malloc(a)
Definition: header.h:50
static void dlist_push_head(dlist_head *head, dlist_node *node)
Definition: ilist.h:347
Assert(fmt[strlen(fmt) - 1] !='\n')
#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 MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk, MemoryContextMethodID methodid)
static void MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block, Size value, MemoryContextMethodID methodid)
#define pg_nextpower2_size_t
Definition: pg_bitutils.h:339
char * freeptr
Definition: generation.c:96
dlist_node node
Definition: generation.c:91
GenerationContext * context
Definition: generation.c:92
GenerationBlock * freeblock
Definition: generation.c:72
uint32 maxBlockSize
Definition: generation.c:65
uint32 nextBlockSize
Definition: generation.c:66
dlist_head blocks
Definition: generation.c:74
GenerationBlock * block
Definition: generation.c:69
uint32 allocChunkLimit
Definition: generation.c:67
Size mem_allocated
Definition: memnodes.h:87

References GenerationContext::allocChunkLimit, Assert(), GenerationBlock::blksize, GenerationContext::block, GenerationContext::blocks, GenerationBlock::context, dlist_push_head(), GenerationBlock::endptr, GenerationContext::freeblock, GenerationBlock::freeptr, Generation_BLOCKHDRSZ, Generation_CHUNKHDRSZ, GenerationBlockFreeBytes(), GenerationBlockInit(), GenerationBlockIsEmpty(), GenerationIsValid, KeeperBlock, malloc, MAXALIGN, GenerationContext::maxBlockSize, MCTX_GENERATION_ID, MemoryContextData::mem_allocated, MemoryChunkGetPointer, MemoryChunkSetHdrMask(), MemoryChunkSetHdrMaskExternal(), GenerationBlock::nchunks, GenerationContext::nextBlockSize, GenerationBlock::nfree, GenerationBlock::node, pg_nextpower2_size_t, VALGRIND_MAKE_MEM_NOACCESS, and VALGRIND_MAKE_MEM_UNDEFINED.

Referenced by GenerationRealloc().

◆ GenerationBlockFree()

static void GenerationBlockFree ( GenerationContext set,
GenerationBlock block 
)
inlinestatic

Definition at line 598 of file generation.c.

599 {
600  /* Make sure nobody tries to free the keeper block */
601  Assert(!IsKeeperBlock(set, block));
602  /* We shouldn't be freeing the freeblock either */
603  Assert(block != set->freeblock);
604 
605  /* release the block from the list of blocks */
606  dlist_delete(&block->node);
607 
608  ((MemoryContext) set)->mem_allocated -= block->blksize;
609 
610 #ifdef CLOBBER_FREED_MEMORY
611  wipe_mem(block, block->blksize);
612 #endif
613 
614  free(block);
615 }
#define IsKeeperBlock(set, block)
Definition: generation.c:128
#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 GenerationReset().

◆ GenerationBlockFreeBytes()

static Size GenerationBlockFreeBytes ( GenerationBlock block)
inlinestatic

Definition at line 588 of file generation.c.

589 {
590  return (block->endptr - block->freeptr);
591 }

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

Referenced by GenerationAlloc().

◆ GenerationBlockInit()

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

Definition at line 533 of file generation.c.

535 {
536  block->context = context;
537  block->blksize = blksize;
538  block->nchunks = 0;
539  block->nfree = 0;
540 
541  block->freeptr = ((char *) block) + Generation_BLOCKHDRSZ;
542  block->endptr = ((char *) block) + blksize;
543 
544  /* Mark unallocated space NOACCESS. */
546  blksize - Generation_BLOCKHDRSZ);
547 }

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

Referenced by GenerationAlloc(), and GenerationContextCreate().

◆ GenerationBlockIsEmpty()

static bool GenerationBlockIsEmpty ( GenerationBlock block)
inlinestatic

Definition at line 554 of file generation.c.

555 {
556  return (block->nchunks == 0);
557 }

References GenerationBlock::nchunks.

Referenced by GenerationAlloc().

◆ GenerationBlockMarkEmpty()

static void GenerationBlockMarkEmpty ( GenerationBlock block)
inlinestatic

Definition at line 564 of file generation.c.

565 {
566 #if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
567  char *datastart = ((char *) block) + Generation_BLOCKHDRSZ;
568 #endif
569 
570 #ifdef CLOBBER_FREED_MEMORY
571  wipe_mem(datastart, block->freeptr - datastart);
572 #else
573  /* wipe_mem() would have done this */
574  VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
575 #endif
576 
577  /* Reset the block, but don't return it to malloc */
578  block->nchunks = 0;
579  block->nfree = 0;
580  block->freeptr = ((char *) block) + Generation_BLOCKHDRSZ;
581 }

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 157 of file generation.c.

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

◆ GenerationDelete()

void GenerationDelete ( MemoryContext  context)

Definition at line 323 of file generation.c.

324 {
325  /* Reset to release all releasable GenerationBlocks */
326  GenerationReset(context);
327  /* And free the context header and keeper block */
328  free(context);
329 }
void GenerationReset(MemoryContext context)
Definition: generation.c:278

References free, and GenerationReset().

◆ GenerationFree()

void GenerationFree ( void *  pointer)

Definition at line 623 of file generation.c.

624 {
625  MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
626  GenerationBlock *block;
627  GenerationContext *set;
628 #if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
629  || defined(CLOBBER_FREED_MEMORY)
630  Size chunksize;
631 #endif
632 
633  /* Allow access to the chunk header. */
635 
636  if (MemoryChunkIsExternal(chunk))
637  {
638  block = ExternalChunkGetBlock(chunk);
639 
640  /*
641  * Try to verify that we have a sane block pointer: the block header
642  * should reference a generation context.
643  */
644  if (!GenerationBlockIsValid(block))
645  elog(ERROR, "could not find block containing chunk %p", chunk);
646 
647 #if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
648  || defined(CLOBBER_FREED_MEMORY)
649  chunksize = block->endptr - (char *) pointer;
650 #endif
651  }
652  else
653  {
654  block = MemoryChunkGetBlock(chunk);
655 
656  /*
657  * In this path, for speed reasons we just Assert that the referenced
658  * block is good. Future field experience may show that this Assert
659  * had better become a regular runtime test-and-elog check.
660  */
662 
663 #if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
664  || defined(CLOBBER_FREED_MEMORY)
665  chunksize = MemoryChunkGetValue(chunk);
666 #endif
667  }
668 
669 #ifdef MEMORY_CONTEXT_CHECKING
670  /* Test for someone scribbling on unused space in chunk */
671  Assert(chunk->requested_size < chunksize);
672  if (!sentinel_ok(pointer, chunk->requested_size))
673  elog(WARNING, "detected write past chunk end in %s %p",
674  ((MemoryContext) block->context)->name, chunk);
675 #endif
676 
677 #ifdef CLOBBER_FREED_MEMORY
678  wipe_mem(pointer, chunksize);
679 #endif
680 
681 #ifdef MEMORY_CONTEXT_CHECKING
682  /* Reset requested_size to InvalidAllocSize in freed chunks */
683  chunk->requested_size = InvalidAllocSize;
684 #endif
685 
686  block->nfree += 1;
687 
688  Assert(block->nchunks > 0);
689  Assert(block->nfree <= block->nchunks);
690 
691  /* If there are still allocated chunks in the block, we're done. */
692  if (block->nfree < block->nchunks)
693  return;
694 
695  set = block->context;
696 
697  /* Don't try to free the keeper block, just mark it empty */
698  if (IsKeeperBlock(set, block))
699  {
701  return;
702  }
703 
704  /*
705  * If there is no freeblock set or if this is the freeblock then instead
706  * of freeing this memory, we keep it around so that new allocations have
707  * the option of recycling it.
708  */
709  if (set->freeblock == NULL || set->freeblock == block)
710  {
711  /* XXX should we only recycle maxBlockSize sized blocks? */
712  set->freeblock = block;
714  return;
715  }
716 
717  /* Also make sure the block is not marked as the current block. */
718  if (set->block == block)
719  set->block = NULL;
720 
721  /*
722  * The block is empty, so let's get rid of it. First remove it from the
723  * list of blocks, then return it to malloc().
724  */
725  dlist_delete(&block->node);
726 
727  set->header.mem_allocated -= block->blksize;
728  free(block);
729 }
#define WARNING
Definition: elog.h:36
static void GenerationBlockMarkEmpty(GenerationBlock *block)
Definition: generation.c:564
#define GenerationBlockIsValid(block)
Definition: generation.c:111
#define ExternalChunkGetBlock(chunk)
Definition: generation.c:119
#define VALGRIND_MAKE_MEM_DEFINED(addr, size)
Definition: memdebug.h:26
#define InvalidAllocSize
Definition: memutils.h:47
static Size MemoryChunkGetValue(MemoryChunk *chunk)
static bool MemoryChunkIsExternal(MemoryChunk *chunk)
static void * MemoryChunkGetBlock(MemoryChunk *chunk)
#define PointerGetMemoryChunk(p)
MemoryContextData header
Definition: generation.c:61

References Assert(), GenerationBlock::blksize, GenerationContext::block, GenerationBlock::context, dlist_delete(), elog(), GenerationBlock::endptr, ERROR, ExternalChunkGetBlock, free, GenerationContext::freeblock, Generation_CHUNKHDRSZ, GenerationBlockIsValid, GenerationBlockMarkEmpty(), GenerationContext::header, InvalidAllocSize, IsKeeperBlock, MemoryContextData::mem_allocated, MemoryChunkGetBlock(), MemoryChunkGetValue(), MemoryChunkIsExternal(), GenerationBlock::nchunks, GenerationBlock::nfree, GenerationBlock::node, PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and WARNING.

Referenced by GenerationRealloc().

◆ GenerationGetChunkContext()

MemoryContext GenerationGetChunkContext ( void *  pointer)

Definition at line 880 of file generation.c.

881 {
882  MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
883  GenerationBlock *block;
884 
885  /* Allow access to the chunk header. */
887 
888  if (MemoryChunkIsExternal(chunk))
889  block = ExternalChunkGetBlock(chunk);
890  else
891  block = (GenerationBlock *) MemoryChunkGetBlock(chunk);
892 
893  /* Disallow access to the chunk header. */
895 
897  return &block->context->header;
898 }

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 906 of file generation.c.

907 {
908  MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
909  Size chunksize;
910 
911  /* Allow access to the chunk header. */
913 
914  if (MemoryChunkIsExternal(chunk))
915  {
916  GenerationBlock *block = ExternalChunkGetBlock(chunk);
917 
919  chunksize = block->endptr - (char *) pointer;
920  }
921  else
922  chunksize = MemoryChunkGetValue(chunk);
923 
924  /* Disallow access to the chunk header. */
926 
927  return Generation_CHUNKHDRSZ + chunksize;
928 }

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 935 of file generation.c.

936 {
937  GenerationContext *set = (GenerationContext *) context;
938  dlist_iter iter;
939 
941 
942  dlist_foreach(iter, &set->blocks)
943  {
944  GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
945 
946  if (block->nchunks > 0)
947  return false;
948  }
949 
950  return true;
951 }
#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 
)

Definition at line 738 of file generation.c.

739 {
740  MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
741  GenerationContext *set;
742  GenerationBlock *block;
743  GenerationPointer newPointer;
744  Size oldsize;
745 
746  /* Allow access to the chunk header. */
748 
749  if (MemoryChunkIsExternal(chunk))
750  {
751  block = ExternalChunkGetBlock(chunk);
752 
753  /*
754  * Try to verify that we have a sane block pointer: the block header
755  * should reference a generation context.
756  */
757  if (!GenerationBlockIsValid(block))
758  elog(ERROR, "could not find block containing chunk %p", chunk);
759 
760  oldsize = block->endptr - (char *) pointer;
761  }
762  else
763  {
764  block = MemoryChunkGetBlock(chunk);
765 
766  /*
767  * In this path, for speed reasons we just Assert that the referenced
768  * block is good. Future field experience may show that this Assert
769  * had better become a regular runtime test-and-elog check.
770  */
772 
773  oldsize = MemoryChunkGetValue(chunk);
774  }
775 
776  set = block->context;
777 
778 #ifdef MEMORY_CONTEXT_CHECKING
779  /* Test for someone scribbling on unused space in chunk */
780  Assert(chunk->requested_size < oldsize);
781  if (!sentinel_ok(pointer, chunk->requested_size))
782  elog(WARNING, "detected write past chunk end in %s %p",
783  ((MemoryContext) set)->name, chunk);
784 #endif
785 
786  /*
787  * Maybe the allocated area already is >= the new size. (In particular,
788  * we always fall out here if the requested size is a decrease.)
789  *
790  * This memory context does not use power-of-2 chunk sizing and instead
791  * carves the chunks to be as small as possible, so most repalloc() calls
792  * will end up in the palloc/memcpy/pfree branch.
793  *
794  * XXX Perhaps we should annotate this condition with unlikely()?
795  */
796  if (oldsize >= size)
797  {
798 #ifdef MEMORY_CONTEXT_CHECKING
799  Size oldrequest = chunk->requested_size;
800 
801 #ifdef RANDOMIZE_ALLOCATED_MEMORY
802  /* We can only fill the extra space if we know the prior request */
803  if (size > oldrequest)
804  randomize_mem((char *) pointer + oldrequest,
805  size - oldrequest);
806 #endif
807 
808  chunk->requested_size = size;
809 
810  /*
811  * If this is an increase, mark any newly-available part UNDEFINED.
812  * Otherwise, mark the obsolete part NOACCESS.
813  */
814  if (size > oldrequest)
815  VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + oldrequest,
816  size - oldrequest);
817  else
818  VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size,
819  oldsize - size);
820 
821  /* set mark to catch clobber of "unused" space */
822  set_sentinel(pointer, size);
823 #else /* !MEMORY_CONTEXT_CHECKING */
824 
825  /*
826  * We don't have the information to determine whether we're growing
827  * the old request or shrinking it, so we conservatively mark the
828  * entire new allocation DEFINED.
829  */
830  VALGRIND_MAKE_MEM_NOACCESS(pointer, oldsize);
831  VALGRIND_MAKE_MEM_DEFINED(pointer, size);
832 #endif
833 
834  /* Disallow access to the chunk header. */
836 
837  return pointer;
838  }
839 
840  /* allocate new chunk */
841  newPointer = GenerationAlloc((MemoryContext) set, size);
842 
843  /* leave immediately if request was not completed */
844  if (newPointer == NULL)
845  {
846  /* Disallow access to the chunk header. */
848  return NULL;
849  }
850 
851  /*
852  * GenerationAlloc() may have returned a region that is still NOACCESS.
853  * Change it to UNDEFINED for the moment; memcpy() will then transfer
854  * definedness from the old allocation to the new. If we know the old
855  * allocation, copy just that much. Otherwise, make the entire old chunk
856  * defined to avoid errors as we copy the currently-NOACCESS trailing
857  * bytes.
858  */
859  VALGRIND_MAKE_MEM_UNDEFINED(newPointer, size);
860 #ifdef MEMORY_CONTEXT_CHECKING
861  oldsize = chunk->requested_size;
862 #else
863  VALGRIND_MAKE_MEM_DEFINED(pointer, oldsize);
864 #endif
865 
866  /* transfer existing data (certain to fit) */
867  memcpy(newPointer, pointer, oldsize);
868 
869  /* free old chunk */
870  GenerationFree(pointer);
871 
872  return newPointer;
873 }
void GenerationFree(void *pointer)
Definition: generation.c:623
void * GenerationPointer
Definition: generation.c:53
void * GenerationAlloc(MemoryContext context, Size size)
Definition: generation.c:345

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

◆ GenerationReset()

void GenerationReset ( MemoryContext  context)

Definition at line 278 of file generation.c.

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

969 {
970  GenerationContext *set = (GenerationContext *) context;
971  Size nblocks = 0;
972  Size nchunks = 0;
973  Size nfreechunks = 0;
974  Size totalspace;
975  Size freespace = 0;
976  dlist_iter iter;
977 
979 
980  /* Include context header in totalspace */
981  totalspace = MAXALIGN(sizeof(GenerationContext));
982 
983  dlist_foreach(iter, &set->blocks)
984  {
985  GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
986 
987  nblocks++;
988  nchunks += block->nchunks;
989  nfreechunks += block->nfree;
990  totalspace += block->blksize;
991  freespace += (block->endptr - block->freeptr);
992  }
993 
994  if (printfunc)
995  {
996  char stats_string[200];
997 
998  snprintf(stats_string, sizeof(stats_string),
999  "%zu total in %zu blocks (%zu chunks); %zu free (%zu chunks); %zu used",
1000  totalspace, nblocks, nchunks, freespace,
1001  nfreechunks, totalspace - freespace);
1002  printfunc(context, passthru, stats_string, print_to_stderr);
1003  }
1004 
1005  if (totals)
1006  {
1007  totals->nblocks += nblocks;
1008  totals->freechunks += nfreechunks;
1009  totals->totalspace += totalspace;
1010  totals->freespace += freespace;
1011  }
1012 }
#define snprintf
Definition: port.h:238

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.