PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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
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 request off to
983 * 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. */
1029 GetChunkSizeFromFreeListIdx(fidx) - size);
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}
static pg_noinline void * AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags, int fidx)
Definition: aset.c:819
#define AllocSetIsValid(set)
Definition: aset.c:200
#define GetFreeListLink(chkptr)
Definition: aset.c:132
#define ALLOC_CHUNKHDRSZ
Definition: aset.c:105
#define GetChunkSizeFromFreeListIdx(fidx)
Definition: aset.c:140
static int AllocSetFreeIndex(Size size)
Definition: aset.c:277
static void * AllocSetAllocChunkFromBlock(MemoryContext context, AllocBlock block, Size size, Size chunk_size, int fidx)
Definition: aset.c:774
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:347
size_t Size
Definition: c.h:576
Assert(PointerIsAligned(start, uint64))
#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)
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(), 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(), 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:1226
#define InvalidAllocSize
Definition: memutils.h:50
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, 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:782
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, 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,
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 {
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,
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
#define Max(x, y)
Definition: c.h:969
#define MemSetAligned(start, val, len)
Definition: c.h:1021
uint32_t uint32
Definition: c.h:502
#define StaticAssertDecl(condition, errmessage)
Definition: c.h:907
#define StaticAssertStmt(condition, errmessage)
Definition: c.h:909
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
void MemoryContextCreate(MemoryContext node, NodeTag tag, MemoryContextMethodID method_id, MemoryContext parent, const char *name)
Definition: mcxt.c:1175
MemoryContext TopMemoryContext
Definition: mcxt.c:165
void MemoryContextStats(MemoryContext context)
Definition: mcxt.c:845
#define ALLOCSET_SMALL_MINSIZE
Definition: memutils.h:187
#define ALLOCSET_DEFAULT_MINSIZE
Definition: memutils.h:177
#define AllocHugeSizeIsValid(size)
Definition: memutils.h:52
#define ALLOCSET_SEPARATE_THRESHOLD
Definition: memutils.h:207
#define ALLOCSET_SMALL_INITSIZE
Definition: memutils.h:188
#define ALLOCSET_DEFAULT_INITSIZE
Definition: memutils.h:178
#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;
612
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:224
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:224
#define free(a)
Definition: header.h:65
void MemoryContextResetOnly(MemoryContext context)
Definition: mcxt.c:433

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);
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:225
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
301 idx = pg_leftmost_one_pos32((uint32) size - 1) - ALLOC_MINBITS + 1;
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:262
#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(), 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);
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);
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:975
#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, 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;
542
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:239

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().