Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

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 FIRST_BLOCKHDRSZ
 
#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 AllocSetIsValid(set)    ((set) && IsA(set, AllocSetContext))
 
#define AllocBlockIsValid(block)    ((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)     ((block) && AllocSetIsValid((block)->aset))

Definition at line 203 of file aset.c.

◆ ALLOCSET_NUM_FREELISTS

#define ALLOCSET_NUM_FREELISTS   11

Definition at line 84 of file aset.c.

◆ AllocSetIsValid

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

Definition at line 196 of file aset.c.

◆ ExternalChunkGetBlock

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

Definition at line 211 of file aset.c.

◆ FIRST_BLOCKHDRSZ

#define FIRST_BLOCKHDRSZ
Value:
(MAXALIGN(sizeof(AllocSetContext)) + \
ALLOC_BLOCKHDRSZ)
#define MAXALIGN(LEN)
Definition: c.h:810

Definition at line 106 of file aset.c.

◆ FreeListIdxIsValid

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

Definition at line 138 of file aset.c.

◆ GetChunkSizeFromFreeListIdx

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

Definition at line 142 of file aset.c.

◆ GetFreeListLink

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

Definition at line 134 of file aset.c.

◆ IsKeeperBlock

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

Definition at line 244 of file aset.c.

◆ KeeperBlock

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

Definition at line 240 of file aset.c.

◆ MAX_FREE_CONTEXTS

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

Definition at line 237 of file aset.c.

Typedef Documentation

◆ AllocBlock

typedef struct AllocBlockData* AllocBlock

Definition at line 109 of file aset.c.

◆ AllocBlockData

◆ AllocFreeListLink

◆ AllocPointer

typedef void* AllocPointer

Definition at line 115 of file aset.c.

◆ AllocSet

Definition at line 169 of file aset.c.

◆ AllocSetContext

◆ AllocSetFreeList

Function Documentation

◆ AllocSetAlloc()

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

Definition at line 1014 of file aset.c.

1015{
1016 AllocSet set = (AllocSet) context;
1017 AllocBlock block;
1018 MemoryChunk *chunk;
1019 int fidx;
1020 Size chunk_size;
1021 Size availspace;
1022
1023 Assert(AllocSetIsValid(set));
1024
1025 /* due to the keeper block set->blocks should never be NULL */
1026 Assert(set->blocks != NULL);
1027
1028 /*
1029 * If requested size exceeds maximum for chunks we hand the request off to
1030 * AllocSetAllocLarge().
1031 */
1032 if (size > set->allocChunkLimit)
1033 return AllocSetAllocLarge(context, size, flags);
1034
1035 /*
1036 * Request is small enough to be treated as a chunk. Look in the
1037 * corresponding free list to see if there is a free chunk we could reuse.
1038 * If one is found, remove it from the free list, make it again a member
1039 * of the alloc set and return its data address.
1040 *
1041 * Note that we don't attempt to ensure there's space for the sentinel
1042 * byte here. We expect a large proportion of allocations to be for sizes
1043 * which are already a power of 2. If we were to always make space for a
1044 * sentinel byte in MEMORY_CONTEXT_CHECKING builds, then we'd end up
1045 * doubling the memory requirements for such allocations.
1046 */
1047 fidx = AllocSetFreeIndex(size);
1048 chunk = set->freelist[fidx];
1049 if (chunk != NULL)
1050 {
1052
1053 /* Allow access to the chunk header. */
1055
1056 Assert(fidx == MemoryChunkGetValue(chunk));
1057
1058 /* pop this chunk off the freelist */
1060 set->freelist[fidx] = link->next;
1062
1063#ifdef MEMORY_CONTEXT_CHECKING
1064 chunk->requested_size = size;
1065 /* set mark to catch clobber of "unused" space */
1066 if (size < GetChunkSizeFromFreeListIdx(fidx))
1067 set_sentinel(MemoryChunkGetPointer(chunk), size);
1068#endif
1069#ifdef RANDOMIZE_ALLOCATED_MEMORY
1070 /* fill the allocated space with junk */
1071 randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
1072#endif
1073
1074 /* Ensure any padding bytes are marked NOACCESS. */
1076 GetChunkSizeFromFreeListIdx(fidx) - size);
1077
1078 /* Disallow access to the chunk header. */
1080
1081 return MemoryChunkGetPointer(chunk);
1082 }
1083
1084 /*
1085 * Choose the actual chunk size to allocate.
1086 */
1087 chunk_size = GetChunkSizeFromFreeListIdx(fidx);
1088 Assert(chunk_size >= size);
1089
1090 block = set->blocks;
1091 availspace = block->endptr - block->freeptr;
1092
1093 /*
1094 * If there is enough room in the active allocation block, we will put the
1095 * chunk into that block. Else must start a new one.
1096 */
1097 if (unlikely(availspace < (chunk_size + ALLOC_CHUNKHDRSZ)))
1098 return AllocSetAllocFromNewBlock(context, size, flags, fidx);
1099
1100 /* There's enough space on the current block, so allocate from that */
1101 return AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx);
1102}
static pg_noinline void * AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags, int fidx)
Definition: aset.c:863
#define AllocSetIsValid(set)
Definition: aset.c:196
#define GetFreeListLink(chkptr)
Definition: aset.c:134
#define ALLOC_CHUNKHDRSZ
Definition: aset.c:105
#define GetChunkSizeFromFreeListIdx(fidx)
Definition: aset.c:142
static int AllocSetFreeIndex(Size size)
Definition: aset.c:273
static void * AllocSetAllocChunkFromBlock(MemoryContext context, AllocBlock block, Size size, Size chunk_size, int fidx)
Definition: aset.c:818
static pg_noinline void * AllocSetAllocLarge(MemoryContext context, Size size, int flags)
Definition: aset.c:737
AllocSetContext * AllocSet
Definition: aset.c:169
#define unlikely(x)
Definition: c.h:402
size_t Size
Definition: c.h:610
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:188
char * endptr
Definition: aset.c:189
uint32 allocChunkLimit
Definition: aset.c:164
AllocBlock blocks
Definition: aset.c:158
MemoryChunk * freelist[ALLOCSET_NUM_FREELISTS]
Definition: aset.c:159

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 818 of file aset.c.

820{
821 MemoryChunk *chunk;
822
823 chunk = (MemoryChunk *) (block->freeptr);
824
825 /* Prepare to initialize the chunk header. */
827
828 block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
829 Assert(block->freeptr <= block->endptr);
830
831 /* store the free list index in the value field */
832 MemoryChunkSetHdrMask(chunk, block, fidx, MCTX_ASET_ID);
833
834#ifdef MEMORY_CONTEXT_CHECKING
835 chunk->requested_size = size;
836 /* set mark to catch clobber of "unused" space */
837 if (size < chunk_size)
838 set_sentinel(MemoryChunkGetPointer(chunk), size);
839#endif
840#ifdef RANDOMIZE_ALLOCATED_MEMORY
841 /* fill the allocated space with junk */
842 randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
843#endif
844
845 /* Ensure any padding bytes are marked NOACCESS. */
847 chunk_size - size);
848
849 /* Disallow access to the chunk header. */
851
852 return MemoryChunkGetPointer(chunk);
853}
#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 863 of file aset.c.

865{
866 AllocSet set = (AllocSet) context;
867 AllocBlock block;
868 Size availspace;
869 Size blksize;
870 Size required_size;
871 Size chunk_size;
872
873 /* due to the keeper block set->blocks should always be valid */
874 Assert(set->blocks != NULL);
875 block = set->blocks;
876 availspace = block->endptr - block->freeptr;
877
878 /*
879 * The existing active (top) block does not have enough room for the
880 * requested allocation, but it might still have a useful amount of space
881 * in it. Once we push it down in the block list, we'll never try to
882 * allocate more space from it. So, before we do that, carve up its free
883 * space into chunks that we can put on the set's freelists.
884 *
885 * Because we can only get here when there's less than ALLOC_CHUNK_LIMIT
886 * left in the block, this loop cannot iterate more than
887 * ALLOCSET_NUM_FREELISTS-1 times.
888 */
889 while (availspace >= ((1 << ALLOC_MINBITS) + ALLOC_CHUNKHDRSZ))
890 {
892 MemoryChunk *chunk;
893 Size availchunk = availspace - ALLOC_CHUNKHDRSZ;
894 int a_fidx = AllocSetFreeIndex(availchunk);
895
896 /*
897 * In most cases, we'll get back the index of the next larger freelist
898 * than the one we need to put this chunk on. The exception is when
899 * availchunk is exactly a power of 2.
900 */
901 if (availchunk != GetChunkSizeFromFreeListIdx(a_fidx))
902 {
903 a_fidx--;
904 Assert(a_fidx >= 0);
905 availchunk = GetChunkSizeFromFreeListIdx(a_fidx);
906 }
907
908 chunk = (MemoryChunk *) (block->freeptr);
909
910 /* Prepare to initialize the chunk header. */
912 block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ);
913 availspace -= (availchunk + ALLOC_CHUNKHDRSZ);
914
915 /* store the freelist index in the value field */
916 MemoryChunkSetHdrMask(chunk, block, a_fidx, MCTX_ASET_ID);
917#ifdef MEMORY_CONTEXT_CHECKING
918 chunk->requested_size = InvalidAllocSize; /* mark it free */
919#endif
920 /* push this chunk onto the free list */
921 link = GetFreeListLink(chunk);
922
924 link->next = set->freelist[a_fidx];
926
927 set->freelist[a_fidx] = chunk;
928 }
929
930 /*
931 * The first such block has size initBlockSize, and we double the space in
932 * each succeeding block, but not more than maxBlockSize.
933 */
934 blksize = set->nextBlockSize;
935 set->nextBlockSize <<= 1;
936 if (set->nextBlockSize > set->maxBlockSize)
937 set->nextBlockSize = set->maxBlockSize;
938
939 /* Choose the actual chunk size to allocate */
940 chunk_size = GetChunkSizeFromFreeListIdx(fidx);
941 Assert(chunk_size >= size);
942
943 /*
944 * If initBlockSize is less than ALLOC_CHUNK_LIMIT, we could need more
945 * space... but try to keep it a power of 2.
946 */
947 required_size = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
948 while (blksize < required_size)
949 blksize <<= 1;
950
951 /* Try to allocate it */
952 block = (AllocBlock) malloc(blksize);
953
954 /*
955 * We could be asking for pretty big blocks here, so cope if malloc fails.
956 * But give up if there's less than 1 MB or so available...
957 */
958 while (block == NULL && blksize > 1024 * 1024)
959 {
960 blksize >>= 1;
961 if (blksize < required_size)
962 break;
963 block = (AllocBlock) malloc(blksize);
964 }
965
966 if (block == NULL)
967 return MemoryContextAllocationFailure(context, size, flags);
968
969 /* Make a vchunk covering the new block's header */
971
972 context->mem_allocated += blksize;
973
974 block->aset = set;
975 block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
976 block->endptr = ((char *) block) + blksize;
977
978 /* Mark unallocated space NOACCESS. */
980 blksize - ALLOC_BLOCKHDRSZ);
981
982 block->prev = NULL;
983 block->next = set->blocks;
984 if (block->next)
985 block->next->prev = block;
986 set->blocks = block;
987
988 return AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx);
989}
#define ALLOC_MINBITS
Definition: aset.c:83
struct AllocBlockData * AllocBlock
Definition: aset.c:109
#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:1195
#define VALGRIND_MEMPOOL_ALLOC(context, addr, size)
Definition: memdebug.h:29
#define InvalidAllocSize
Definition: memutils.h:47
AllocBlock prev
Definition: aset.c:186
AllocSet aset
Definition: aset.c:185
AllocBlock next
Definition: aset.c:187
uint32 maxBlockSize
Definition: aset.c:162
uint32 nextBlockSize
Definition: aset.c:163

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, VALGRIND_MAKE_MEM_UNDEFINED, and VALGRIND_MEMPOOL_ALLOC.

Referenced by AllocSetAlloc().

◆ AllocSetAllocLarge()

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

Definition at line 737 of file aset.c.

738{
739 AllocSet set = (AllocSet) context;
740 AllocBlock block;
741 MemoryChunk *chunk;
742 Size chunk_size;
743 Size blksize;
744
745 /* validate 'size' is within the limits for the given 'flags' */
746 MemoryContextCheckSize(context, size, flags);
747
748#ifdef MEMORY_CONTEXT_CHECKING
749 /* ensure there's always space for the sentinel byte */
750 chunk_size = MAXALIGN(size + 1);
751#else
752 chunk_size = MAXALIGN(size);
753#endif
754
755 blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
756 block = (AllocBlock) malloc(blksize);
757 if (block == NULL)
758 return MemoryContextAllocationFailure(context, size, flags);
759
760 /* Make a vchunk covering the new block's header */
762
763 context->mem_allocated += blksize;
764
765 block->aset = set;
766 block->freeptr = block->endptr = ((char *) block) + blksize;
767
768 chunk = (MemoryChunk *) (((char *) block) + ALLOC_BLOCKHDRSZ);
769
770 /* mark the MemoryChunk as externally managed */
772
773#ifdef MEMORY_CONTEXT_CHECKING
774 chunk->requested_size = size;
775 /* set mark to catch clobber of "unused" space */
776 Assert(size < chunk_size);
777 set_sentinel(MemoryChunkGetPointer(chunk), size);
778#endif
779#ifdef RANDOMIZE_ALLOCATED_MEMORY
780 /* fill the allocated space with junk */
781 randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
782#endif
783
784 /*
785 * Stick the new block underneath the active allocation block, if any, so
786 * that we don't lose the use of the space remaining therein.
787 */
788 if (set->blocks != NULL)
789 {
790 block->prev = set->blocks;
791 block->next = set->blocks->next;
792 if (block->next)
793 block->next->prev = block;
794 set->blocks->next = block;
795 }
796 else
797 {
798 block->prev = NULL;
799 block->next = NULL;
800 set->blocks = block;
801 }
802
803 /* Ensure any padding bytes are marked NOACCESS. */
805 chunk_size - size);
806
807 /* Disallow access to the chunk header. */
809
810 return MemoryChunkGetPointer(chunk);
811}
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, VALGRIND_MAKE_MEM_NOACCESS, and VALGRIND_MEMPOOL_ALLOC.

Referenced by AllocSetAlloc().

◆ AllocSetContextCreateInternal()

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

Definition at line 343 of file aset.c.

348{
349 int freeListIndex;
350 Size firstBlockSize;
351 AllocSet set;
352 AllocBlock block;
353
354 /* ensure MemoryChunk's size is properly maxaligned */
356 "sizeof(MemoryChunk) is not maxaligned");
357 /* check we have enough space to store the freelist link */
359 "sizeof(AllocFreeListLink) larger than minimum allocation size");
360
361 /*
362 * First, validate allocation parameters. Once these were regular runtime
363 * tests and elog's, but in practice Asserts seem sufficient because
364 * nobody varies their parameters at runtime. We somewhat arbitrarily
365 * enforce a minimum 1K block size. We restrict the maximum block size to
366 * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
367 * regards to addressing the offset between the chunk and the block that
368 * the chunk is stored on. We would be unable to store the offset between
369 * the chunk and block for any chunks that were beyond
370 * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
371 * larger than this.
372 */
373 Assert(initBlockSize == MAXALIGN(initBlockSize) &&
374 initBlockSize >= 1024);
375 Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
376 maxBlockSize >= initBlockSize &&
377 AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
378 Assert(minContextSize == 0 ||
379 (minContextSize == MAXALIGN(minContextSize) &&
380 minContextSize >= 1024 &&
381 minContextSize <= maxBlockSize));
382 Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
383
384 /*
385 * Check whether the parameters match either available freelist. We do
386 * not need to demand a match of maxBlockSize.
387 */
388 if (minContextSize == ALLOCSET_DEFAULT_MINSIZE &&
389 initBlockSize == ALLOCSET_DEFAULT_INITSIZE)
390 freeListIndex = 0;
391 else if (minContextSize == ALLOCSET_SMALL_MINSIZE &&
392 initBlockSize == ALLOCSET_SMALL_INITSIZE)
393 freeListIndex = 1;
394 else
395 freeListIndex = -1;
396
397 /*
398 * If a suitable freelist entry exists, just recycle that context.
399 */
400 if (freeListIndex >= 0)
401 {
402 AllocSetFreeList *freelist = &context_freelists[freeListIndex];
403
404 if (freelist->first_free != NULL)
405 {
406 /* Remove entry from freelist */
407 set = freelist->first_free;
408 freelist->first_free = (AllocSet) set->header.nextchild;
409 freelist->num_free--;
410
411 /* Update its maxBlockSize; everything else should be OK */
412 set->maxBlockSize = maxBlockSize;
413
414 /* Reinitialize its header, installing correct name and parent */
416 T_AllocSetContext,
418 parent,
419 name);
420
421 ((MemoryContext) set)->mem_allocated =
422 KeeperBlock(set)->endptr - ((char *) set);
423
424 return (MemoryContext) set;
425 }
426 }
427
428 /* Determine size of initial block */
429 firstBlockSize = MAXALIGN(sizeof(AllocSetContext)) +
431 if (minContextSize != 0)
432 firstBlockSize = Max(firstBlockSize, minContextSize);
433 else
434 firstBlockSize = Max(firstBlockSize, initBlockSize);
435
436 /*
437 * Allocate the initial block. Unlike other aset.c blocks, it starts with
438 * the context header and its block header follows that.
439 */
440 set = (AllocSet) malloc(firstBlockSize);
441 if (set == NULL)
442 {
446 (errcode(ERRCODE_OUT_OF_MEMORY),
447 errmsg("out of memory"),
448 errdetail("Failed while creating memory context \"%s\".",
449 name)));
450 }
451
452 /*
453 * Avoid writing code that can fail between here and MemoryContextCreate;
454 * we'd leak the header/initial block if we ereport in this stretch.
455 */
456
457 /* Create a vpool associated with the context */
458 VALGRIND_CREATE_MEMPOOL(set, 0, false);
459
460 /*
461 * Create a vchunk covering both the AllocSetContext struct and the keeper
462 * block's header. (Perhaps it would be more sensible for these to be two
463 * separate vchunks, but doing that seems to tickle bugs in some versions
464 * of Valgrind.) We must have these vchunks, and also a vchunk for each
465 * subsequently-added block header, so that Valgrind considers the
466 * pointers within them while checking for leaked memory. Note that
467 * Valgrind doesn't distinguish between these vchunks and those created by
468 * mcxt.c for the user-accessible-data chunks we allocate.
469 */
471
472 /* Fill in the initial block's block header */
473 block = KeeperBlock(set);
474 block->aset = set;
475 block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
476 block->endptr = ((char *) set) + firstBlockSize;
477 block->prev = NULL;
478 block->next = NULL;
479
480 /* Mark unallocated space NOACCESS; leave the block header alone. */
481 VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, block->endptr - block->freeptr);
482
483 /* Remember block as part of block list */
484 set->blocks = block;
485
486 /* Finish filling in aset-specific parts of the context header */
487 MemSetAligned(set->freelist, 0, sizeof(set->freelist));
488
489 set->initBlockSize = (uint32) initBlockSize;
490 set->maxBlockSize = (uint32) maxBlockSize;
491 set->nextBlockSize = (uint32) initBlockSize;
492 set->freeListIndex = freeListIndex;
493
494 /*
495 * Compute the allocation chunk size limit for this context. It can't be
496 * more than ALLOC_CHUNK_LIMIT because of the fixed number of freelists.
497 * If maxBlockSize is small then requests exceeding the maxBlockSize, or
498 * even a significant fraction of it, should be treated as large chunks
499 * too. For the typical case of maxBlockSize a power of 2, the chunk size
500 * limit will be at most 1/8th maxBlockSize, so that given a stream of
501 * requests that are all the maximum chunk size we will waste at most
502 * 1/8th of the allocated space.
503 *
504 * Also, allocChunkLimit must not exceed ALLOCSET_SEPARATE_THRESHOLD.
505 */
507 "ALLOC_CHUNK_LIMIT != ALLOCSET_SEPARATE_THRESHOLD");
508
509 /*
510 * Determine the maximum size that a chunk can be before we allocate an
511 * entire AllocBlock dedicated for that chunk. We set the absolute limit
512 * of that size as ALLOC_CHUNK_LIMIT but we reduce it further so that we
513 * can fit about ALLOC_CHUNK_FRACTION chunks this size on a maximally
514 * sized block. (We opt to keep allocChunkLimit a power-of-2 value
515 * primarily for legacy reasons rather than calculating it so that exactly
516 * ALLOC_CHUNK_FRACTION chunks fit on a maximally sized block.)
517 */
519 while ((Size) (set->allocChunkLimit + ALLOC_CHUNKHDRSZ) >
520 (Size) ((maxBlockSize - ALLOC_BLOCKHDRSZ) / ALLOC_CHUNK_FRACTION))
521 set->allocChunkLimit >>= 1;
522
523 /* Finally, do the type-independent part of context creation */
525 T_AllocSetContext,
527 parent,
528 name);
529
530 ((MemoryContext) set)->mem_allocated = firstBlockSize;
531
532 return (MemoryContext) set;
533}
#define KeeperBlock(set)
Definition: aset.c:240
#define ALLOC_CHUNK_FRACTION
Definition: aset.c:87
#define FIRST_BLOCKHDRSZ
Definition: aset.c:106
#define ALLOC_CHUNK_LIMIT
Definition: aset.c:85
static AllocSetFreeList context_freelists[2]
Definition: aset.c:253
#define Max(x, y)
Definition: c.h:997
#define MemSetAligned(start, val, len)
Definition: c.h:1049
uint32_t uint32
Definition: c.h:538
#define StaticAssertDecl(condition, errmessage)
Definition: c.h:935
#define StaticAssertStmt(condition, errmessage)
Definition: c.h:937
int errdetail(const char *fmt,...)
Definition: elog.c:1207
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:150
void MemoryContextCreate(MemoryContext node, NodeTag tag, MemoryContextMethodID method_id, MemoryContext parent, const char *name)
Definition: mcxt.c:1146
MemoryContext TopMemoryContext
Definition: mcxt.c:166
void MemoryContextStats(MemoryContext context)
Definition: mcxt.c:860
#define VALGRIND_CREATE_MEMPOOL(context, redzones, zeroed)
Definition: memdebug.h:24
#define ALLOCSET_SMALL_MINSIZE
Definition: memutils.h:167
#define ALLOCSET_DEFAULT_MINSIZE
Definition: memutils.h:157
#define AllocHugeSizeIsValid(size)
Definition: memutils.h:49
#define ALLOCSET_SEPARATE_THRESHOLD
Definition: memutils.h:187
#define ALLOCSET_SMALL_INITSIZE
Definition: memutils.h:168
#define ALLOCSET_DEFAULT_INITSIZE
Definition: memutils.h:158
#define MEMORYCHUNK_MAX_BLOCKOFFSET
struct MemoryContextData * MemoryContext
Definition: palloc.h:36
uint32 initBlockSize
Definition: aset.c:161
MemoryContextData header
Definition: aset.c:156
int freeListIndex
Definition: aset.c:166
int num_free
Definition: aset.c:248
AllocSetContext * first_free
Definition: aset.c:249
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, FIRST_BLOCKHDRSZ, 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, VALGRIND_CREATE_MEMPOOL, VALGRIND_MAKE_MEM_NOACCESS, and VALGRIND_MEMPOOL_ALLOC.

◆ AllocSetDelete()

void AllocSetDelete ( MemoryContext  context)

Definition at line 634 of file aset.c.

635{
636 AllocSet set = (AllocSet) context;
637 AllocBlock block = set->blocks;
639
641
642#ifdef MEMORY_CONTEXT_CHECKING
643 /* Check for corruption and leaks before freeing */
644 AllocSetCheck(context);
645#endif
646
647 /* Remember keeper block size for Assert below */
648 keepersize = KeeperBlock(set)->endptr - ((char *) set);
649
650 /*
651 * If the context is a candidate for a freelist, put it into that freelist
652 * instead of destroying it.
653 */
654 if (set->freeListIndex >= 0)
655 {
657
658 /*
659 * Reset the context, if it needs it, so that we aren't hanging on to
660 * more than the initial malloc chunk.
661 */
662 if (!context->isReset)
663 MemoryContextResetOnly(context);
664
665 /*
666 * If the freelist is full, just discard what's already in it. See
667 * comments with context_freelists[].
668 */
669 if (freelist->num_free >= MAX_FREE_CONTEXTS)
670 {
671 while (freelist->first_free != NULL)
672 {
673 AllocSetContext *oldset = freelist->first_free;
674
675 freelist->first_free = (AllocSetContext *) oldset->header.nextchild;
676 freelist->num_free--;
677
678 /* Destroy the context's vpool --- see notes below */
680
681 /* All that remains is to free the header/initial block */
682 free(oldset);
683 }
684 Assert(freelist->num_free == 0);
685 }
686
687 /* Now add the just-deleted context to the freelist. */
688 set->header.nextchild = (MemoryContext) freelist->first_free;
689 freelist->first_free = set;
690 freelist->num_free++;
691
692 return;
693 }
694
695 /* Free all blocks, except the keeper which is part of context header */
696 while (block != NULL)
697 {
698 AllocBlock next = block->next;
699
700 if (!IsKeeperBlock(set, block))
701 context->mem_allocated -= block->endptr - ((char *) block);
702
703#ifdef CLOBBER_FREED_MEMORY
704 wipe_mem(block, block->freeptr - ((char *) block));
705#endif
706
707 if (!IsKeeperBlock(set, block))
708 {
709 /* As in AllocSetReset, free block-header vchunks explicitly */
710 VALGRIND_MEMPOOL_FREE(set, block);
711 free(block);
712 }
713
714 block = next;
715 }
716
717 Assert(context->mem_allocated == keepersize);
718
719 /*
720 * Destroy the vpool. We don't seem to need to explicitly free the
721 * initial block's header vchunk, nor any user-data vchunks that Valgrind
722 * still knows about; they'll all go away automatically.
723 */
725
726 /* Finally, free the context header, including the keeper block */
727 free(set);
728}
#define IsKeeperBlock(set, block)
Definition: aset.c:244
#define MAX_FREE_CONTEXTS
Definition: aset.c:237
static int32 next
Definition: blutils.c:224
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:223
#define free(a)
Definition: header.h:65
void MemoryContextResetOnly(MemoryContext context)
Definition: mcxt.c:419
#define VALGRIND_DESTROY_MEMPOOL(context)
Definition: memdebug.h:25
#define VALGRIND_MEMPOOL_FREE(context, addr)
Definition: memdebug.h:30

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, PG_USED_FOR_ASSERTS_ONLY, VALGRIND_DESTROY_MEMPOOL, and VALGRIND_MEMPOOL_FREE.

◆ AllocSetFree()

void AllocSetFree ( void *  pointer)

Definition at line 1109 of file aset.c.

1110{
1111 AllocSet set;
1112 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1113
1114 /* Allow access to the chunk header. */
1116
1117 if (MemoryChunkIsExternal(chunk))
1118 {
1119 /* Release single-chunk block. */
1120 AllocBlock block = ExternalChunkGetBlock(chunk);
1121
1122 /*
1123 * Try to verify that we have a sane block pointer: the block header
1124 * should reference an aset and the freeptr should match the endptr.
1125 */
1126 if (!AllocBlockIsValid(block) || block->freeptr != block->endptr)
1127 elog(ERROR, "could not find block containing chunk %p", chunk);
1128
1129 set = block->aset;
1130
1131#ifdef MEMORY_CONTEXT_CHECKING
1132 {
1133 /* Test for someone scribbling on unused space in chunk */
1134 Assert(chunk->requested_size < (block->endptr - (char *) pointer));
1135 if (!sentinel_ok(pointer, chunk->requested_size))
1136 elog(WARNING, "detected write past chunk end in %s %p",
1137 set->header.name, chunk);
1138 }
1139#endif
1140
1141 /* OK, remove block from aset's list and free it */
1142 if (block->prev)
1143 block->prev->next = block->next;
1144 else
1145 set->blocks = block->next;
1146 if (block->next)
1147 block->next->prev = block->prev;
1148
1149 set->header.mem_allocated -= block->endptr - ((char *) block);
1150
1151#ifdef CLOBBER_FREED_MEMORY
1152 wipe_mem(block, block->freeptr - ((char *) block));
1153#endif
1154
1155 /* As in AllocSetReset, free block-header vchunks explicitly */
1156 VALGRIND_MEMPOOL_FREE(set, block);
1157
1158 free(block);
1159 }
1160 else
1161 {
1162 AllocBlock block = MemoryChunkGetBlock(chunk);
1163 int fidx;
1165
1166 /*
1167 * In this path, for speed reasons we just Assert that the referenced
1168 * block is good. We can also Assert that the value field is sane.
1169 * Future field experience may show that these Asserts had better
1170 * become regular runtime test-and-elog checks.
1171 */
1172 Assert(AllocBlockIsValid(block));
1173 set = block->aset;
1174
1175 fidx = MemoryChunkGetValue(chunk);
1177 link = GetFreeListLink(chunk);
1178
1179#ifdef MEMORY_CONTEXT_CHECKING
1180 /* Test for someone scribbling on unused space in chunk */
1181 if (chunk->requested_size < GetChunkSizeFromFreeListIdx(fidx))
1182 if (!sentinel_ok(pointer, chunk->requested_size))
1183 elog(WARNING, "detected write past chunk end in %s %p",
1184 set->header.name, chunk);
1185#endif
1186
1187#ifdef CLOBBER_FREED_MEMORY
1188 wipe_mem(pointer, GetChunkSizeFromFreeListIdx(fidx));
1189#endif
1190 /* push this chunk onto the top of the free list */
1192 link->next = set->freelist[fidx];
1194 set->freelist[fidx] = chunk;
1195
1196#ifdef MEMORY_CONTEXT_CHECKING
1197
1198 /*
1199 * Reset requested_size to InvalidAllocSize in chunks that are on free
1200 * list.
1201 */
1202 chunk->requested_size = InvalidAllocSize;
1203#endif
1204 }
1205}
#define AllocBlockIsValid(block)
Definition: aset.c:203
#define FreeListIdxIsValid(fidx)
Definition: aset.c:138
#define ExternalChunkGetBlock(chunk)
Definition: aset.c:211
#define WARNING
Definition: elog.h:36
#define elog(elevel,...)
Definition: elog.h:226
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, VALGRIND_MEMPOOL_FREE, and WARNING.

Referenced by AllocSetRealloc().

◆ AllocSetFreeIndex()

static int AllocSetFreeIndex ( Size  size)
inlinestatic

Definition at line 273 of file aset.c.

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

1493{
1494 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1495 AllocBlock block;
1496 AllocSet set;
1497
1498 /* Allow access to the chunk header. */
1500
1501 if (MemoryChunkIsExternal(chunk))
1502 block = ExternalChunkGetBlock(chunk);
1503 else
1504 block = (AllocBlock) MemoryChunkGetBlock(chunk);
1505
1506 /* Disallow access to the chunk header. */
1508
1509 Assert(AllocBlockIsValid(block));
1510 set = block->aset;
1511
1512 return &set->header;
1513}

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 1521 of file aset.c.

1522{
1523 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1524 int fidx;
1525
1526 /* Allow access to the chunk header. */
1528
1529 if (MemoryChunkIsExternal(chunk))
1530 {
1531 AllocBlock block = ExternalChunkGetBlock(chunk);
1532
1533 /* Disallow access to the chunk header. */
1535
1536 Assert(AllocBlockIsValid(block));
1537
1538 return block->endptr - (char *) chunk;
1539 }
1540
1541 fidx = MemoryChunkGetValue(chunk);
1543
1544 /* Disallow access to the chunk header. */
1546
1548}

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 1555 of file aset.c.

1556{
1557 Assert(AllocSetIsValid(context));
1558
1559 /*
1560 * For now, we say "empty" only if the context is new or just reset. We
1561 * could examine the freelists to determine if all space has been freed,
1562 * but it's not really worth the trouble for present uses of this
1563 * functionality.
1564 */
1565 if (context->isReset)
1566 return true;
1567 return false;
1568}

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

◆ AllocSetRealloc()

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

Definition at line 1220 of file aset.c.

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

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, VALGRIND_MEMPOOL_CHANGE, and WARNING.

◆ AllocSetReset()

void AllocSetReset ( MemoryContext  context)

Definition at line 548 of file aset.c.

549{
550 AllocSet set = (AllocSet) context;
551 AllocBlock block;
553
555
556#ifdef MEMORY_CONTEXT_CHECKING
557 /* Check for corruption and leaks before freeing */
558 AllocSetCheck(context);
559#endif
560
561 /* Remember keeper block size for Assert below */
562 keepersize = KeeperBlock(set)->endptr - ((char *) set);
563
564 /* Clear chunk freelists */
565 MemSetAligned(set->freelist, 0, sizeof(set->freelist));
566
567 block = set->blocks;
568
569 /* New blocks list will be just the keeper block */
570 set->blocks = KeeperBlock(set);
571
572 while (block != NULL)
573 {
574 AllocBlock next = block->next;
575
576 if (IsKeeperBlock(set, block))
577 {
578 /* Reset the block, but don't return it to malloc */
579 char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
580
581#ifdef CLOBBER_FREED_MEMORY
582 wipe_mem(datastart, block->freeptr - datastart);
583#else
584 /* wipe_mem() would have done this */
585 VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
586#endif
587 block->freeptr = datastart;
588 block->prev = NULL;
589 block->next = NULL;
590 }
591 else
592 {
593 /* Normal case, release the block */
594 context->mem_allocated -= block->endptr - ((char *) block);
595
596#ifdef CLOBBER_FREED_MEMORY
597 wipe_mem(block, block->freeptr - ((char *) block));
598#endif
599
600 /*
601 * We need to free the block header's vchunk explicitly, although
602 * the user-data vchunks within will go away in the TRIM below.
603 * Otherwise Valgrind complains about leaked allocations.
604 */
605 VALGRIND_MEMPOOL_FREE(set, block);
606
607 free(block);
608 }
609 block = next;
610 }
611
612 Assert(context->mem_allocated == keepersize);
613
614 /*
615 * Instruct Valgrind to throw away all the vchunks associated with this
616 * context, except for the one covering the AllocSetContext and
617 * keeper-block header. This gets rid of the vchunks for whatever user
618 * data is getting discarded by the context reset.
619 */
621
622 /* Reset block size allocation sequence, too */
623 set->nextBlockSize = set->initBlockSize;
624}
#define VALGRIND_MEMPOOL_TRIM(context, addr, size)
Definition: memdebug.h:32

References ALLOC_BLOCKHDRSZ, AllocSetIsValid, Assert(), AllocSetContext::blocks, AllocBlockData::endptr, FIRST_BLOCKHDRSZ, 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, VALGRIND_MAKE_MEM_NOACCESS, VALGRIND_MEMPOOL_FREE, and VALGRIND_MEMPOOL_TRIM.

◆ AllocSetStats()

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

Definition at line 1580 of file aset.c.

1583{
1584 AllocSet set = (AllocSet) context;
1585 Size nblocks = 0;
1586 Size freechunks = 0;
1587 Size totalspace;
1588 Size freespace = 0;
1589 AllocBlock block;
1590 int fidx;
1591
1592 Assert(AllocSetIsValid(set));
1593
1594 /* Include context header in totalspace */
1595 totalspace = MAXALIGN(sizeof(AllocSetContext));
1596
1597 for (block = set->blocks; block != NULL; block = block->next)
1598 {
1599 nblocks++;
1600 totalspace += block->endptr - ((char *) block);
1601 freespace += block->endptr - block->freeptr;
1602 }
1603 for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++)
1604 {
1605 Size chksz = GetChunkSizeFromFreeListIdx(fidx);
1606 MemoryChunk *chunk = set->freelist[fidx];
1607
1608 while (chunk != NULL)
1609 {
1611
1612 /* Allow access to the chunk header. */
1614 Assert(MemoryChunkGetValue(chunk) == fidx);
1616
1617 freechunks++;
1618 freespace += chksz + ALLOC_CHUNKHDRSZ;
1619
1621 chunk = link->next;
1623 }
1624 }
1625
1626 if (printfunc)
1627 {
1628 char stats_string[200];
1629
1630 snprintf(stats_string, sizeof(stats_string),
1631 "%zu total in %zu blocks; %zu free (%zu chunks); %zu used",
1632 totalspace, nblocks, freespace, freechunks,
1633 totalspace - freespace);
1634 printfunc(context, passthru, stats_string, print_to_stderr);
1635 }
1636
1637 if (totals)
1638 {
1639 totals->nblocks += nblocks;
1640 totals->freechunks += freechunks;
1641 totals->totalspace += totalspace;
1642 totals->freespace += freespace;
1643 }
1644}
#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 253 of file aset.c.

Referenced by AllocSetContextCreateInternal(), and AllocSetDelete().