aset.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,125 行 · 第 1/3 页
C
1,125 行
* 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 onto any "keeper" block specified for the set. In this way, * we don't thrash malloc() when a context is repeatedly reset after small * allocations, which is typical behavior for per-tuple contexts. */static voidAllocSetReset(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 voidAllocSetDelete(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); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %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 { /* * Use first power of 2 that is larger than previous block, * but not more than the allowed limit. (We don't simply double * the prior block size, because in some cases this could be a * funny size, eg if very first allocation was for an odd-sized * large chunk.) */ Size pblksize = set->blocks->endptr - ((char *) set->blocks); blksize = set->initBlockSize; while (blksize <= pblksize) blksize <<= 1; if (blksize > set->maxBlockSize) blksize = set->maxBlockSize; } /* * If initBlockSize is less than ALLOC_CHUNK_LIMIT, we could need * more space... but try to keep it a power of 2. */ required_size = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; while (blksize < required_size) blksize <<= 1; /* 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); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %lu.", (unsigned long) size))); } block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; /* * If this is the first block of the set, make it the "keeper" * block. Formerly, a keeper block could only be created during * context creation, but allowing it to happen here lets us have * fast reset cycling even for contexts created with * minContextSize = 0; that way we don't have to force space to be * allocated in contexts that might never need any space. Don't * mark an oversize block as a keeper, however. */ if (set->blocks == NULL && blksize == set->initBlockSize) { Assert(set->keeper == NULL); set->keeper = block; } 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. */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?