PostgreSQL Source Code  git master
aset.c File Reference
Include dependency graph for aset.c:

Go to the source code of this file.

Data Structures

struct  AllocFreeListLink
 
struct  AllocSetContext
 
struct  AllocBlockData
 
struct  AllocSetFreeList
 

Macros

#define ALLOC_MINBITS   3 /* smallest chunk size is 8 bytes */
 
#define ALLOCSET_NUM_FREELISTS   11
 
#define ALLOC_CHUNK_LIMIT   (1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS))
 
#define ALLOC_CHUNK_FRACTION   4
 
#define ALLOC_BLOCKHDRSZ   MAXALIGN(sizeof(AllocBlockData))
 
#define ALLOC_CHUNKHDRSZ   sizeof(MemoryChunk)
 
#define GetFreeListLink(chkptr)    (AllocFreeListLink *) ((char *) (chkptr) + ALLOC_CHUNKHDRSZ)
 
#define FreeListIdxIsValid(fidx)    ((fidx) >= 0 && (fidx) < ALLOCSET_NUM_FREELISTS)
 
#define GetChunkSizeFromFreeListIdx(fidx)    ((((Size) 1) << ALLOC_MINBITS) << (fidx))
 
#define AllocPointerIsValid(pointer)   PointerIsValid(pointer)
 
#define AllocSetIsValid(set)    (PointerIsValid(set) && IsA(set, AllocSetContext))
 
#define AllocBlockIsValid(block)    (PointerIsValid(block) && AllocSetIsValid((block)->aset))
 
#define ExternalChunkGetBlock(chunk)    (AllocBlock) ((char *) chunk - ALLOC_BLOCKHDRSZ)
 
#define MAX_FREE_CONTEXTS   100 /* arbitrary limit on freelist length */
 
#define KeeperBlock(set)    ((AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext))))
 
#define IsKeeperBlock(set, block)   ((block) == (KeeperBlock(set)))
 

Typedefs

typedef struct AllocBlockDataAllocBlock
 
typedef void * AllocPointer
 
typedef struct AllocFreeListLink AllocFreeListLink
 
typedef struct AllocSetContext AllocSetContext
 
typedef AllocSetContextAllocSet
 
typedef struct AllocBlockData AllocBlockData
 
typedef struct AllocSetFreeList AllocSetFreeList
 

Functions

static int AllocSetFreeIndex (Size size)
 
MemoryContext AllocSetContextCreateInternal (MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
 
void AllocSetReset (MemoryContext context)
 
void AllocSetDelete (MemoryContext context)
 
static pg_noinline void * AllocSetAllocLarge (MemoryContext context, Size size, int flags)
 
static void * AllocSetAllocChunkFromBlock (MemoryContext context, AllocBlock block, Size size, Size chunk_size, int fidx)
 
static pg_noinline void * AllocSetAllocFromNewBlock (MemoryContext context, Size size, int flags, int fidx)
 
void * AllocSetAlloc (MemoryContext context, Size size, int flags)
 
void AllocSetFree (void *pointer)
 
void * AllocSetRealloc (void *pointer, Size size, int flags)
 
MemoryContext AllocSetGetChunkContext (void *pointer)
 
Size AllocSetGetChunkSpace (void *pointer)
 
bool AllocSetIsEmpty (MemoryContext context)
 
void AllocSetStats (MemoryContext context, MemoryStatsPrintFunc printfunc, void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
 

Variables

static AllocSetFreeList context_freelists [2]
 

Macro Definition Documentation

◆ ALLOC_BLOCKHDRSZ

#define ALLOC_BLOCKHDRSZ   MAXALIGN(sizeof(AllocBlockData))

Definition at line 104 of file aset.c.

◆ ALLOC_CHUNK_FRACTION

#define ALLOC_CHUNK_FRACTION   4

Definition at line 87 of file aset.c.

◆ ALLOC_CHUNK_LIMIT

#define ALLOC_CHUNK_LIMIT   (1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS))

Definition at line 85 of file aset.c.

◆ ALLOC_CHUNKHDRSZ

#define ALLOC_CHUNKHDRSZ   sizeof(MemoryChunk)

Definition at line 105 of file aset.c.

◆ ALLOC_MINBITS

#define ALLOC_MINBITS   3 /* smallest chunk size is 8 bytes */

Definition at line 83 of file aset.c.

◆ AllocBlockIsValid

#define AllocBlockIsValid (   block)     (PointerIsValid(block) && AllocSetIsValid((block)->aset))

Definition at line 207 of file aset.c.

◆ AllocPointerIsValid

#define AllocPointerIsValid (   pointer)    PointerIsValid(pointer)

Definition at line 194 of file aset.c.

◆ ALLOCSET_NUM_FREELISTS

#define ALLOCSET_NUM_FREELISTS   11

Definition at line 84 of file aset.c.

◆ AllocSetIsValid

#define AllocSetIsValid (   set)     (PointerIsValid(set) && IsA(set, AllocSetContext))

Definition at line 200 of file aset.c.

◆ ExternalChunkGetBlock

#define ExternalChunkGetBlock (   chunk)     (AllocBlock) ((char *) chunk - ALLOC_BLOCKHDRSZ)

Definition at line 215 of file aset.c.

◆ FreeListIdxIsValid

#define FreeListIdxIsValid (   fidx)     ((fidx) >= 0 && (fidx) < ALLOCSET_NUM_FREELISTS)

Definition at line 136 of file aset.c.

◆ GetChunkSizeFromFreeListIdx

#define GetChunkSizeFromFreeListIdx (   fidx)     ((((Size) 1) << ALLOC_MINBITS) << (fidx))

Definition at line 140 of file aset.c.

◆ GetFreeListLink

#define GetFreeListLink (   chkptr)     (AllocFreeListLink *) ((char *) (chkptr) + ALLOC_CHUNKHDRSZ)

Definition at line 132 of file aset.c.

◆ IsKeeperBlock

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

Definition at line 248 of file aset.c.

◆ KeeperBlock

#define KeeperBlock (   set)     ((AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext))))

Definition at line 244 of file aset.c.

◆ MAX_FREE_CONTEXTS

#define MAX_FREE_CONTEXTS   100 /* arbitrary limit on freelist length */

Definition at line 241 of file aset.c.

Typedef Documentation

◆ AllocBlock

typedef struct AllocBlockData* AllocBlock

Definition at line 107 of file aset.c.

◆ AllocBlockData

◆ AllocFreeListLink

◆ AllocPointer

typedef void* AllocPointer

Definition at line 113 of file aset.c.

◆ AllocSet

Definition at line 167 of file aset.c.

◆ AllocSetContext

◆ AllocSetFreeList

Function Documentation

◆ AllocSetAlloc()

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

Definition at line 967 of file aset.c.

968 {
969  AllocSet set = (AllocSet) context;
970  AllocBlock block;
971  MemoryChunk *chunk;
972  int fidx;
973  Size chunk_size;
974  Size availspace;
975 
976  Assert(AllocSetIsValid(set));
977 
978  /* due to the keeper block set->blocks should never be NULL */
979  Assert(set->blocks != NULL);
980 
981  /*
982  * If requested size exceeds maximum for chunks we hand the the request
983  * off to AllocSetAllocLarge().
984  */
985  if (size > set->allocChunkLimit)
986  return AllocSetAllocLarge(context, size, flags);
987 
988  /*
989  * Request is small enough to be treated as a chunk. Look in the
990  * corresponding free list to see if there is a free chunk we could reuse.
991  * If one is found, remove it from the free list, make it again a member
992  * of the alloc set and return its data address.
993  *
994  * Note that we don't attempt to ensure there's space for the sentinel
995  * byte here. We expect a large proportion of allocations to be for sizes
996  * which are already a power of 2. If we were to always make space for a
997  * sentinel byte in MEMORY_CONTEXT_CHECKING builds, then we'd end up
998  * doubling the memory requirements for such allocations.
999  */
1000  fidx = AllocSetFreeIndex(size);
1001  chunk = set->freelist[fidx];
1002  if (chunk != NULL)
1003  {
1005 
1006  /* Allow access to the chunk header. */
1008 
1009  Assert(fidx == MemoryChunkGetValue(chunk));
1010 
1011  /* pop this chunk off the freelist */
1013  set->freelist[fidx] = link->next;
1015 
1016 #ifdef MEMORY_CONTEXT_CHECKING
1017  chunk->requested_size = size;
1018  /* set mark to catch clobber of "unused" space */
1019  if (size < GetChunkSizeFromFreeListIdx(fidx))
1020  set_sentinel(MemoryChunkGetPointer(chunk), size);
1021 #endif
1022 #ifdef RANDOMIZE_ALLOCATED_MEMORY
1023  /* fill the allocated space with junk */
1024  randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
1025 #endif
1026 
1027  /* Ensure any padding bytes are marked NOACCESS. */
1030 
1031  /* Disallow access to the chunk header. */
1033 
1034  return MemoryChunkGetPointer(chunk);
1035  }
1036 
1037  /*
1038  * Choose the actual chunk size to allocate.
1039  */
1040  chunk_size = GetChunkSizeFromFreeListIdx(fidx);
1041  Assert(chunk_size >= size);
1042 
1043  block = set->blocks;
1044  availspace = block->endptr - block->freeptr;
1045 
1046  /*
1047  * If there is enough room in the active allocation block, we will put the
1048  * chunk into that block. Else must start a new one.
1049  */
1050  if (unlikely(availspace < (chunk_size + ALLOC_CHUNKHDRSZ)))
1051  return AllocSetAllocFromNewBlock(context, size, flags, fidx);
1052 
1053  /* There's enough space on the current block, so allocate from that */
1054  return AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx);
1055 }
#define AllocSetIsValid(set)
Definition: aset.c:200
#define GetFreeListLink(chkptr)
Definition: aset.c:132
#define ALLOC_CHUNKHDRSZ
Definition: aset.c:105
static void * AllocSetAllocChunkFromBlock(MemoryContext context, AllocBlock block, Size size, Size chunk_size, int fidx)
Definition: aset.c:774
#define GetChunkSizeFromFreeListIdx(fidx)
Definition: aset.c:140
static int AllocSetFreeIndex(Size size)
Definition: aset.c:277
static pg_noinline void * AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags, int fidx)
Definition: aset.c:819
static pg_noinline void * AllocSetAllocLarge(MemoryContext context, Size size, int flags)
Definition: aset.c:696
AllocSetContext * AllocSet
Definition: aset.c:167
#define unlikely(x)
Definition: c.h:298
size_t Size
Definition: c.h:592
Assert(fmt[strlen(fmt) - 1] !='\n')
#define VALGRIND_MAKE_MEM_DEFINED(addr, size)
Definition: memdebug.h:26
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
Definition: memdebug.h:27
static Size MemoryChunkGetValue(MemoryChunk *chunk)
#define MemoryChunkGetPointer(c)
static pg_noinline void Size size
Definition: slab.c:607
char * freeptr
Definition: aset.c:186
char * endptr
Definition: aset.c:187
uint32 allocChunkLimit
Definition: aset.c:162
AllocBlock blocks
Definition: aset.c:156
MemoryChunk * freelist[ALLOCSET_NUM_FREELISTS]
Definition: aset.c:157

References ALLOC_CHUNKHDRSZ, AllocSetContext::allocChunkLimit, AllocSetAllocChunkFromBlock(), AllocSetAllocFromNewBlock(), AllocSetAllocLarge(), AllocSetFreeIndex(), AllocSetIsValid, Assert(), AllocSetContext::blocks, AllocBlockData::endptr, AllocSetContext::freelist, AllocBlockData::freeptr, GetChunkSizeFromFreeListIdx, GetFreeListLink, MemoryChunkGetPointer, MemoryChunkGetValue(), size, unlikely, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

Referenced by AllocSetRealloc().

◆ AllocSetAllocChunkFromBlock()

static void* AllocSetAllocChunkFromBlock ( MemoryContext  context,
AllocBlock  block,
Size  size,
Size  chunk_size,
int  fidx 
)
inlinestatic

Definition at line 774 of file aset.c.

776 {
777  MemoryChunk *chunk;
778 
779  chunk = (MemoryChunk *) (block->freeptr);
780 
781  /* Prepare to initialize the chunk header. */
783 
784  block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
785  Assert(block->freeptr <= block->endptr);
786 
787  /* store the free list index in the value field */
788  MemoryChunkSetHdrMask(chunk, block, fidx, MCTX_ASET_ID);
789 
790 #ifdef MEMORY_CONTEXT_CHECKING
791  chunk->requested_size = size;
792  /* set mark to catch clobber of "unused" space */
793  if (size < chunk_size)
794  set_sentinel(MemoryChunkGetPointer(chunk), size);
795 #endif
796 #ifdef RANDOMIZE_ALLOCATED_MEMORY
797  /* fill the allocated space with junk */
798  randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
799 #endif
800 
801  /* Ensure any padding bytes are marked NOACCESS. */
803  chunk_size - size);
804 
805  /* Disallow access to the chunk header. */
807 
808  return MemoryChunkGetPointer(chunk);
809 }
#define VALGRIND_MAKE_MEM_UNDEFINED(addr, size)
Definition: memdebug.h:28
@ MCTX_ASET_ID
static void MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block, Size value, MemoryContextMethodID methodid)

References ALLOC_CHUNKHDRSZ, Assert(), AllocBlockData::endptr, AllocBlockData::freeptr, MCTX_ASET_ID, MemoryChunkGetPointer, MemoryChunkSetHdrMask(), size, VALGRIND_MAKE_MEM_NOACCESS, and VALGRIND_MAKE_MEM_UNDEFINED.

Referenced by AllocSetAlloc(), and AllocSetAllocFromNewBlock().

◆ AllocSetAllocFromNewBlock()

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

Definition at line 819 of file aset.c.

821 {
822  AllocSet set = (AllocSet) context;
823  AllocBlock block;
824  Size availspace;
825  Size blksize;
826  Size required_size;
827  Size chunk_size;
828 
829  /* due to the keeper block set->blocks should always be valid */
830  Assert(set->blocks != NULL);
831  block = set->blocks;
832  availspace = block->endptr - block->freeptr;
833 
834  /*
835  * The existing active (top) block does not have enough room for the
836  * requested allocation, but it might still have a useful amount of space
837  * in it. Once we push it down in the block list, we'll never try to
838  * allocate more space from it. So, before we do that, carve up its free
839  * space into chunks that we can put on the set's freelists.
840  *
841  * Because we can only get here when there's less than ALLOC_CHUNK_LIMIT
842  * left in the block, this loop cannot iterate more than
843  * ALLOCSET_NUM_FREELISTS-1 times.
844  */
845  while (availspace >= ((1 << ALLOC_MINBITS) + ALLOC_CHUNKHDRSZ))
846  {
848  MemoryChunk *chunk;
849  Size availchunk = availspace - ALLOC_CHUNKHDRSZ;
850  int a_fidx = AllocSetFreeIndex(availchunk);
851 
852  /*
853  * In most cases, we'll get back the index of the next larger freelist
854  * than the one we need to put this chunk on. The exception is when
855  * availchunk is exactly a power of 2.
856  */
857  if (availchunk != GetChunkSizeFromFreeListIdx(a_fidx))
858  {
859  a_fidx--;
860  Assert(a_fidx >= 0);
861  availchunk = GetChunkSizeFromFreeListIdx(a_fidx);
862  }
863 
864  chunk = (MemoryChunk *) (block->freeptr);
865 
866  /* Prepare to initialize the chunk header. */
868  block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ);
869  availspace -= (availchunk + ALLOC_CHUNKHDRSZ);
870 
871  /* store the freelist index in the value field */
872  MemoryChunkSetHdrMask(chunk, block, a_fidx, MCTX_ASET_ID);
873 #ifdef MEMORY_CONTEXT_CHECKING
874  chunk->requested_size = InvalidAllocSize; /* mark it free */
875 #endif
876  /* push this chunk onto the free list */
877  link = GetFreeListLink(chunk);
878 
880  link->next = set->freelist[a_fidx];
882 
883  set->freelist[a_fidx] = chunk;
884  }
885 
886  /*
887  * The first such block has size initBlockSize, and we double the space in
888  * each succeeding block, but not more than maxBlockSize.
889  */
890  blksize = set->nextBlockSize;
891  set->nextBlockSize <<= 1;
892  if (set->nextBlockSize > set->maxBlockSize)
893  set->nextBlockSize = set->maxBlockSize;
894 
895  /* Choose the actual chunk size to allocate */
896  chunk_size = GetChunkSizeFromFreeListIdx(fidx);
897  Assert(chunk_size >= size);
898 
899  /*
900  * If initBlockSize is less than ALLOC_CHUNK_LIMIT, we could need more
901  * space... but try to keep it a power of 2.
902  */
903  required_size = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
904  while (blksize < required_size)
905  blksize <<= 1;
906 
907  /* Try to allocate it */
908  block = (AllocBlock) malloc(blksize);
909 
910  /*
911  * We could be asking for pretty big blocks here, so cope if malloc fails.
912  * But give up if there's less than 1 MB or so available...
913  */
914  while (block == NULL && blksize > 1024 * 1024)
915  {
916  blksize >>= 1;
917  if (blksize < required_size)
918  break;
919  block = (AllocBlock) malloc(blksize);
920  }
921 
922  if (block == NULL)
923  return MemoryContextAllocationFailure(context, size, flags);
924 
925  context->mem_allocated += blksize;
926 
927  block->aset = set;
928  block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
929  block->endptr = ((char *) block) + blksize;
930 
931  /* Mark unallocated space NOACCESS. */
933  blksize - ALLOC_BLOCKHDRSZ);
934 
935  block->prev = NULL;
936  block->next = set->blocks;
937  if (block->next)
938  block->next->prev = block;
939  set->blocks = block;
940 
941  return AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx);
942 }
#define ALLOC_MINBITS
Definition: aset.c:83
struct AllocBlockData * AllocBlock
Definition: aset.c:107
#define ALLOC_BLOCKHDRSZ
Definition: aset.c:104
#define malloc(a)
Definition: header.h:50
void * MemoryContextAllocationFailure(MemoryContext context, Size size, int flags)
Definition: mcxt.c:1135
#define InvalidAllocSize
Definition: memutils.h:47
AllocBlock prev
Definition: aset.c:184
AllocSet aset
Definition: aset.c:183
AllocBlock next
Definition: aset.c:185
uint32 maxBlockSize
Definition: aset.c:160
uint32 nextBlockSize
Definition: aset.c:161

References ALLOC_BLOCKHDRSZ, ALLOC_CHUNKHDRSZ, ALLOC_MINBITS, AllocSetAllocChunkFromBlock(), AllocSetFreeIndex(), AllocBlockData::aset, Assert(), AllocSetContext::blocks, AllocBlockData::endptr, AllocSetContext::freelist, AllocBlockData::freeptr, GetChunkSizeFromFreeListIdx, GetFreeListLink, InvalidAllocSize, link(), malloc, AllocSetContext::maxBlockSize, MCTX_ASET_ID, MemoryContextData::mem_allocated, MemoryChunkSetHdrMask(), MemoryContextAllocationFailure(), AllocBlockData::next, AllocSetContext::nextBlockSize, AllocBlockData::prev, size, VALGRIND_MAKE_MEM_DEFINED, VALGRIND_MAKE_MEM_NOACCESS, and VALGRIND_MAKE_MEM_UNDEFINED.

Referenced by AllocSetAlloc().

◆ AllocSetAllocLarge()

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

Definition at line 696 of file aset.c.

697 {
698  AllocSet set = (AllocSet) context;
699  AllocBlock block;
700  MemoryChunk *chunk;
701  Size chunk_size;
702  Size blksize;
703 
704  /* validate 'size' is within the limits for the given 'flags' */
705  MemoryContextCheckSize(context, size, flags);
706 
707 #ifdef MEMORY_CONTEXT_CHECKING
708  /* ensure there's always space for the sentinel byte */
709  chunk_size = MAXALIGN(size + 1);
710 #else
711  chunk_size = MAXALIGN(size);
712 #endif
713 
714  blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
715  block = (AllocBlock) malloc(blksize);
716  if (block == NULL)
717  return MemoryContextAllocationFailure(context, size, flags);
718 
719  context->mem_allocated += blksize;
720 
721  block->aset = set;
722  block->freeptr = block->endptr = ((char *) block) + blksize;
723 
724  chunk = (MemoryChunk *) (((char *) block) + ALLOC_BLOCKHDRSZ);
725 
726  /* mark the MemoryChunk as externally managed */
728 
729 #ifdef MEMORY_CONTEXT_CHECKING
730  chunk->requested_size = size;
731  /* set mark to catch clobber of "unused" space */
732  Assert(size < chunk_size);
733  set_sentinel(MemoryChunkGetPointer(chunk), size);
734 #endif
735 #ifdef RANDOMIZE_ALLOCATED_MEMORY
736  /* fill the allocated space with junk */
737  randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
738 #endif
739 
740  /*
741  * Stick the new block underneath the active allocation block, if any, so
742  * that we don't lose the use of the space remaining therein.
743  */
744  if (set->blocks != NULL)
745  {
746  block->prev = set->blocks;
747  block->next = set->blocks->next;
748  if (block->next)
749  block->next->prev = block;
750  set->blocks->next = block;
751  }
752  else
753  {
754  block->prev = NULL;
755  block->next = NULL;
756  set->blocks = block;
757  }
758 
759  /* Ensure any padding bytes are marked NOACCESS. */
761  chunk_size - size);
762 
763  /* Disallow access to the chunk header. */
765 
766  return MemoryChunkGetPointer(chunk);
767 }
#define MAXALIGN(LEN)
Definition: c.h:798
static void MemoryContextCheckSize(MemoryContext context, Size size, int flags)
static void MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk, MemoryContextMethodID methodid)

References ALLOC_BLOCKHDRSZ, ALLOC_CHUNKHDRSZ, AllocBlockData::aset, Assert(), AllocSetContext::blocks, AllocBlockData::endptr, AllocBlockData::freeptr, malloc, MAXALIGN, MCTX_ASET_ID, MemoryContextData::mem_allocated, MemoryChunkGetPointer, MemoryChunkSetHdrMaskExternal(), MemoryContextAllocationFailure(), MemoryContextCheckSize(), AllocBlockData::next, AllocBlockData::prev, size, and VALGRIND_MAKE_MEM_NOACCESS.

Referenced by AllocSetAlloc().

◆ AllocSetContextCreateInternal()

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

Definition at line 347 of file aset.c.

352 {
353  int freeListIndex;
354  Size firstBlockSize;
355  AllocSet set;
356  AllocBlock block;
357 
358  /* ensure MemoryChunk's size is properly maxaligned */
360  "sizeof(MemoryChunk) is not maxaligned");
361  /* check we have enough space to store the freelist link */
363  "sizeof(AllocFreeListLink) larger than minimum allocation size");
364 
365  /*
366  * First, validate allocation parameters. Once these were regular runtime
367  * tests and elog's, but in practice Asserts seem sufficient because
368  * nobody varies their parameters at runtime. We somewhat arbitrarily
369  * enforce a minimum 1K block size. We restrict the maximum block size to
370  * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
371  * regards to addressing the offset between the chunk and the block that
372  * the chunk is stored on. We would be unable to store the offset between
373  * the chunk and block for any chunks that were beyond
374  * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
375  * larger than this.
376  */
377  Assert(initBlockSize == MAXALIGN(initBlockSize) &&
378  initBlockSize >= 1024);
379  Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
380  maxBlockSize >= initBlockSize &&
381  AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
382  Assert(minContextSize == 0 ||
383  (minContextSize == MAXALIGN(minContextSize) &&
384  minContextSize >= 1024 &&
385  minContextSize <= maxBlockSize));
386  Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
387 
388  /*
389  * Check whether the parameters match either available freelist. We do
390  * not need to demand a match of maxBlockSize.
391  */
392  if (minContextSize == ALLOCSET_DEFAULT_MINSIZE &&
393  initBlockSize == ALLOCSET_DEFAULT_INITSIZE)
394  freeListIndex = 0;
395  else if (minContextSize == ALLOCSET_SMALL_MINSIZE &&
396  initBlockSize == ALLOCSET_SMALL_INITSIZE)
397  freeListIndex = 1;
398  else
399  freeListIndex = -1;
400 
401  /*
402  * If a suitable freelist entry exists, just recycle that context.
403  */
404  if (freeListIndex >= 0)
405  {
406  AllocSetFreeList *freelist = &context_freelists[freeListIndex];
407 
408  if (freelist->first_free != NULL)
409  {
410  /* Remove entry from freelist */
411  set = freelist->first_free;
412  freelist->first_free = (AllocSet) set->header.nextchild;
413  freelist->num_free--;
414 
415  /* Update its maxBlockSize; everything else should be OK */
416  set->maxBlockSize = maxBlockSize;
417 
418  /* Reinitialize its header, installing correct name and parent */
420  T_AllocSetContext,
421  MCTX_ASET_ID,
422  parent,
423  name);
424 
425  ((MemoryContext) set)->mem_allocated =
426  KeeperBlock(set)->endptr - ((char *) set);
427 
428  return (MemoryContext) set;
429  }
430  }
431 
432  /* Determine size of initial block */
433  firstBlockSize = MAXALIGN(sizeof(AllocSetContext)) +
435  if (minContextSize != 0)
436  firstBlockSize = Max(firstBlockSize, minContextSize);
437  else
438  firstBlockSize = Max(firstBlockSize, initBlockSize);
439 
440  /*
441  * Allocate the initial block. Unlike other aset.c blocks, it starts with
442  * the context header and its block header follows that.
443  */
444  set = (AllocSet) malloc(firstBlockSize);
445  if (set == NULL)
446  {
447  if (TopMemoryContext)
449  ereport(ERROR,
450  (errcode(ERRCODE_OUT_OF_MEMORY),
451  errmsg("out of memory"),
452  errdetail("Failed while creating memory context \"%s\".",
453  name)));
454  }
455 
456  /*
457  * Avoid writing code that can fail between here and MemoryContextCreate;
458  * we'd leak the header/initial block if we ereport in this stretch.
459  */
460 
461  /* Fill in the initial block's block header */
462  block = KeeperBlock(set);
463  block->aset = set;
464  block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
465  block->endptr = ((char *) set) + firstBlockSize;
466  block->prev = NULL;
467  block->next = NULL;
468 
469  /* Mark unallocated space NOACCESS; leave the block header alone. */
470  VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, block->endptr - block->freeptr);
471 
472  /* Remember block as part of block list */
473  set->blocks = block;
474 
475  /* Finish filling in aset-specific parts of the context header */
476  MemSetAligned(set->freelist, 0, sizeof(set->freelist));
477 
478  set->initBlockSize = (uint32) initBlockSize;
479  set->maxBlockSize = (uint32) maxBlockSize;
480  set->nextBlockSize = (uint32) initBlockSize;
481  set->freeListIndex = freeListIndex;
482 
483  /*
484  * Compute the allocation chunk size limit for this context. It can't be
485  * more than ALLOC_CHUNK_LIMIT because of the fixed number of freelists.
486  * If maxBlockSize is small then requests exceeding the maxBlockSize, or
487  * even a significant fraction of it, should be treated as large chunks
488  * too. For the typical case of maxBlockSize a power of 2, the chunk size
489  * limit will be at most 1/8th maxBlockSize, so that given a stream of
490  * requests that are all the maximum chunk size we will waste at most
491  * 1/8th of the allocated space.
492  *
493  * Also, allocChunkLimit must not exceed ALLOCSET_SEPARATE_THRESHOLD.
494  */
496  "ALLOC_CHUNK_LIMIT != ALLOCSET_SEPARATE_THRESHOLD");
497 
498  /*
499  * Determine the maximum size that a chunk can be before we allocate an
500  * entire AllocBlock dedicated for that chunk. We set the absolute limit
501  * of that size as ALLOC_CHUNK_LIMIT but we reduce it further so that we
502  * can fit about ALLOC_CHUNK_FRACTION chunks this size on a maximally
503  * sized block. (We opt to keep allocChunkLimit a power-of-2 value
504  * primarily for legacy reasons rather than calculating it so that exactly
505  * ALLOC_CHUNK_FRACTION chunks fit on a maximally sized block.)
506  */
508  while ((Size) (set->allocChunkLimit + ALLOC_CHUNKHDRSZ) >
509  (Size) ((maxBlockSize - ALLOC_BLOCKHDRSZ) / ALLOC_CHUNK_FRACTION))
510  set->allocChunkLimit >>= 1;
511 
512  /* Finally, do the type-independent part of context creation */
514  T_AllocSetContext,
515  MCTX_ASET_ID,
516  parent,
517  name);
518 
519  ((MemoryContext) set)->mem_allocated = firstBlockSize;
520 
521  return (MemoryContext) set;
522 }
#define KeeperBlock(set)
Definition: aset.c:244
#define ALLOC_CHUNK_FRACTION
Definition: aset.c:87
#define ALLOC_CHUNK_LIMIT
Definition: aset.c:85
static AllocSetFreeList context_freelists[2]
Definition: aset.c:257
unsigned int uint32
Definition: c.h:493
#define Max(x, y)
Definition: c.h:985
#define MemSetAligned(start, val, len)
Definition: c.h:1037
#define StaticAssertDecl(condition, errmessage)
Definition: c.h:923
#define StaticAssertStmt(condition, errmessage)
Definition: c.h:925
int errdetail(const char *fmt,...)
Definition: elog.c:1205
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
void MemoryContextCreate(MemoryContext node, NodeTag tag, MemoryContextMethodID method_id, MemoryContext parent, const char *name)
Definition: mcxt.c:1088
MemoryContext TopMemoryContext
Definition: mcxt.c:137
void MemoryContextStats(MemoryContext context)
Definition: mcxt.c:802
#define ALLOCSET_SMALL_MINSIZE
Definition: memutils.h:160
#define ALLOCSET_DEFAULT_MINSIZE
Definition: memutils.h:150
#define AllocHugeSizeIsValid(size)
Definition: memutils.h:49
#define ALLOCSET_SEPARATE_THRESHOLD
Definition: memutils.h:180
#define ALLOCSET_SMALL_INITSIZE
Definition: memutils.h:161
#define ALLOCSET_DEFAULT_INITSIZE
Definition: memutils.h:151
#define MEMORYCHUNK_MAX_BLOCKOFFSET
struct MemoryContextData * MemoryContext
Definition: palloc.h:36
uint32 initBlockSize
Definition: aset.c:159
MemoryContextData header
Definition: aset.c:154
int freeListIndex
Definition: aset.c:164
int num_free
Definition: aset.c:252
AllocSetContext * first_free
Definition: aset.c:253
MemoryContext nextchild
Definition: memnodes.h:130
const char * name

References ALLOC_BLOCKHDRSZ, ALLOC_CHUNK_FRACTION, ALLOC_CHUNK_LIMIT, ALLOC_CHUNKHDRSZ, ALLOC_MINBITS, AllocSetContext::allocChunkLimit, AllocHugeSizeIsValid, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_SEPARATE_THRESHOLD, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MINSIZE, AllocBlockData::aset, Assert(), AllocSetContext::blocks, context_freelists, AllocBlockData::endptr, ereport, errcode(), errdetail(), errmsg(), ERROR, AllocSetFreeList::first_free, AllocSetContext::freelist, AllocSetContext::freeListIndex, AllocBlockData::freeptr, AllocSetContext::header, AllocSetContext::initBlockSize, KeeperBlock, malloc, Max, MAXALIGN, AllocSetContext::maxBlockSize, MCTX_ASET_ID, MEMORYCHUNK_MAX_BLOCKOFFSET, MemoryContextCreate(), MemoryContextStats(), MemSetAligned, name, AllocBlockData::next, AllocSetContext::nextBlockSize, MemoryContextData::nextchild, AllocSetFreeList::num_free, AllocBlockData::prev, StaticAssertDecl, StaticAssertStmt, TopMemoryContext, and VALGRIND_MAKE_MEM_NOACCESS.

◆ AllocSetDelete()

void AllocSetDelete ( MemoryContext  context)

Definition at line 607 of file aset.c.

608 {
609  AllocSet set = (AllocSet) context;
610  AllocBlock block = set->blocks;
611  Size keepersize PG_USED_FOR_ASSERTS_ONLY;
612 
613  Assert(AllocSetIsValid(set));
614 
615 #ifdef MEMORY_CONTEXT_CHECKING
616  /* Check for corruption and leaks before freeing */
617  AllocSetCheck(context);
618 #endif
619 
620  /* Remember keeper block size for Assert below */
621  keepersize = KeeperBlock(set)->endptr - ((char *) set);
622 
623  /*
624  * If the context is a candidate for a freelist, put it into that freelist
625  * instead of destroying it.
626  */
627  if (set->freeListIndex >= 0)
628  {
630 
631  /*
632  * Reset the context, if it needs it, so that we aren't hanging on to
633  * more than the initial malloc chunk.
634  */
635  if (!context->isReset)
636  MemoryContextResetOnly(context);
637 
638  /*
639  * If the freelist is full, just discard what's already in it. See
640  * comments with context_freelists[].
641  */
642  if (freelist->num_free >= MAX_FREE_CONTEXTS)
643  {
644  while (freelist->first_free != NULL)
645  {
646  AllocSetContext *oldset = freelist->first_free;
647 
648  freelist->first_free = (AllocSetContext *) oldset->header.nextchild;
649  freelist->num_free--;
650 
651  /* All that remains is to free the header/initial block */
652  free(oldset);
653  }
654  Assert(freelist->num_free == 0);
655  }
656 
657  /* Now add the just-deleted context to the freelist. */
658  set->header.nextchild = (MemoryContext) freelist->first_free;
659  freelist->first_free = set;
660  freelist->num_free++;
661 
662  return;
663  }
664 
665  /* Free all blocks, except the keeper which is part of context header */
666  while (block != NULL)
667  {
668  AllocBlock next = block->next;
669 
670  if (!IsKeeperBlock(set, block))
671  context->mem_allocated -= block->endptr - ((char *) block);
672 
673 #ifdef CLOBBER_FREED_MEMORY
674  wipe_mem(block, block->freeptr - ((char *) block));
675 #endif
676 
677  if (!IsKeeperBlock(set, block))
678  free(block);
679 
680  block = next;
681  }
682 
683  Assert(context->mem_allocated == keepersize);
684 
685  /* Finally, free the context header, including the keeper block */
686  free(set);
687 }
#define IsKeeperBlock(set, block)
Definition: aset.c:248
#define MAX_FREE_CONTEXTS
Definition: aset.c:241
static int32 next
Definition: blutils.c:221
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:169
#define free(a)
Definition: header.h:65
void MemoryContextResetOnly(MemoryContext context)
Definition: mcxt.c:390

References AllocSetIsValid, Assert(), AllocSetContext::blocks, context_freelists, AllocBlockData::endptr, AllocSetFreeList::first_free, free, AllocSetContext::freeListIndex, AllocBlockData::freeptr, AllocSetContext::header, IsKeeperBlock, MemoryContextData::isReset, KeeperBlock, MAX_FREE_CONTEXTS, MemoryContextData::mem_allocated, MemoryContextResetOnly(), next, AllocBlockData::next, MemoryContextData::nextchild, AllocSetFreeList::num_free, and PG_USED_FOR_ASSERTS_ONLY.

◆ AllocSetFree()

void AllocSetFree ( void *  pointer)

Definition at line 1062 of file aset.c.

1063 {
1064  AllocSet set;
1065  MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1066 
1067  /* Allow access to the chunk header. */
1069 
1070  if (MemoryChunkIsExternal(chunk))
1071  {
1072  /* Release single-chunk block. */
1073  AllocBlock block = ExternalChunkGetBlock(chunk);
1074 
1075  /*
1076  * Try to verify that we have a sane block pointer: the block header
1077  * should reference an aset and the freeptr should match the endptr.
1078  */
1079  if (!AllocBlockIsValid(block) || block->freeptr != block->endptr)
1080  elog(ERROR, "could not find block containing chunk %p", chunk);
1081 
1082  set = block->aset;
1083 
1084 #ifdef MEMORY_CONTEXT_CHECKING
1085  {
1086  /* Test for someone scribbling on unused space in chunk */
1087  Assert(chunk->requested_size < (block->endptr - (char *) pointer));
1088  if (!sentinel_ok(pointer, chunk->requested_size))
1089  elog(WARNING, "detected write past chunk end in %s %p",
1090  set->header.name, chunk);
1091  }
1092 #endif
1093 
1094  /* OK, remove block from aset's list and free it */
1095  if (block->prev)
1096  block->prev->next = block->next;
1097  else
1098  set->blocks = block->next;
1099  if (block->next)
1100  block->next->prev = block->prev;
1101 
1102  set->header.mem_allocated -= block->endptr - ((char *) block);
1103 
1104 #ifdef CLOBBER_FREED_MEMORY
1105  wipe_mem(block, block->freeptr - ((char *) block));
1106 #endif
1107  free(block);
1108  }
1109  else
1110  {
1111  AllocBlock block = MemoryChunkGetBlock(chunk);
1112  int fidx;
1114 
1115  /*
1116  * In this path, for speed reasons we just Assert that the referenced
1117  * block is good. We can also Assert that the value field is sane.
1118  * Future field experience may show that these Asserts had better
1119  * become regular runtime test-and-elog checks.
1120  */
1121  Assert(AllocBlockIsValid(block));
1122  set = block->aset;
1123 
1124  fidx = MemoryChunkGetValue(chunk);
1125  Assert(FreeListIdxIsValid(fidx));
1126  link = GetFreeListLink(chunk);
1127 
1128 #ifdef MEMORY_CONTEXT_CHECKING
1129  /* Test for someone scribbling on unused space in chunk */
1130  if (chunk->requested_size < GetChunkSizeFromFreeListIdx(fidx))
1131  if (!sentinel_ok(pointer, chunk->requested_size))
1132  elog(WARNING, "detected write past chunk end in %s %p",
1133  set->header.name, chunk);
1134 #endif
1135 
1136 #ifdef CLOBBER_FREED_MEMORY
1137  wipe_mem(pointer, GetChunkSizeFromFreeListIdx(fidx));
1138 #endif
1139  /* push this chunk onto the top of the free list */
1141  link->next = set->freelist[fidx];
1143  set->freelist[fidx] = chunk;
1144 
1145 #ifdef MEMORY_CONTEXT_CHECKING
1146 
1147  /*
1148  * Reset requested_size to InvalidAllocSize in chunks that are on free
1149  * list.
1150  */
1151  chunk->requested_size = InvalidAllocSize;
1152 #endif
1153  }
1154 }
#define AllocBlockIsValid(block)
Definition: aset.c:207
#define FreeListIdxIsValid(fidx)
Definition: aset.c:136
#define ExternalChunkGetBlock(chunk)
Definition: aset.c:215
#define WARNING
Definition: elog.h:36
#define elog(elevel,...)
Definition: elog.h:224
static bool MemoryChunkIsExternal(MemoryChunk *chunk)
static void * MemoryChunkGetBlock(MemoryChunk *chunk)
#define PointerGetMemoryChunk(p)
const char * name
Definition: memnodes.h:131

References ALLOC_CHUNKHDRSZ, AllocBlockIsValid, AllocBlockData::aset, Assert(), AllocSetContext::blocks, elog, AllocBlockData::endptr, ERROR, ExternalChunkGetBlock, free, AllocSetContext::freelist, FreeListIdxIsValid, AllocBlockData::freeptr, GetChunkSizeFromFreeListIdx, GetFreeListLink, AllocSetContext::header, InvalidAllocSize, link(), MemoryContextData::mem_allocated, MemoryChunkGetBlock(), MemoryChunkGetValue(), MemoryChunkIsExternal(), MemoryContextData::name, AllocBlockData::next, PointerGetMemoryChunk, AllocBlockData::prev, VALGRIND_MAKE_MEM_DEFINED, VALGRIND_MAKE_MEM_NOACCESS, and WARNING.

Referenced by AllocSetRealloc().

◆ AllocSetFreeIndex()

static int AllocSetFreeIndex ( Size  size)
inlinestatic

Definition at line 277 of file aset.c.

278 {
279  int idx;
280 
281  if (size > (1 << ALLOC_MINBITS))
282  {
283  /*----------
284  * At this point we must compute ceil(log2(size >> ALLOC_MINBITS)).
285  * This is the same as
286  * pg_leftmost_one_pos32((size - 1) >> ALLOC_MINBITS) + 1
287  * or equivalently
288  * pg_leftmost_one_pos32(size - 1) - ALLOC_MINBITS + 1
289  *
290  * However, for platforms without intrinsic support, we duplicate the
291  * logic here, allowing an additional optimization. It's reasonable
292  * to assume that ALLOC_CHUNK_LIMIT fits in 16 bits, so we can unroll
293  * the byte-at-a-time loop in pg_leftmost_one_pos32 and just handle
294  * the last two bytes.
295  *
296  * Yes, this function is enough of a hot-spot to make it worth this
297  * much trouble.
298  *----------
299  */
300 #ifdef HAVE_BITSCAN_REVERSE
302 #else
303  uint32 t,
304  tsize;
305 
306  /* Statically assert that we only have a 16-bit input value. */
308  "ALLOC_CHUNK_LIMIT must be less than 64kB");
309 
310  tsize = size - 1;
311  t = tsize >> 8;
312  idx = t ? pg_leftmost_one_pos[t] + 8 : pg_leftmost_one_pos[tsize];
313  idx -= ALLOC_MINBITS - 1;
314 #endif
315 
317  }
318  else
319  idx = 0;
320 
321  return idx;
322 }
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
#define ALLOCSET_NUM_FREELISTS
Definition: aset.c:84
static int pg_leftmost_one_pos32(uint32 word)
Definition: pg_bitutils.h:41
PGDLLIMPORT const uint8 pg_leftmost_one_pos[256]
Definition: pg_bitutils.c:34

References ALLOC_CHUNK_LIMIT, ALLOC_MINBITS, ALLOCSET_NUM_FREELISTS, Assert(), idx(), pg_leftmost_one_pos, pg_leftmost_one_pos32(), size, and StaticAssertDecl.

Referenced by AllocSetAlloc(), and AllocSetAllocFromNewBlock().

◆ AllocSetGetChunkContext()

MemoryContext AllocSetGetChunkContext ( void *  pointer)

Definition at line 1433 of file aset.c.

1434 {
1435  MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1436  AllocBlock block;
1437  AllocSet set;
1438 
1439  /* Allow access to the chunk header. */
1441 
1442  if (MemoryChunkIsExternal(chunk))
1443  block = ExternalChunkGetBlock(chunk);
1444  else
1445  block = (AllocBlock) MemoryChunkGetBlock(chunk);
1446 
1447  /* Disallow access to the chunk header. */
1449 
1450  Assert(AllocBlockIsValid(block));
1451  set = block->aset;
1452 
1453  return &set->header;
1454 }

References ALLOC_CHUNKHDRSZ, AllocBlockIsValid, AllocBlockData::aset, Assert(), ExternalChunkGetBlock, AllocSetContext::header, MemoryChunkGetBlock(), MemoryChunkIsExternal(), PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ AllocSetGetChunkSpace()

Size AllocSetGetChunkSpace ( void *  pointer)

Definition at line 1462 of file aset.c.

1463 {
1464  MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1465  int fidx;
1466 
1467  /* Allow access to the chunk header. */
1469 
1470  if (MemoryChunkIsExternal(chunk))
1471  {
1472  AllocBlock block = ExternalChunkGetBlock(chunk);
1473 
1474  /* Disallow access to the chunk header. */
1476 
1477  Assert(AllocBlockIsValid(block));
1478 
1479  return block->endptr - (char *) chunk;
1480  }
1481 
1482  fidx = MemoryChunkGetValue(chunk);
1483  Assert(FreeListIdxIsValid(fidx));
1484 
1485  /* Disallow access to the chunk header. */
1487 
1489 }

References ALLOC_CHUNKHDRSZ, AllocBlockIsValid, Assert(), AllocBlockData::endptr, ExternalChunkGetBlock, FreeListIdxIsValid, GetChunkSizeFromFreeListIdx, MemoryChunkGetValue(), MemoryChunkIsExternal(), PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ AllocSetIsEmpty()

bool AllocSetIsEmpty ( MemoryContext  context)

Definition at line 1496 of file aset.c.

1497 {
1498  Assert(AllocSetIsValid(context));
1499 
1500  /*
1501  * For now, we say "empty" only if the context is new or just reset. We
1502  * could examine the freelists to determine if all space has been freed,
1503  * but it's not really worth the trouble for present uses of this
1504  * functionality.
1505  */
1506  if (context->isReset)
1507  return true;
1508  return false;
1509 }

References AllocSetIsValid, Assert(), and MemoryContextData::isReset.

◆ AllocSetRealloc()

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

Definition at line 1169 of file aset.c.

1170 {
1171  AllocBlock block;
1172  AllocSet set;
1173  MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1174  Size oldchksize;
1175  int fidx;
1176 
1177  /* Allow access to the chunk header. */
1179 
1180  if (MemoryChunkIsExternal(chunk))
1181  {
1182  /*
1183  * The chunk must have been allocated as a single-chunk block. Use
1184  * realloc() to make the containing block bigger, or smaller, with
1185  * minimum space wastage.
1186  */
1187  Size chksize;
1188  Size blksize;
1189  Size oldblksize;
1190 
1191  block = ExternalChunkGetBlock(chunk);
1192 
1193  /*
1194  * Try to verify that we have a sane block pointer: the block header
1195  * should reference an aset and the freeptr should match the endptr.
1196  */
1197  if (!AllocBlockIsValid(block) || block->freeptr != block->endptr)
1198  elog(ERROR, "could not find block containing chunk %p", chunk);
1199 
1200  set = block->aset;
1201 
1202  /* only check size in paths where the limits could be hit */
1203  MemoryContextCheckSize((MemoryContext) set, size, flags);
1204 
1205  oldchksize = block->endptr - (char *) pointer;
1206 
1207 #ifdef MEMORY_CONTEXT_CHECKING
1208  /* Test for someone scribbling on unused space in chunk */
1209  Assert(chunk->requested_size < oldchksize);
1210  if (!sentinel_ok(pointer, chunk->requested_size))
1211  elog(WARNING, "detected write past chunk end in %s %p",
1212  set->header.name, chunk);
1213 #endif
1214 
1215 #ifdef MEMORY_CONTEXT_CHECKING
1216  /* ensure there's always space for the sentinel byte */
1217  chksize = MAXALIGN(size + 1);
1218 #else
1219  chksize = MAXALIGN(size);
1220 #endif
1221 
1222  /* Do the realloc */
1223  blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
1224  oldblksize = block->endptr - ((char *) block);
1225 
1226  block = (AllocBlock) realloc(block, blksize);
1227  if (block == NULL)
1228  {
1229  /* Disallow access to the chunk header. */
1231  return MemoryContextAllocationFailure(&set->header, size, flags);
1232  }
1233 
1234  /* updated separately, not to underflow when (oldblksize > blksize) */
1235  set->header.mem_allocated -= oldblksize;
1236  set->header.mem_allocated += blksize;
1237 
1238  block->freeptr = block->endptr = ((char *) block) + blksize;
1239 
1240  /* Update pointers since block has likely been moved */
1241  chunk = (MemoryChunk *) (((char *) block) + ALLOC_BLOCKHDRSZ);
1242  pointer = MemoryChunkGetPointer(chunk);
1243  if (block->prev)
1244  block->prev->next = block;
1245  else
1246  set->blocks = block;
1247  if (block->next)
1248  block->next->prev = block;
1249 
1250 #ifdef MEMORY_CONTEXT_CHECKING
1251 #ifdef RANDOMIZE_ALLOCATED_MEMORY
1252 
1253  /*
1254  * We can only randomize the extra space if we know the prior request.
1255  * When using Valgrind, randomize_mem() also marks memory UNDEFINED.
1256  */
1257  if (size > chunk->requested_size)
1258  randomize_mem((char *) pointer + chunk->requested_size,
1259  size - chunk->requested_size);
1260 #else
1261 
1262  /*
1263  * If this is an increase, realloc() will have marked any
1264  * newly-allocated part (from oldchksize to chksize) UNDEFINED, but we
1265  * also need to adjust trailing bytes from the old allocation (from
1266  * chunk->requested_size to oldchksize) as they are marked NOACCESS.
1267  * Make sure not to mark too many bytes in case chunk->requested_size
1268  * < size < oldchksize.
1269  */
1270 #ifdef USE_VALGRIND
1271  if (Min(size, oldchksize) > chunk->requested_size)
1272  VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + chunk->requested_size,
1273  Min(size, oldchksize) - chunk->requested_size);
1274 #endif
1275 #endif
1276 
1277  chunk->requested_size = size;
1278  /* set mark to catch clobber of "unused" space */
1279  Assert(size < chksize);
1280  set_sentinel(pointer, size);
1281 #else /* !MEMORY_CONTEXT_CHECKING */
1282 
1283  /*
1284  * We may need to adjust marking of bytes from the old allocation as
1285  * some of them may be marked NOACCESS. We don't know how much of the
1286  * old chunk size was the requested size; it could have been as small
1287  * as one byte. We have to be conservative and just mark the entire
1288  * old portion DEFINED. Make sure not to mark memory beyond the new
1289  * allocation in case it's smaller than the old one.
1290  */
1291  VALGRIND_MAKE_MEM_DEFINED(pointer, Min(size, oldchksize));
1292 #endif
1293 
1294  /* Ensure any padding bytes are marked NOACCESS. */
1295  VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size, chksize - size);
1296 
1297  /* Disallow access to the chunk header . */
1299 
1300  return pointer;
1301  }
1302 
1303  block = MemoryChunkGetBlock(chunk);
1304 
1305  /*
1306  * In this path, for speed reasons we just Assert that the referenced
1307  * block is good. We can also Assert that the value field is sane. Future
1308  * field experience may show that these Asserts had better become regular
1309  * runtime test-and-elog checks.
1310  */
1311  Assert(AllocBlockIsValid(block));
1312  set = block->aset;
1313 
1314  fidx = MemoryChunkGetValue(chunk);
1315  Assert(FreeListIdxIsValid(fidx));
1316  oldchksize = GetChunkSizeFromFreeListIdx(fidx);
1317 
1318 #ifdef MEMORY_CONTEXT_CHECKING
1319  /* Test for someone scribbling on unused space in chunk */
1320  if (chunk->requested_size < oldchksize)
1321  if (!sentinel_ok(pointer, chunk->requested_size))
1322  elog(WARNING, "detected write past chunk end in %s %p",
1323  set->header.name, chunk);
1324 #endif
1325 
1326  /*
1327  * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the
1328  * allocated area already is >= the new size. (In particular, we will
1329  * fall out here if the requested size is a decrease.)
1330  */
1331  if (oldchksize >= size)
1332  {
1333 #ifdef MEMORY_CONTEXT_CHECKING
1334  Size oldrequest = chunk->requested_size;
1335 
1336 #ifdef RANDOMIZE_ALLOCATED_MEMORY
1337  /* We can only fill the extra space if we know the prior request */
1338  if (size > oldrequest)
1339  randomize_mem((char *) pointer + oldrequest,
1340  size - oldrequest);
1341 #endif
1342 
1343  chunk->requested_size = size;
1344 
1345  /*
1346  * If this is an increase, mark any newly-available part UNDEFINED.
1347  * Otherwise, mark the obsolete part NOACCESS.
1348  */
1349  if (size > oldrequest)
1350  VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + oldrequest,
1351  size - oldrequest);
1352  else
1353  VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size,
1354  oldchksize - size);
1355 
1356  /* set mark to catch clobber of "unused" space */
1357  if (size < oldchksize)
1358  set_sentinel(pointer, size);
1359 #else /* !MEMORY_CONTEXT_CHECKING */
1360 
1361  /*
1362  * We don't have the information to determine whether we're growing
1363  * the old request or shrinking it, so we conservatively mark the
1364  * entire new allocation DEFINED.
1365  */
1366  VALGRIND_MAKE_MEM_NOACCESS(pointer, oldchksize);
1367  VALGRIND_MAKE_MEM_DEFINED(pointer, size);
1368 #endif
1369 
1370  /* Disallow access to the chunk header. */
1372 
1373  return pointer;
1374  }
1375  else
1376  {
1377  /*
1378  * Enlarge-a-small-chunk case. We just do this by brute force, ie,
1379  * allocate a new chunk and copy the data. Since we know the existing
1380  * data isn't huge, this won't involve any great memcpy expense, so
1381  * it's not worth being smarter. (At one time we tried to avoid
1382  * memcpy when it was possible to enlarge the chunk in-place, but that
1383  * turns out to misbehave unpleasantly for repeated cycles of
1384  * palloc/repalloc/pfree: the eventually freed chunks go into the
1385  * wrong freelist for the next initial palloc request, and so we leak
1386  * memory indefinitely. See pgsql-hackers archives for 2007-08-11.)
1387  */
1388  AllocPointer newPointer;
1389  Size oldsize;
1390 
1391  /* allocate new chunk (this also checks size is valid) */
1392  newPointer = AllocSetAlloc((MemoryContext) set, size, flags);
1393 
1394  /* leave immediately if request was not completed */
1395  if (newPointer == NULL)
1396  {
1397  /* Disallow access to the chunk header. */
1399  return MemoryContextAllocationFailure((MemoryContext) set, size, flags);
1400  }
1401 
1402  /*
1403  * AllocSetAlloc() may have returned a region that is still NOACCESS.
1404  * Change it to UNDEFINED for the moment; memcpy() will then transfer
1405  * definedness from the old allocation to the new. If we know the old
1406  * allocation, copy just that much. Otherwise, make the entire old
1407  * chunk defined to avoid errors as we copy the currently-NOACCESS
1408  * trailing bytes.
1409  */
1410  VALGRIND_MAKE_MEM_UNDEFINED(newPointer, size);
1411 #ifdef MEMORY_CONTEXT_CHECKING
1412  oldsize = chunk->requested_size;
1413 #else
1414  oldsize = oldchksize;
1415  VALGRIND_MAKE_MEM_DEFINED(pointer, oldsize);
1416 #endif
1417 
1418  /* transfer existing data (certain to fit) */
1419  memcpy(newPointer, pointer, oldsize);
1420 
1421  /* free old chunk */
1422  AllocSetFree(pointer);
1423 
1424  return newPointer;
1425  }
1426 }
void * AllocSetAlloc(MemoryContext context, Size size, int flags)
Definition: aset.c:967
void * AllocPointer
Definition: aset.c:113
void AllocSetFree(void *pointer)
Definition: aset.c:1062
#define Min(x, y)
Definition: c.h:991
#define realloc(a, b)
Definition: header.h:60

References ALLOC_BLOCKHDRSZ, ALLOC_CHUNKHDRSZ, AllocBlockIsValid, AllocSetAlloc(), AllocSetFree(), AllocBlockData::aset, Assert(), elog, AllocBlockData::endptr, ERROR, ExternalChunkGetBlock, FreeListIdxIsValid, AllocBlockData::freeptr, GetChunkSizeFromFreeListIdx, AllocSetContext::header, MAXALIGN, MemoryContextData::mem_allocated, MemoryChunkGetBlock(), MemoryChunkGetPointer, MemoryChunkGetValue(), MemoryChunkIsExternal(), MemoryContextAllocationFailure(), MemoryContextCheckSize(), Min, MemoryContextData::name, AllocBlockData::next, PointerGetMemoryChunk, AllocBlockData::prev, realloc, size, VALGRIND_MAKE_MEM_DEFINED, VALGRIND_MAKE_MEM_NOACCESS, VALGRIND_MAKE_MEM_UNDEFINED, and WARNING.

◆ AllocSetReset()

void AllocSetReset ( MemoryContext  context)

Definition at line 537 of file aset.c.

538 {
539  AllocSet set = (AllocSet) context;
540  AllocBlock block;
541  Size keepersize PG_USED_FOR_ASSERTS_ONLY;
542 
543  Assert(AllocSetIsValid(set));
544 
545 #ifdef MEMORY_CONTEXT_CHECKING
546  /* Check for corruption and leaks before freeing */
547  AllocSetCheck(context);
548 #endif
549 
550  /* Remember keeper block size for Assert below */
551  keepersize = KeeperBlock(set)->endptr - ((char *) set);
552 
553  /* Clear chunk freelists */
554  MemSetAligned(set->freelist, 0, sizeof(set->freelist));
555 
556  block = set->blocks;
557 
558  /* New blocks list will be just the keeper block */
559  set->blocks = KeeperBlock(set);
560 
561  while (block != NULL)
562  {
563  AllocBlock next = block->next;
564 
565  if (IsKeeperBlock(set, block))
566  {
567  /* Reset the block, but don't return it to malloc */
568  char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
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  block->freeptr = datastart;
577  block->prev = NULL;
578  block->next = NULL;
579  }
580  else
581  {
582  /* Normal case, release the block */
583  context->mem_allocated -= block->endptr - ((char *) block);
584 
585 #ifdef CLOBBER_FREED_MEMORY
586  wipe_mem(block, block->freeptr - ((char *) block));
587 #endif
588  free(block);
589  }
590  block = next;
591  }
592 
593  Assert(context->mem_allocated == keepersize);
594 
595  /* Reset block size allocation sequence, too */
596  set->nextBlockSize = set->initBlockSize;
597 }

References ALLOC_BLOCKHDRSZ, AllocSetIsValid, Assert(), AllocSetContext::blocks, AllocBlockData::endptr, free, AllocSetContext::freelist, AllocBlockData::freeptr, AllocSetContext::initBlockSize, IsKeeperBlock, KeeperBlock, MemoryContextData::mem_allocated, MemSetAligned, next, AllocBlockData::next, AllocSetContext::nextBlockSize, PG_USED_FOR_ASSERTS_ONLY, AllocBlockData::prev, and VALGRIND_MAKE_MEM_NOACCESS.

◆ AllocSetStats()

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

Definition at line 1521 of file aset.c.

1524 {
1525  AllocSet set = (AllocSet) context;
1526  Size nblocks = 0;
1527  Size freechunks = 0;
1528  Size totalspace;
1529  Size freespace = 0;
1530  AllocBlock block;
1531  int fidx;
1532 
1533  Assert(AllocSetIsValid(set));
1534 
1535  /* Include context header in totalspace */
1536  totalspace = MAXALIGN(sizeof(AllocSetContext));
1537 
1538  for (block = set->blocks; block != NULL; block = block->next)
1539  {
1540  nblocks++;
1541  totalspace += block->endptr - ((char *) block);
1542  freespace += block->endptr - block->freeptr;
1543  }
1544  for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++)
1545  {
1546  Size chksz = GetChunkSizeFromFreeListIdx(fidx);
1547  MemoryChunk *chunk = set->freelist[fidx];
1548 
1549  while (chunk != NULL)
1550  {
1552 
1553  /* Allow access to the chunk header. */
1555  Assert(MemoryChunkGetValue(chunk) == fidx);
1557 
1558  freechunks++;
1559  freespace += chksz + ALLOC_CHUNKHDRSZ;
1560 
1562  chunk = link->next;
1564  }
1565  }
1566 
1567  if (printfunc)
1568  {
1569  char stats_string[200];
1570 
1571  snprintf(stats_string, sizeof(stats_string),
1572  "%zu total in %zu blocks; %zu free (%zu chunks); %zu used",
1573  totalspace, nblocks, freespace, freechunks,
1574  totalspace - freespace);
1575  printfunc(context, passthru, stats_string, print_to_stderr);
1576  }
1577 
1578  if (totals)
1579  {
1580  totals->nblocks += nblocks;
1581  totals->freechunks += freechunks;
1582  totals->totalspace += totalspace;
1583  totals->freespace += freespace;
1584  }
1585 }
#define snprintf
Definition: port.h:238

References ALLOC_CHUNKHDRSZ, ALLOCSET_NUM_FREELISTS, AllocSetIsValid, Assert(), AllocSetContext::blocks, AllocBlockData::endptr, MemoryContextCounters::freechunks, AllocSetContext::freelist, AllocBlockData::freeptr, MemoryContextCounters::freespace, GetChunkSizeFromFreeListIdx, GetFreeListLink, MAXALIGN, MemoryChunkGetValue(), MemoryContextCounters::nblocks, AllocBlockData::next, snprintf, MemoryContextCounters::totalspace, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

Variable Documentation

◆ context_freelists

AllocSetFreeList context_freelists[2]
static
Initial value:
=
{
{
0, NULL
},
{
0, NULL
}
}

Definition at line 257 of file aset.c.

Referenced by AllocSetContextCreateInternal(), and AllocSetDelete().