GenerationBlock *block; /* current (most recently allocated) block */
GenerationBlock *freeblock; /* pointer to an empty block that's being
* recycled, or NULL if there's no such block. */
dlist_head blocks; /* list of blocks */
} GenerationContext;
/* *GenerationBlock *GenerationBlockistheunitofmemorythatisobtainedbygeneration.c *frommalloc().ItcontainszeroormoreMemoryChunks,whicharethe *unitsrequestedbypalloc()andfreedbypfree().MemoryChunkscannot *bereturnedtomalloc()individually,insteadpfree()updatesthefree *counteroftheblockandwhenallchunksinablockarefreethewhole *blockcanbereturnedtomalloc(). * *GenerationBlockistheheaderdataforablock---theusablespace *withintheblockbeginsatthenextalignmentboundary.
*/ struct GenerationBlock
{
dlist_node node; /* doubly-linked list of blocks */
GenerationContext *context; /* pointer back to the owning context */
Size blksize; /* allocated size of this block */ int nchunks; /* number of chunks in the block */ int nfree; /* number of free chunks */ char *freeptr; /* start of free space in this block */ char *endptr; /* end of space in this block */
};
/* ensure MemoryChunk's size is properly maxaligned */
StaticAssertDecl(Generation_CHUNKHDRSZ == MAXALIGN(Generation_CHUNKHDRSZ), "sizeof(MemoryChunk) is not maxaligned");
/* Fill in the initial block's block header */
block = KeeperBlock(set); /* determine the block size and initialize it */
firstBlockSize = allocSize - MAXALIGN(sizeof(GenerationContext));
GenerationBlockInit(set, block, firstBlockSize);
/* add it to the doubly-linked list of blocks */
dlist_push_head(&set->blocks, &block->node);
/* use it as the current allocation block */
set->block = block;
/* No free block, yet */
set->freeblock = NULL;
/* Fill in GenerationContext-specific header fields */
set->initBlockSize = (uint32) initBlockSize;
set->maxBlockSize = (uint32) maxBlockSize;
set->nextBlockSize = (uint32) initBlockSize;
/* Finally, do the type-independent part of context creation */
MemoryContextCreate((MemoryContext) set,
T_GenerationContext,
MCTX_GENERATION_ID,
parent,
name);
/* Ensure there is only 1 item in the dlist */
Assert(!dlist_is_empty(&set->blocks));
Assert(!dlist_has_next(&set->blocks, dlist_head_node(&set->blocks)));
}
/* *GenerationDelete *Freeallmemorywhichisallocatedinthegivencontext.
*/ void
GenerationDelete(MemoryContext context)
{ /* Reset to release all releasable GenerationBlocks */
GenerationReset(context); /* And free the context header and keeper block */
free(context);
}
/* mark the MemoryChunk as externally managed */
MemoryChunkSetHdrMaskExternal(chunk, MCTX_GENERATION_ID);
#ifdef MEMORY_CONTEXT_CHECKING
chunk->requested_size = size; /* set mark to catch clobber of "unused" space */
Assert(size < chunk_size);
set_sentinel(MemoryChunkGetPointer(chunk), size); #endif #ifdef RANDOMIZE_ALLOCATED_MEMORY /* fill the allocated space with junk */
randomize_mem((char *) MemoryChunkGetPointer(chunk), size); #endif
/* add the block to the list of allocated blocks */
dlist_push_head(&set->blocks, &block->node);
/* Ensure any padding bytes are marked NOACCESS. */
VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
chunk_size - size);
/* Disallow access to the chunk header. */
VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
/* validate we've been given a block with enough free space */
Assert(block != NULL);
Assert((block->endptr - block->freeptr) >=
Generation_CHUNKHDRSZ + chunk_size);
/* Prepare to initialize the chunk header. */
VALGRIND_MAKE_MEM_UNDEFINED(chunk, Generation_CHUNKHDRSZ);
MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_GENERATION_ID); #ifdef MEMORY_CONTEXT_CHECKING
chunk->requested_size = size; /* set mark to catch clobber of "unused" space */
Assert(size < chunk_size);
set_sentinel(MemoryChunkGetPointer(chunk), size); #endif #ifdef RANDOMIZE_ALLOCATED_MEMORY /* fill the allocated space with junk */
randomize_mem((char *) MemoryChunkGetPointer(chunk), size); #endif
/* Ensure any padding bytes are marked NOACCESS. */
VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
chunk_size - size);
/* Disallow access to the chunk header. */
VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
if (unlikely(GenerationBlockFreeBytes(block) < required_size))
{
GenerationBlock *freeblock = set->freeblock;
/* freeblock, if set, must be empty */
Assert(freeblock == NULL || GenerationBlockIsEmpty(freeblock));
/* check if we have a freeblock and if it's big enough */ if (freeblock != NULL &&
GenerationBlockFreeBytes(freeblock) >= required_size)
{ /* make the freeblock the current block */
set->freeblock = NULL;
set->block = freeblock;
/* *GenerationBlockFree *Remove'block'from'set'andreleasethememoryconsumedbyit.
*/ staticinlinevoid
GenerationBlockFree(GenerationContext *set, GenerationBlock *block)
{ /* Make sure nobody tries to free the keeper block */
Assert(!IsKeeperBlock(set, block)); /* We shouldn't be freeing the freeblock either */
Assert(block != set->freeblock);
/* release the block from the list of blocks */
dlist_delete(&block->node);
#ifdef MEMORY_CONTEXT_CHECKING /* See comments in AllocSetFree about uses of ERROR and WARNING here */ /* Test for previously-freed chunk */ if (unlikely(chunk->requested_size == InvalidAllocSize))
elog(ERROR, "detected double pfree in %s %p",
((MemoryContext) block->context)->name, chunk); /* Test for someone scribbling on unused space in chunk */
Assert(chunk->requested_size < chunksize); if (!sentinel_ok(pointer, chunk->requested_size))
elog(WARNING, "detected write past chunk end in %s %p",
((MemoryContext) block->context)->name, chunk); #endif
#ifdef MEMORY_CONTEXT_CHECKING /* See comments in AllocSetFree about uses of ERROR and WARNING here */ /* Test for previously-freed chunk */ if (unlikely(chunk->requested_size == InvalidAllocSize))
elog(ERROR, "detected realloc of freed chunk in %s %p",
((MemoryContext) set)->name, chunk); /* Test for someone scribbling on unused space in chunk */
Assert(chunk->requested_size < oldsize); if (!sentinel_ok(pointer, chunk->requested_size))
elog(WARNING, "detected write past chunk end in %s %p",
((MemoryContext) set)->name, chunk); #endif
/* *Maybetheallocatedareaalreadybigenough.(Inparticular,wealways *fallouthereiftherequestedsizeisadecrease.) * *Thismemorycontextdoesnotusepower-of-2chunksizingandinstead *carvesthechunkstobeassmallaspossible,somostrepalloc()calls *willendupinthepalloc/memcpy/pfreebranch. * *XXXPerhapsweshouldannotatethisconditionwithunlikely()?
*/ #ifdef MEMORY_CONTEXT_CHECKING /* With MEMORY_CONTEXT_CHECKING, we need an extra byte for the sentinel */ if (oldsize > size) #else if (oldsize >= size) #endif
{ #ifdef MEMORY_CONTEXT_CHECKING
Size oldrequest = chunk->requested_size;
#ifdef RANDOMIZE_ALLOCATED_MEMORY /* We can only fill the extra space if we know the prior request */ if (size > oldrequest)
randomize_mem((char *) pointer + oldrequest,
size - oldrequest); #endif
/* Disallow access to the chunk header. */
VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
return pointer;
}
/* allocate new chunk (this also checks size is valid) */
newPointer = GenerationAlloc((MemoryContext) set, size, flags);
/* leave immediately if request was not completed */ if (newPointer == NULL)
{ /* Disallow access to the chunk header. */
VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ); return MemoryContextAllocationFailure((MemoryContext) set, size, flags);
}
/* walk all blocks in this context */
dlist_foreach(iter, &gen->blocks)
{
GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur); int nfree,
nchunks; char *ptr; bool has_external_chunk = false;
total_allocated += block->blksize;
/* *nfree>nchunksissurelywrong.Equalityisallowedastheblock *mightcompletelyemptyifit'sthefreeblock.
*/ if (block->nfree > block->nchunks)
elog(WARNING, "problem in Generation %s: number of free chunks %d in block %p exceeds %d allocated",
name, block->nfree, block, block->nchunks);
/* check block belongs to the correct context */ if (block->context != gen)
elog(WARNING, "problem in Generation %s: bogus context link in block %p",
name, block);
/* Now walk through the chunks and count them. */
nfree = 0;
nchunks = 0;
ptr = ((char *) block) + Generation_BLOCKHDRSZ;
/* move to the next chunk */
ptr += (chunksize + Generation_CHUNKHDRSZ);
nchunks += 1;
/* chunks have both block and context pointers, so check both */ if (chunkblock != block)
elog(WARNING, "problem in Generation %s: bogus block link in block %p, chunk %p",
name, block, chunk);
/* is chunk allocated? */ if (chunk->requested_size != InvalidAllocSize)
{ /* now make sure the chunk size is correct */ if (chunksize < chunk->requested_size ||
chunksize != MAXALIGN(chunksize))
elog(WARNING, "problem in Generation %s: bogus chunk size in block %p, chunk %p",
name, block, chunk);
/* check sentinel */
Assert(chunk->requested_size < chunksize); if (!sentinel_ok(chunk, Generation_CHUNKHDRSZ + chunk->requested_size))
elog(WARNING, "problem in Generation %s: detected write past chunk end in block %p, chunk %p",
name, block, chunk);
} else
nfree += 1;
/* if chunk is allocated, disallow access to the chunk header */ if (chunk->requested_size != InvalidAllocSize)
VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
}
/* *Makesurewegottheexpectednumberofallocatedandfreechunks *(astrackedintheblockheader).
*/ if (nchunks != block->nchunks)
elog(WARNING, "problem in Generation %s: number of allocated chunks %d in block %p does not match header %d",
name, nchunks, block, block->nchunks);
if (nfree != block->nfree)
elog(WARNING, "problem in Generation %s: number of free chunks %d in block %p does not match header %d",
name, nfree, block, block->nfree);
if (has_external_chunk && nchunks > 1)
elog(WARNING, "problem in Generation %s: external chunk on non-dedicated block %p",
name, block);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.