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 + -
显示快捷键?