diff options
Diffstat (limited to 'src/backend/utils/mmgr/aset.c')
-rw-r--r-- | src/backend/utils/mmgr/aset.c | 1086 |
1 files changed, 0 insertions, 1086 deletions
diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c deleted file mode 100644 index 5dc20557dd2..00000000000 --- a/src/backend/utils/mmgr/aset.c +++ /dev/null @@ -1,1086 +0,0 @@ -/*------------------------------------------------------------------------- - * - * aset.c - * Allocation set definitions. - * - * AllocSet is our standard implementation of the abstract MemoryContext - * type. - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.46 2002/06/20 20:29:40 momjian Exp $ - * - * NOTE: - * This is a new (Feb. 05, 1999) implementation of the allocation set - * routines. AllocSet...() does not use OrderedSet...() any more. - * Instead it manages allocations in a block pool by itself, combining - * many small allocations in a few bigger blocks. AllocSetFree() normally - * doesn't free() memory really. It just add's the free'd area to some - * list for later reuse by AllocSetAlloc(). All memory blocks are free()'d - * at once on AllocSetReset(), which happens when the memory context gets - * destroyed. - * Jan Wieck - * - * Performance improvement from Tom Lane, 8/99: for extremely large request - * sizes, we do want to be able to give the memory back to free() as soon - * as it is pfree()'d. Otherwise we risk tying up a lot of memory in - * freelist entries that might never be usable. This is specially needed - * when the caller is repeatedly repalloc()'ing a block bigger and bigger; - * the previous instances of the block were guaranteed to be wasted until - * AllocSetReset() under the old way. - * - * Further improvement 12/00: as the code stood, request sizes in the - * midrange between "small" and "large" were handled very inefficiently, - * because any sufficiently large free chunk would be used to satisfy a - * request, even if it was much larger than necessary. This led to more - * and more wasted space in allocated chunks over time. To fix, get rid - * of the midrange behavior: we now handle only "small" power-of-2-size - * chunks as chunks. Anything "large" is passed off to malloc(). Change - * the number of freelists to change the small/large boundary. - * - * - * About CLOBBER_FREED_MEMORY: - * - * If this symbol is defined, all freed memory is overwritten with 0x7F's. - * This is useful for catching places that reference already-freed memory. - * - * About MEMORY_CONTEXT_CHECKING: - * - * Since we usually round request sizes up to the next power of 2, there - * is often some unused space immediately after a requested data area. - * Thus, if someone makes the common error of writing past what they've - * requested, the problem is likely to go unnoticed ... until the day when - * there *isn't* any wasted space, perhaps because of different memory - * alignment on a new platform, or some other effect. To catch this sort - * of problem, the MEMORY_CONTEXT_CHECKING option stores 0x7E just beyond - * the requested space whenever the request is less than the actual chunk - * size, and verifies that the byte is undamaged when the chunk is freed. - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include "utils/memutils.h" - -/* Define this to detail debug alloc information */ -/* #define HAVE_ALLOCINFO */ - -/*-------------------- - * Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS), - * for k = 0 .. ALLOCSET_NUM_FREELISTS-1. - * - * Note that all chunks in the freelists have power-of-2 sizes. This - * improves recyclability: we may waste some space, but the wasted space - * should stay pretty constant as requests are made and released. - * - * A request too large for the last freelist is handled by allocating a - * dedicated block from malloc(). The block still has a block header and - * chunk header, but when the chunk is freed we'll return the whole block - * to malloc(), not put it on our freelists. - * - * CAUTION: ALLOC_MINBITS must be large enough so that - * 1<<ALLOC_MINBITS is at least MAXALIGN, - * or we may fail to align the smallest chunks adequately. - * 16-byte alignment is enough on all currently known machines. - * - * With the current parameters, request sizes up to 8K are treated as chunks, - * larger requests go into dedicated blocks. Change ALLOCSET_NUM_FREELISTS - * to adjust the boundary point. - *-------------------- - */ - -#define ALLOC_MINBITS 4 /* smallest chunk size is 16 bytes */ -#define ALLOCSET_NUM_FREELISTS 10 -#define ALLOC_CHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS)) -/* Size of largest chunk that we use a fixed size for */ - -/*-------------------- - * The first block allocated for an allocset has size initBlockSize. - * Each time we have to allocate another block, we double the block size - * (if possible, and without exceeding maxBlockSize), so as to reduce - * the bookkeeping load on malloc(). - * - * Blocks allocated to hold oversize chunks do not follow this rule, however; - * they are just however big they need to be to hold that single chunk. - *-------------------- - */ - -#define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData)) -#define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData)) - -typedef struct AllocBlockData *AllocBlock; /* forward reference */ -typedef struct AllocChunkData *AllocChunk; - -/* - * AllocPointer - * Aligned pointer which may be a member of an allocation set. - */ -typedef void *AllocPointer; - -/* - * AllocSetContext is our standard implementation of MemoryContext. - */ -typedef struct AllocSetContext -{ - MemoryContextData header; /* Standard memory-context fields */ - /* Info about storage allocated in this context: */ - AllocBlock blocks; /* head of list of blocks in this set */ - AllocChunk freelist[ALLOCSET_NUM_FREELISTS]; /* free chunk lists */ - /* Allocation parameters for this context: */ - Size initBlockSize; /* initial block size */ - Size maxBlockSize; /* maximum block size */ - AllocBlock keeper; /* if not NULL, keep this block over - * resets */ -} AllocSetContext; - -typedef AllocSetContext *AllocSet; - -/* - * AllocBlock - * An AllocBlock is the unit of memory that is obtained by aset.c - * from malloc(). It contains one or more AllocChunks, which are - * the units requested by palloc() and freed by pfree(). AllocChunks - * cannot be returned to malloc() individually, instead they are put - * on freelists by pfree() and re-used by the next palloc() that has - * a matching request size. - * - * AllocBlockData is the header data for a block --- the usable space - * within the block begins at the next alignment boundary. - */ -typedef struct AllocBlockData -{ - AllocSet aset; /* aset that owns this block */ - AllocBlock next; /* next block in aset's blocks list */ - char *freeptr; /* start of free space in this block */ - char *endptr; /* end of space in this block */ -} AllocBlockData; - -/* - * AllocChunk - * The prefix of each piece of memory in an AllocBlock - * - * NB: this MUST match StandardChunkHeader as defined by utils/memutils.h. - */ -typedef struct AllocChunkData -{ - /* aset is the owning aset if allocated, or the freelist link if free */ - void *aset; - /* size is always the size of the usable space in the chunk */ - Size size; -#ifdef MEMORY_CONTEXT_CHECKING - /* when debugging memory usage, also store actual requested size */ - /* this is zero in a free chunk */ - Size requested_size; -#endif -} AllocChunkData; - -/* - * AllocPointerIsValid - * True iff pointer is valid allocation pointer. - */ -#define AllocPointerIsValid(pointer) PointerIsValid(pointer) - -/* - * AllocSetIsValid - * True iff set is valid allocation set. - */ -#define AllocSetIsValid(set) PointerIsValid(set) - -#define AllocPointerGetChunk(ptr) \ - ((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ)) -#define AllocChunkGetPointer(chk) \ - ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ)) - -/* - * These functions implement the MemoryContext API for AllocSet contexts. - */ -static void *AllocSetAlloc(MemoryContext context, Size size); -static void AllocSetFree(MemoryContext context, void *pointer); -static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size); -static void AllocSetInit(MemoryContext context); -static void AllocSetReset(MemoryContext context); -static void AllocSetDelete(MemoryContext context); - -#ifdef MEMORY_CONTEXT_CHECKING -static void AllocSetCheck(MemoryContext context); -#endif -static void AllocSetStats(MemoryContext context); - -/* - * This is the virtual function table for AllocSet contexts. - */ -static MemoryContextMethods AllocSetMethods = { - AllocSetAlloc, - AllocSetFree, - AllocSetRealloc, - AllocSetInit, - AllocSetReset, - AllocSetDelete, -#ifdef MEMORY_CONTEXT_CHECKING - AllocSetCheck, -#endif - AllocSetStats -}; - - -/* ---------- - * Debug macros - * ---------- - */ -#ifdef HAVE_ALLOCINFO -#define AllocFreeInfo(_cxt, _chunk) \ - fprintf(stderr, "AllocFree: %s: %p, %d\n", \ - (_cxt)->header.name, (_chunk), (_chunk)->size) -#define AllocAllocInfo(_cxt, _chunk) \ - fprintf(stderr, "AllocAlloc: %s: %p, %d\n", \ - (_cxt)->header.name, (_chunk), (_chunk)->size) -#else -#define AllocFreeInfo(_cxt, _chunk) -#define AllocAllocInfo(_cxt, _chunk) -#endif - -/* ---------- - * AllocSetFreeIndex - - * - * Depending on the size of an allocation compute which freechunk - * list of the alloc set it belongs to. Caller must have verified - * that size <= ALLOC_CHUNK_LIMIT. - * ---------- - */ -static inline int -AllocSetFreeIndex(Size size) -{ - int idx = 0; - - if (size > 0) - { - size = (size - 1) >> ALLOC_MINBITS; - while (size != 0) - { - idx++; - size >>= 1; - } - Assert(idx < ALLOCSET_NUM_FREELISTS); - } - - return idx; -} - - -/* - * Public routines - */ - - -/* - * AllocSetContextCreate - * Create a new AllocSet context. - * - * parent: parent context, or NULL if top-level context - * name: name of context (for debugging --- string will be copied) - * minContextSize: minimum context size - * initBlockSize: initial allocation block size - * maxBlockSize: maximum allocation block size - */ -MemoryContext -AllocSetContextCreate(MemoryContext parent, - const char *name, - Size minContextSize, - Size initBlockSize, - Size maxBlockSize) -{ - AllocSet context; - - /* Do the type-independent part of context creation */ - context = (AllocSet) MemoryContextCreate(T_AllocSetContext, - sizeof(AllocSetContext), - &AllocSetMethods, - parent, - name); - - /* - * Make sure alloc parameters are reasonable, and save them. - * - * We somewhat arbitrarily enforce a minimum 1K block size. - */ - initBlockSize = MAXALIGN(initBlockSize); - if (initBlockSize < 1024) - initBlockSize = 1024; - maxBlockSize = MAXALIGN(maxBlockSize); - if (maxBlockSize < initBlockSize) - maxBlockSize = initBlockSize; - context->initBlockSize = initBlockSize; - context->maxBlockSize = maxBlockSize; - - /* - * Grab always-allocated space, if requested - */ - if (minContextSize > ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ) - { - Size blksize = MAXALIGN(minContextSize); - AllocBlock block; - - block = (AllocBlock) malloc(blksize); - if (block == NULL) - { - MemoryContextStats(TopMemoryContext); - elog(ERROR, "Memory exhausted in AllocSetContextCreate(%lu)", - (unsigned long) minContextSize); - } - block->aset = context; - block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; - block->endptr = ((char *) block) + blksize; - block->next = context->blocks; - context->blocks = block; - /* Mark block as not to be released at reset time */ - context->keeper = block; - } - - return (MemoryContext) context; -} - -/* - * AllocSetInit - * Context-type-specific initialization routine. - * - * This is called by MemoryContextCreate() after setting up the - * generic MemoryContext fields and before linking the new context - * into the context tree. We must do whatever is needed to make the - * new context minimally valid for deletion. We must *not* risk - * failure --- thus, for example, allocating more memory is not cool. - * (AllocSetContextCreate can allocate memory when it gets control - * back, however.) - */ -static void -AllocSetInit(MemoryContext context) -{ - /* - * Since MemoryContextCreate already zeroed the context node, we don't - * have to do anything here: it's already OK. - */ -} - -/* - * AllocSetReset - * Frees all memory which is allocated in the given set. - * - * Actually, this routine has some discretion about what to do. - * It should mark all allocated chunks freed, but it need not - * necessarily give back all the resources the set owns. Our - * actual implementation is that we hang on to any "keeper" - * block specified for the set. - */ -static void -AllocSetReset(MemoryContext context) -{ - AllocSet set = (AllocSet) context; - AllocBlock block = set->blocks; - - AssertArg(AllocSetIsValid(set)); - -#ifdef MEMORY_CONTEXT_CHECKING - /* Check for corruption and leaks before freeing */ - AllocSetCheck(context); -#endif - - /* Clear chunk freelists */ - MemSet(set->freelist, 0, sizeof(set->freelist)); - /* New blocks list is either empty or just the keeper block */ - set->blocks = set->keeper; - - while (block != NULL) - { - AllocBlock next = block->next; - - if (block == set->keeper) - { - /* Reset the block, but don't return it to malloc */ - char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ; - -#ifdef CLOBBER_FREED_MEMORY - /* Wipe freed memory for debugging purposes */ - memset(datastart, 0x7F, block->freeptr - datastart); -#endif - block->freeptr = datastart; - block->next = NULL; - } - else - { - /* Normal case, release the block */ -#ifdef CLOBBER_FREED_MEMORY - /* Wipe freed memory for debugging purposes */ - memset(block, 0x7F, block->freeptr - ((char *) block)); -#endif - free(block); - } - block = next; - } -} - -/* - * AllocSetDelete - * Frees all memory which is allocated in the given set, - * in preparation for deletion of the set. - * - * Unlike AllocSetReset, this *must* free all resources of the set. - * But note we are not responsible for deleting the context node itself. - */ -static void -AllocSetDelete(MemoryContext context) -{ - AllocSet set = (AllocSet) context; - AllocBlock block = set->blocks; - - AssertArg(AllocSetIsValid(set)); - -#ifdef MEMORY_CONTEXT_CHECKING - /* Check for corruption and leaks before freeing */ - AllocSetCheck(context); -#endif - - /* Make it look empty, just in case... */ - MemSet(set->freelist, 0, sizeof(set->freelist)); - set->blocks = NULL; - set->keeper = NULL; - - while (block != NULL) - { - AllocBlock next = block->next; - -#ifdef CLOBBER_FREED_MEMORY - /* Wipe freed memory for debugging purposes */ - memset(block, 0x7F, block->freeptr - ((char *) block)); -#endif - free(block); - block = next; - } -} - -/* - * AllocSetAlloc - * Returns pointer to allocated memory of given size; memory is added - * to the set. - */ -static void * -AllocSetAlloc(MemoryContext context, Size size) -{ - AllocSet set = (AllocSet) context; - AllocBlock block; - AllocChunk chunk; - AllocChunk priorfree; - int fidx; - Size chunk_size; - Size blksize; - - AssertArg(AllocSetIsValid(set)); - - /* - * If requested size exceeds maximum for chunks, allocate an entire - * block for this request. - */ - if (size > ALLOC_CHUNK_LIMIT) - { - chunk_size = MAXALIGN(size); - blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; - block = (AllocBlock) malloc(blksize); - if (block == NULL) - { - MemoryContextStats(TopMemoryContext); - elog(ERROR, "Memory exhausted in AllocSetAlloc(%lu)", - (unsigned long) size); - } - block->aset = set; - block->freeptr = block->endptr = ((char *) block) + blksize; - - chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ); - chunk->aset = set; - chunk->size = chunk_size; -#ifdef MEMORY_CONTEXT_CHECKING - chunk->requested_size = size; - /* set mark to catch clobber of "unused" space */ - if (size < chunk_size) - ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E; -#endif - - /* - * Stick the new block underneath the active allocation block, so - * that we don't lose the use of the space remaining therein. - */ - if (set->blocks != NULL) - { - block->next = set->blocks->next; - set->blocks->next = block; - } - else - { - block->next = NULL; - set->blocks = block; - } - - AllocAllocInfo(set, chunk); - return AllocChunkGetPointer(chunk); - } - - /* - * Request is small enough to be treated as a chunk. Look in the - * corresponding free list to see if there is a free chunk we could - * reuse. - */ - fidx = AllocSetFreeIndex(size); - priorfree = NULL; - for (chunk = set->freelist[fidx]; chunk; chunk = (AllocChunk) chunk->aset) - { - if (chunk->size >= size) - break; - priorfree = chunk; - } - - /* - * If one is found, remove it from the free list, make it again a - * member of the alloc set and return its data address. - */ - if (chunk != NULL) - { - if (priorfree == NULL) - set->freelist[fidx] = (AllocChunk) chunk->aset; - else - priorfree->aset = chunk->aset; - - chunk->aset = (void *) set; - -#ifdef MEMORY_CONTEXT_CHECKING - chunk->requested_size = size; - /* set mark to catch clobber of "unused" space */ - if (size < chunk->size) - ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E; -#endif - - AllocAllocInfo(set, chunk); - return AllocChunkGetPointer(chunk); - } - - /* - * Choose the actual chunk size to allocate. - */ - chunk_size = 1 << (fidx + ALLOC_MINBITS); - Assert(chunk_size >= size); - - /* - * If there is enough room in the active allocation block, we will put - * the chunk into that block. Else must start a new one. - */ - if ((block = set->blocks) != NULL) - { - Size availspace = block->endptr - block->freeptr; - - if (availspace < (chunk_size + ALLOC_CHUNKHDRSZ)) - { - /* - * The existing active (top) block does not have enough room - * for the requested allocation, but it might still have a - * useful amount of space in it. Once we push it down in the - * block list, we'll never try to allocate more space from it. - * So, before we do that, carve up its free space into chunks - * that we can put on the set's freelists. - * - * Because we can only get here when there's less than - * ALLOC_CHUNK_LIMIT left in the block, this loop cannot - * iterate more than ALLOCSET_NUM_FREELISTS-1 times. - */ - while (availspace >= ((1 << ALLOC_MINBITS) + ALLOC_CHUNKHDRSZ)) - { - Size availchunk = availspace - ALLOC_CHUNKHDRSZ; - int a_fidx = AllocSetFreeIndex(availchunk); - - /* - * In most cases, we'll get back the index of the next - * larger freelist than the one we need to put this chunk - * on. The exception is when availchunk is exactly a - * power of 2. - */ - if (availchunk != (1 << (a_fidx + ALLOC_MINBITS))) - { - a_fidx--; - Assert(a_fidx >= 0); - availchunk = (1 << (a_fidx + ALLOC_MINBITS)); - } - - chunk = (AllocChunk) (block->freeptr); - - block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ); - availspace -= (availchunk + ALLOC_CHUNKHDRSZ); - - chunk->size = availchunk; -#ifdef MEMORY_CONTEXT_CHECKING - chunk->requested_size = 0; /* mark it free */ -#endif - chunk->aset = (void *) set->freelist[a_fidx]; - set->freelist[a_fidx] = chunk; - } - - /* Mark that we need to create a new block */ - block = NULL; - } - } - - /* - * Time to create a new regular (multi-chunk) block? - */ - if (block == NULL) - { - Size required_size; - - if (set->blocks == NULL) - { - /* First block of the alloc set, use initBlockSize */ - blksize = set->initBlockSize; - } - else - { - /* Get size of prior block */ - blksize = set->blocks->endptr - ((char *) set->blocks); - - /* - * Special case: if very first allocation was for a large - * chunk (or we have a small "keeper" block), could have an - * undersized top block. Do something reasonable. - */ - if (blksize < set->initBlockSize) - blksize = set->initBlockSize; - else - { - /* Crank it up, but not past max */ - blksize <<= 1; - if (blksize > set->maxBlockSize) - blksize = set->maxBlockSize; - } - } - - /* - * If initBlockSize is less than ALLOC_CHUNK_LIMIT, we could need - * more space... - */ - required_size = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; - if (blksize < required_size) - blksize = required_size; - - /* Try to allocate it */ - block = (AllocBlock) malloc(blksize); - - /* - * We could be asking for pretty big blocks here, so cope if - * malloc fails. But give up if there's less than a meg or so - * available... - */ - while (block == NULL && blksize > 1024 * 1024) - { - blksize >>= 1; - if (blksize < required_size) - break; - block = (AllocBlock) malloc(blksize); - } - - if (block == NULL) - { - MemoryContextStats(TopMemoryContext); - elog(ERROR, "Memory exhausted in AllocSetAlloc(%lu)", - (unsigned long) size); - } - - block->aset = set; - block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; - block->endptr = ((char *) block) + blksize; - - block->next = set->blocks; - set->blocks = block; - } - - /* - * OK, do the allocation - */ - chunk = (AllocChunk) (block->freeptr); - - block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ); - Assert(block->freeptr <= block->endptr); - - chunk->aset = (void *) set; - chunk->size = chunk_size; -#ifdef MEMORY_CONTEXT_CHECKING - chunk->requested_size = size; - /* set mark to catch clobber of "unused" space */ - if (size < chunk->size) - ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E; -#endif - - AllocAllocInfo(set, chunk); - return AllocChunkGetPointer(chunk); -} - -/* - * AllocSetFree - * Frees allocated memory; memory is removed from the set. - */ -static void -AllocSetFree(MemoryContext context, void *pointer) -{ - AllocSet set = (AllocSet) context; - AllocChunk chunk = AllocPointerGetChunk(pointer); - - AllocFreeInfo(set, chunk); - -#ifdef MEMORY_CONTEXT_CHECKING - /* Test for someone scribbling on unused space in chunk */ - if (chunk->requested_size < chunk->size) - if (((char *) pointer)[chunk->requested_size] != 0x7E) - elog(WARNING, "AllocSetFree: detected write past chunk end in %s %p", - set->header.name, chunk); -#endif - - if (chunk->size > ALLOC_CHUNK_LIMIT) - { - /* - * Big chunks are certain to have been allocated as single-chunk - * blocks. Find the containing block and return it to malloc(). - */ - AllocBlock block = set->blocks; - AllocBlock prevblock = NULL; - - while (block != NULL) - { - if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ)) - break; - prevblock = block; - block = block->next; - } - if (block == NULL) - elog(ERROR, "AllocSetFree: cannot find block containing chunk %p", - chunk); - /* let's just make sure chunk is the only one in the block */ - Assert(block->freeptr == ((char *) block) + - (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)); - - /* OK, remove block from aset's list and free it */ - if (prevblock == NULL) - set->blocks = block->next; - else - prevblock->next = block->next; -#ifdef CLOBBER_FREED_MEMORY - /* Wipe freed memory for debugging purposes */ - memset(block, 0x7F, block->freeptr - ((char *) block)); -#endif - free(block); - } - else - { - /* Normal case, put the chunk into appropriate freelist */ - int fidx = AllocSetFreeIndex(chunk->size); - - chunk->aset = (void *) set->freelist[fidx]; - -#ifdef CLOBBER_FREED_MEMORY - /* Wipe freed memory for debugging purposes */ - memset(pointer, 0x7F, chunk->size); -#endif - -#ifdef MEMORY_CONTEXT_CHECKING - /* Reset requested_size to 0 in chunks that are on freelist */ - chunk->requested_size = 0; -#endif - set->freelist[fidx] = chunk; - } -} - -/* - * AllocSetRealloc - * Returns new pointer to allocated memory of given size; this memory - * is added to the set. Memory associated with given pointer is copied - * into the new memory, and the old memory is freed. - */ -static void * -AllocSetRealloc(MemoryContext context, void *pointer, Size size) -{ - AllocSet set = (AllocSet) context; - AllocChunk chunk = AllocPointerGetChunk(pointer); - Size oldsize = chunk->size; - -#ifdef MEMORY_CONTEXT_CHECKING - /* Test for someone scribbling on unused space in chunk */ - if (chunk->requested_size < oldsize) - if (((char *) pointer)[chunk->requested_size] != 0x7E) - elog(WARNING, "AllocSetRealloc: detected write past chunk end in %s %p", - set->header.name, chunk); -#endif - - /* - * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the - * allocated area already is >= the new size. (In particular, we - * always fall out here if the requested size is a decrease.) - */ - if (oldsize >= size) - { -#ifdef MEMORY_CONTEXT_CHECKING - chunk->requested_size = size; - /* set mark to catch clobber of "unused" space */ - if (size < oldsize) - ((char *) pointer)[size] = 0x7E; -#endif - return pointer; - } - - if (oldsize > ALLOC_CHUNK_LIMIT) - { - /* - * The chunk must been allocated as a single-chunk block. Find - * the containing block and use realloc() to make it bigger with - * minimum space wastage. - */ - AllocBlock block = set->blocks; - AllocBlock prevblock = NULL; - Size chksize; - Size blksize; - - while (block != NULL) - { - if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ)) - break; - prevblock = block; - block = block->next; - } - if (block == NULL) - elog(ERROR, "AllocSetRealloc: cannot find block containing chunk %p", - chunk); - /* let's just make sure chunk is the only one in the block */ - Assert(block->freeptr == ((char *) block) + - (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)); - - /* Do the realloc */ - chksize = MAXALIGN(size); - blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; - block = (AllocBlock) realloc(block, blksize); - if (block == NULL) - { - MemoryContextStats(TopMemoryContext); - elog(ERROR, "Memory exhausted in AllocSetReAlloc(%lu)", - (unsigned long) size); - } - block->freeptr = block->endptr = ((char *) block) + blksize; - - /* Update pointers since block has likely been moved */ - chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ); - if (prevblock == NULL) - set->blocks = block; - else - prevblock->next = block; - chunk->size = chksize; - -#ifdef MEMORY_CONTEXT_CHECKING - chunk->requested_size = size; - /* set mark to catch clobber of "unused" space */ - if (size < chunk->size) - ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E; -#endif - - return AllocChunkGetPointer(chunk); - } - else - { - /* - * Small-chunk case. If the chunk is the last one in its block, - * there might be enough free space after it that we can just - * enlarge the chunk in-place. It's relatively painful to find - * the containing block in the general case, but we can detect - * last-ness quite cheaply for the typical case where the chunk is - * in the active (topmost) allocation block. (At least with the - * regression tests and code as of 1/2001, realloc'ing the last - * chunk of a non-topmost block hardly ever happens, so it's not - * worth scanning the block list to catch that case.) - * - * NOTE: must be careful not to create a chunk of a size that - * AllocSetAlloc would not create, else we'll get confused later. - */ - AllocPointer newPointer; - - if (size <= ALLOC_CHUNK_LIMIT) - { - AllocBlock block = set->blocks; - char *chunk_end; - - chunk_end = (char *) chunk + (oldsize + ALLOC_CHUNKHDRSZ); - if (chunk_end == block->freeptr) - { - /* OK, it's last in block ... is there room? */ - Size freespace = block->endptr - block->freeptr; - int fidx; - Size newsize; - Size delta; - - fidx = AllocSetFreeIndex(size); - newsize = 1 << (fidx + ALLOC_MINBITS); - Assert(newsize >= oldsize); - delta = newsize - oldsize; - if (freespace >= delta) - { - /* Yes, so just enlarge the chunk. */ - block->freeptr += delta; - chunk->size += delta; -#ifdef MEMORY_CONTEXT_CHECKING - chunk->requested_size = size; - /* set mark to catch clobber of "unused" space */ - if (size < chunk->size) - ((char *) pointer)[size] = 0x7E; -#endif - return pointer; - } - } - } - - /* Normal small-chunk case: just do it by brute force. */ - - /* allocate new chunk */ - newPointer = AllocSetAlloc((MemoryContext) set, size); - - /* transfer existing data (certain to fit) */ - memcpy(newPointer, pointer, oldsize); - - /* free old chunk */ - AllocSetFree((MemoryContext) set, pointer); - - return newPointer; - } -} - -/* - * AllocSetStats - * Displays stats about memory consumption of an allocset. - */ -static void -AllocSetStats(MemoryContext context) -{ - AllocSet set = (AllocSet) context; - long nblocks = 0; - long nchunks = 0; - long totalspace = 0; - long freespace = 0; - AllocBlock block; - AllocChunk chunk; - int fidx; - - for (block = set->blocks; block != NULL; block = block->next) - { - nblocks++; - totalspace += block->endptr - ((char *) block); - freespace += block->endptr - block->freeptr; - } - for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++) - { - for (chunk = set->freelist[fidx]; chunk != NULL; - chunk = (AllocChunk) chunk->aset) - { - nchunks++; - freespace += chunk->size + ALLOC_CHUNKHDRSZ; - } - } - fprintf(stderr, - "%s: %ld total in %ld blocks; %ld free (%ld chunks); %ld used\n", - set->header.name, totalspace, nblocks, freespace, nchunks, - totalspace - freespace); -} - - -#ifdef MEMORY_CONTEXT_CHECKING - -/* - * AllocSetCheck - * Walk through chunks and check consistency of memory. - * - * NOTE: report errors as WARNING, *not* ERROR or FATAL. Otherwise you'll - * find yourself in an infinite loop when trouble occurs, because this - * routine will be entered again when elog cleanup tries to release memory! - */ -static void -AllocSetCheck(MemoryContext context) -{ - AllocSet set = (AllocSet) context; - char *name = set->header.name; - AllocBlock block; - - for (block = set->blocks; block != NULL; block = block->next) - { - char *bpoz = ((char *) block) + ALLOC_BLOCKHDRSZ; - long blk_used = block->freeptr - bpoz; - long blk_data = 0; - long nchunks = 0; - - /* - * Empty block - empty can be keeper-block only - */ - if (!blk_used) - { - if (set->keeper != block) - elog(WARNING, "AllocSetCheck: %s: empty block %p", - name, block); - } - - /* - * Chunk walker - */ - while (bpoz < block->freeptr) - { - AllocChunk chunk = (AllocChunk) bpoz; - Size chsize, - dsize; - char *chdata_end; - - chsize = chunk->size; /* aligned chunk size */ - dsize = chunk->requested_size; /* real data */ - chdata_end = ((char *) chunk) + (ALLOC_CHUNKHDRSZ + dsize); - - /* - * Check chunk size - */ - if (dsize > chsize) - elog(WARNING, "AllocSetCheck: %s: req size > alloc size for chunk %p in block %p", - name, chunk, block); - if (chsize < (1 << ALLOC_MINBITS)) - elog(WARNING, "AllocSetCheck: %s: bad size %lu for chunk %p in block %p", - name, (unsigned long) chsize, chunk, block); - - /* single-chunk block? */ - if (chsize > ALLOC_CHUNK_LIMIT && - chsize + ALLOC_CHUNKHDRSZ != blk_used) - elog(WARNING, "AllocSetCheck: %s: bad single-chunk %p in block %p", - name, chunk, block); - - /* - * If chunk is allocated, check for correct aset pointer. (If - * it's free, the aset is the freelist pointer, which we can't - * check as easily...) - */ - if (dsize > 0 && chunk->aset != (void *) set) - elog(WARNING, "AllocSetCheck: %s: bogus aset link in block %p, chunk %p", - name, block, chunk); - - /* - * Check for overwrite of "unallocated" space in chunk - */ - if (dsize > 0 && dsize < chsize && *chdata_end != 0x7E) - elog(WARNING, "AllocSetCheck: %s: detected write past chunk end in block %p, chunk %p", - name, block, chunk); - - blk_data += chsize; - nchunks++; - - bpoz += ALLOC_CHUNKHDRSZ + chsize; - } - - if ((blk_data + (nchunks * ALLOC_CHUNKHDRSZ)) != blk_used) - elog(WARNING, "AllocSetCheck: %s: found inconsistent memory block %p", - name, block); - } -} - -#endif /* MEMORY_CONTEXT_CHECKING */ |