aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/mmgr/generation.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/mmgr/generation.c')
-rw-r--r--src/backend/utils/mmgr/generation.c360
1 files changed, 185 insertions, 175 deletions
diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c
index e530e272e05..b39894ec946 100644
--- a/src/backend/utils/mmgr/generation.c
+++ b/src/backend/utils/mmgr/generation.c
@@ -39,15 +39,16 @@
#include "port/pg_bitutils.h"
#include "utils/memdebug.h"
#include "utils/memutils.h"
+#include "utils/memutils_memorychunk.h"
+#include "utils/memutils_internal.h"
#define Generation_BLOCKHDRSZ MAXALIGN(sizeof(GenerationBlock))
-#define Generation_CHUNKHDRSZ sizeof(GenerationChunk)
+#define Generation_CHUNKHDRSZ sizeof(MemoryChunk)
#define Generation_CHUNK_FRACTION 8
typedef struct GenerationBlock GenerationBlock; /* forward reference */
-typedef struct GenerationChunk GenerationChunk;
typedef void *GenerationPointer;
@@ -77,11 +78,11 @@ typedef struct GenerationContext
/*
* GenerationBlock
* GenerationBlock is the unit of memory that is obtained by generation.c
- * from malloc(). It contains zero or more GenerationChunks, which are
- * the units requested by palloc() and freed by pfree(). GenerationChunks
- * cannot be returned to malloc() individually, instead pfree()
- * updates the free counter of the block and when all chunks in a block
- * are free the whole block can be returned to malloc().
+ * from malloc(). It contains zero or more MemoryChunks, which are the
+ * units requested by palloc() and freed by pfree(). MemoryChunks cannot
+ * be returned to malloc() individually, instead pfree() updates the free
+ * counter of the block and when all chunks in a block are free the whole
+ * block can be returned to malloc().
*
* GenerationBlock is the header data for a block --- the usable space
* within the block begins at the next alignment boundary.
@@ -89,6 +90,7 @@ typedef struct GenerationContext
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 */
@@ -97,104 +99,36 @@ struct GenerationBlock
};
/*
- * GenerationChunk
- * The prefix of each piece of memory in a GenerationBlock
- *
- * Note: to meet the memory context APIs, the payload area of the chunk must
- * be maxaligned, and the "context" link must be immediately adjacent to the
- * payload area (cf. GetMemoryChunkContext). We simplify matters for this
- * module by requiring sizeof(GenerationChunk) to be maxaligned, and then
- * we can ensure things work by adding any required alignment padding before
- * the pointer fields. There is a static assertion below that the alignment
- * is done correctly.
- */
-struct GenerationChunk
-{
- /* 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;
-
-#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T * 2 + SIZEOF_VOID_P * 2)
-#else
-#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T + SIZEOF_VOID_P * 2)
-#endif /* MEMORY_CONTEXT_CHECKING */
-
- /* ensure proper alignment by adding padding if needed */
-#if (GENERATIONCHUNK_RAWSIZE % MAXIMUM_ALIGNOF) != 0
- char padding[MAXIMUM_ALIGNOF - GENERATIONCHUNK_RAWSIZE % MAXIMUM_ALIGNOF];
-#endif
-
- GenerationBlock *block; /* block owning this chunk */
- GenerationContext *context; /* owning context, or NULL if freed chunk */
- /* there must not be any padding to reach a MAXALIGN boundary here! */
-};
-
-/*
- * Only the "context" field should be accessed outside this module.
+ * Only the "hdrmask" field should be accessed outside this module.
* We keep the rest of an allocated chunk's header marked NOACCESS when using
* valgrind. But note that freed chunk headers are kept accessible, for
* simplicity.
*/
-#define GENERATIONCHUNK_PRIVATE_LEN offsetof(GenerationChunk, context)
-
+#define GENERATIONCHUNK_PRIVATE_LEN offsetof(MemoryChunk, hdrmask)
/*
* GenerationIsValid
* True iff set is valid allocation set.
*/
#define GenerationIsValid(set) PointerIsValid(set)
-#define GenerationPointerGetChunk(ptr) \
- ((GenerationChunk *)(((char *)(ptr)) - Generation_CHUNKHDRSZ))
-#define GenerationChunkGetPointer(chk) \
- ((GenerationPointer *)(((char *)(chk)) + Generation_CHUNKHDRSZ))
+/*
+ * We always store external chunks on a dedicated block. This makes fetching
+ * the block from an external chunk easy since it's always the first and only
+ * chunk on the block.
+ */
+#define ExternalChunkGetBlock(chunk) \
+ (GenerationBlock *) ((char *) chunk - Generation_BLOCKHDRSZ)
/* Inlined helper functions */
-static inline void GenerationBlockInit(GenerationBlock *block, Size blksize);
+static inline void GenerationBlockInit(GenerationContext *context,
+ GenerationBlock *block,
+ Size blksize);
static inline bool GenerationBlockIsEmpty(GenerationBlock *block);
static inline void GenerationBlockMarkEmpty(GenerationBlock *block);
static inline Size GenerationBlockFreeBytes(GenerationBlock *block);
static inline void GenerationBlockFree(GenerationContext *set,
GenerationBlock *block);
-/*
- * These functions implement the MemoryContext API for Generation contexts.
- */
-static void *GenerationAlloc(MemoryContext context, Size size);
-static void GenerationFree(MemoryContext context, void *pointer);
-static void *GenerationRealloc(MemoryContext context, void *pointer, Size size);
-static void GenerationReset(MemoryContext context);
-static void GenerationDelete(MemoryContext context);
-static Size GenerationGetChunkSpace(MemoryContext context, void *pointer);
-static bool GenerationIsEmpty(MemoryContext context);
-static void GenerationStats(MemoryContext context,
- MemoryStatsPrintFunc printfunc, void *passthru,
- MemoryContextCounters *totals,
- bool print_to_stderr);
-
-#ifdef MEMORY_CONTEXT_CHECKING
-static void GenerationCheck(MemoryContext context);
-#endif
-
-/*
- * This is the virtual function table for Generation contexts.
- */
-static const MemoryContextMethods GenerationMethods = {
- GenerationAlloc,
- GenerationFree,
- GenerationRealloc,
- GenerationReset,
- GenerationDelete,
- GenerationGetChunkSpace,
- GenerationIsEmpty,
- GenerationStats
-#ifdef MEMORY_CONTEXT_CHECKING
- ,GenerationCheck
-#endif
-};
-
/*
* Public routines
@@ -223,17 +157,20 @@ GenerationContextCreate(MemoryContext parent,
GenerationContext *set;
GenerationBlock *block;
- /* Assert we padded GenerationChunk properly */
+ /* ensure MemoryChunk's size is properly maxaligned */
StaticAssertStmt(Generation_CHUNKHDRSZ == MAXALIGN(Generation_CHUNKHDRSZ),
- "sizeof(GenerationChunk) is not maxaligned");
- StaticAssertStmt(offsetof(GenerationChunk, context) + sizeof(MemoryContext) ==
- Generation_CHUNKHDRSZ,
- "padding calculation in GenerationChunk is wrong");
+ "sizeof(MemoryChunk) is not maxaligned");
/*
* First, validate allocation parameters. Asserts seem sufficient because
* nobody varies their parameters at runtime. We somewhat arbitrarily
- * enforce a minimum 1K block size.
+ * enforce a minimum 1K block size. We restrict the maximum block size to
+ * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
+ * regards to addressing the offset between the chunk and the block that
+ * the chunk is stored on. We would be unable to store the offset between
+ * the chunk and block for any chunks that were beyond
+ * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
+ * larger than this.
*/
Assert(initBlockSize == MAXALIGN(initBlockSize) &&
initBlockSize >= 1024);
@@ -244,6 +181,7 @@ GenerationContextCreate(MemoryContext parent,
(minContextSize == MAXALIGN(minContextSize) &&
minContextSize >= 1024 &&
minContextSize <= maxBlockSize));
+ Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
/* Determine size of initial block */
allocSize = MAXALIGN(sizeof(GenerationContext)) +
@@ -278,7 +216,7 @@ GenerationContextCreate(MemoryContext parent,
block = (GenerationBlock *) (((char *) set) + MAXALIGN(sizeof(GenerationContext)));
/* determine the block size and initialize it */
firstBlockSize = allocSize - MAXALIGN(sizeof(GenerationContext));
- GenerationBlockInit(block, firstBlockSize);
+ GenerationBlockInit(set, block, firstBlockSize);
/* add it to the doubly-linked list of blocks */
dlist_push_head(&set->blocks, &block->node);
@@ -300,9 +238,14 @@ GenerationContextCreate(MemoryContext parent,
/*
* Compute the allocation chunk size limit for this context.
*
- * Follows similar ideas as AllocSet, see aset.c for details ...
+ * Limit the maximum size a non-dedicated chunk can be so that we can fit
+ * at least Generation_CHUNK_FRACTION of chunks this big onto the maximum
+ * sized block. We must further limit this value so that it's no more
+ * than MEMORYCHUNK_MAX_VALUE. We're unable to have non-external chunks
+ * larger than that value as we store the chunk size in the MemoryChunk
+ * 'value' field in the call to MemoryChunkSetHdrMask().
*/
- set->allocChunkLimit = maxBlockSize;
+ set->allocChunkLimit = Min(maxBlockSize, MEMORYCHUNK_MAX_VALUE);
while ((Size) (set->allocChunkLimit + Generation_CHUNKHDRSZ) >
(Size) ((Size) (maxBlockSize - Generation_BLOCKHDRSZ) / Generation_CHUNK_FRACTION))
set->allocChunkLimit >>= 1;
@@ -310,7 +253,7 @@ GenerationContextCreate(MemoryContext parent,
/* Finally, do the type-independent part of context creation */
MemoryContextCreate((MemoryContext) set,
T_GenerationContext,
- &GenerationMethods,
+ MCTX_GENERATION_ID,
parent,
name);
@@ -326,7 +269,7 @@ GenerationContextCreate(MemoryContext parent,
* The code simply frees all the blocks in the context - we don't keep any
* keeper blocks or anything like that.
*/
-static void
+void
GenerationReset(MemoryContext context)
{
GenerationContext *set = (GenerationContext *) context;
@@ -371,7 +314,7 @@ GenerationReset(MemoryContext context)
* GenerationDelete
* Free all memory which is allocated in the given context.
*/
-static void
+void
GenerationDelete(MemoryContext context)
{
/* Reset to release all releasable GenerationBlocks */
@@ -393,12 +336,12 @@ GenerationDelete(MemoryContext context)
* is marked, as mcxt.c will set it to UNDEFINED. In some paths we will
* return space that is marked NOACCESS - GenerationRealloc has to beware!
*/
-static void *
+void *
GenerationAlloc(MemoryContext context, Size size)
{
GenerationContext *set = (GenerationContext *) context;
GenerationBlock *block;
- GenerationChunk *chunk;
+ MemoryChunk *chunk;
Size chunk_size = MAXALIGN(size);
Size required_size = chunk_size + Generation_CHUNKHDRSZ;
@@ -414,6 +357,7 @@ GenerationAlloc(MemoryContext context, Size size)
context->mem_allocated += blksize;
/* block with a single (used) chunk */
+ block->context = set;
block->blksize = blksize;
block->nchunks = 1;
block->nfree = 0;
@@ -421,33 +365,33 @@ GenerationAlloc(MemoryContext context, Size size)
/* the block is completely full */
block->freeptr = block->endptr = ((char *) block) + blksize;
- chunk = (GenerationChunk *) (((char *) block) + Generation_BLOCKHDRSZ);
- chunk->block = block;
- chunk->context = set;
- chunk->size = chunk_size;
+ chunk = (MemoryChunk *) (((char *) block) + Generation_BLOCKHDRSZ);
+
+ /* 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 */
if (size < chunk_size)
- set_sentinel(GenerationChunkGetPointer(chunk), size);
+ set_sentinel(MemoryChunkGetPointer(chunk), size);
#endif
#ifdef RANDOMIZE_ALLOCATED_MEMORY
/* fill the allocated space with junk */
- randomize_mem((char *) GenerationChunkGetPointer(chunk), size);
+ 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 *) GenerationChunkGetPointer(chunk) + size,
+ VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
chunk_size - size);
/* Disallow external access to private part of chunk header. */
VALGRIND_MAKE_MEM_NOACCESS(chunk, GENERATIONCHUNK_PRIVATE_LEN);
- return GenerationChunkGetPointer(chunk);
+ return MemoryChunkGetPointer(chunk);
}
/*
@@ -516,7 +460,7 @@ GenerationAlloc(MemoryContext context, Size size)
context->mem_allocated += blksize;
/* initialize the new block */
- GenerationBlockInit(block, blksize);
+ GenerationBlockInit(set, block, blksize);
/* add it to the doubly-linked list of blocks */
dlist_push_head(&set->blocks, &block->node);
@@ -533,7 +477,7 @@ GenerationAlloc(MemoryContext context, Size size)
Assert(block != NULL);
Assert((block->endptr - block->freeptr) >= Generation_CHUNKHDRSZ + chunk_size);
- chunk = (GenerationChunk *) block->freeptr;
+ chunk = (MemoryChunk *) block->freeptr;
/* Prepare to initialize the chunk header. */
VALGRIND_MAKE_MEM_UNDEFINED(chunk, Generation_CHUNKHDRSZ);
@@ -543,29 +487,26 @@ GenerationAlloc(MemoryContext context, Size size)
Assert(block->freeptr <= block->endptr);
- chunk->block = block;
- chunk->context = set;
- chunk->size = chunk_size;
-
+ MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_GENERATION_ID);
#ifdef MEMORY_CONTEXT_CHECKING
chunk->requested_size = size;
/* set mark to catch clobber of "unused" space */
- if (size < chunk->size)
- set_sentinel(GenerationChunkGetPointer(chunk), size);
+ if (size < chunk_size)
+ set_sentinel(MemoryChunkGetPointer(chunk), size);
#endif
#ifdef RANDOMIZE_ALLOCATED_MEMORY
/* fill the allocated space with junk */
- randomize_mem((char *) GenerationChunkGetPointer(chunk), size);
+ randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
#endif
/* Ensure any padding bytes are marked NOACCESS. */
- VALGRIND_MAKE_MEM_NOACCESS((char *) GenerationChunkGetPointer(chunk) + size,
+ VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
chunk_size - size);
/* Disallow external access to private part of chunk header. */
VALGRIND_MAKE_MEM_NOACCESS(chunk, GENERATIONCHUNK_PRIVATE_LEN);
- return GenerationChunkGetPointer(chunk);
+ return MemoryChunkGetPointer(chunk);
}
/*
@@ -574,8 +515,10 @@ GenerationAlloc(MemoryContext context, Size size)
* mem_allocated field.
*/
static inline void
-GenerationBlockInit(GenerationBlock *block, Size blksize)
+GenerationBlockInit(GenerationContext *context, GenerationBlock *block,
+ Size blksize)
{
+ block->context = context;
block->blksize = blksize;
block->nchunks = 0;
block->nfree = 0;
@@ -661,36 +604,49 @@ GenerationBlockFree(GenerationContext *set, GenerationBlock *block)
* Update number of chunks in the block, and if all chunks in the block
* are now free then discard the block.
*/
-static void
-GenerationFree(MemoryContext context, void *pointer)
+void
+GenerationFree(void *pointer)
{
- GenerationContext *set = (GenerationContext *) context;
- GenerationChunk *chunk = GenerationPointerGetChunk(pointer);
+ MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
GenerationBlock *block;
+ GenerationContext *set;
+#if defined(MEMORY_CONTEXT_CHECKING) || defined(CLOBBER_FREED_MEMORY)
+ Size chunksize;
+#endif
+
+ if (MemoryChunkIsExternal(chunk))
+ {
+ block = ExternalChunkGetBlock(chunk);
+#if defined(MEMORY_CONTEXT_CHECKING) || defined(CLOBBER_FREED_MEMORY)
+ chunksize = block->endptr - (char *) pointer;
+#endif
+ }
+ else
+ {
+ block = MemoryChunkGetBlock(chunk);
+#if defined(MEMORY_CONTEXT_CHECKING) || defined(CLOBBER_FREED_MEMORY)
+ chunksize = MemoryChunkGetValue(chunk);
+#endif
+ }
/* Allow access to private part of chunk header. */
VALGRIND_MAKE_MEM_DEFINED(chunk, GENERATIONCHUNK_PRIVATE_LEN);
- block = chunk->block;
-
#ifdef MEMORY_CONTEXT_CHECKING
/* Test for someone scribbling on unused space in chunk */
- if (chunk->requested_size < chunk->size)
+ if (chunk->requested_size < chunksize)
if (!sentinel_ok(pointer, chunk->requested_size))
elog(WARNING, "detected write past chunk end in %s %p",
- ((MemoryContext) set)->name, chunk);
+ ((MemoryContext) block->context)->name, chunk);
#endif
#ifdef CLOBBER_FREED_MEMORY
- wipe_mem(pointer, chunk->size);
+ wipe_mem(pointer, chunksize);
#endif
- /* Reset context to NULL in freed chunks */
- chunk->context = NULL;
-
#ifdef MEMORY_CONTEXT_CHECKING
- /* Reset requested_size to 0 in freed chunks */
- chunk->requested_size = 0;
+ /* Reset requested_size to InvalidAllocSize in freed chunks */
+ chunk->requested_size = InvalidAllocSize;
#endif
block->nfree += 1;
@@ -702,6 +658,8 @@ GenerationFree(MemoryContext context, void *pointer)
if (block->nfree < block->nchunks)
return;
+ set = block->context;
+
/* Don't try to free the keeper block, just mark it empty */
if (block == set->keeper)
{
@@ -732,7 +690,7 @@ GenerationFree(MemoryContext context, void *pointer)
*/
dlist_delete(&block->node);
- context->mem_allocated -= block->blksize;
+ set->header.mem_allocated -= block->blksize;
free(block);
}
@@ -742,18 +700,30 @@ GenerationFree(MemoryContext context, void *pointer)
* and discard the old one. The only exception is when the new size fits
* into the old chunk - in that case we just update chunk header.
*/
-static void *
-GenerationRealloc(MemoryContext context, void *pointer, Size size)
+void *
+GenerationRealloc(void *pointer, Size size)
{
- GenerationContext *set = (GenerationContext *) context;
- GenerationChunk *chunk = GenerationPointerGetChunk(pointer);
+ MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
+ GenerationContext *set;
+ GenerationBlock *block;
GenerationPointer newPointer;
Size oldsize;
/* Allow access to private part of chunk header. */
VALGRIND_MAKE_MEM_DEFINED(chunk, GENERATIONCHUNK_PRIVATE_LEN);
- oldsize = chunk->size;
+ if (MemoryChunkIsExternal(chunk))
+ {
+ block = ExternalChunkGetBlock(chunk);
+ oldsize = block->endptr - (char *) pointer;
+ }
+ else
+ {
+ block = MemoryChunkGetBlock(chunk);
+ oldsize = MemoryChunkGetValue(chunk);
+ }
+
+ set = block->context;
#ifdef MEMORY_CONTEXT_CHECKING
/* Test for someone scribbling on unused space in chunk */
@@ -848,33 +818,57 @@ GenerationRealloc(MemoryContext context, void *pointer, Size size)
memcpy(newPointer, pointer, oldsize);
/* free old chunk */
- GenerationFree((MemoryContext) set, pointer);
+ GenerationFree(pointer);
return newPointer;
}
/*
+ * GenerationGetChunkContext
+ * Return the MemoryContext that 'pointer' belongs to.
+ */
+MemoryContext
+GenerationGetChunkContext(void *pointer)
+{
+ MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
+ GenerationBlock *block;
+
+ if (MemoryChunkIsExternal(chunk))
+ block = ExternalChunkGetBlock(chunk);
+ else
+ block = (GenerationBlock *) MemoryChunkGetBlock(chunk);
+
+ return &block->context->header;
+}
+
+/*
* GenerationGetChunkSpace
* Given a currently-allocated chunk, determine the total space
* it occupies (including all memory-allocation overhead).
*/
-static Size
-GenerationGetChunkSpace(MemoryContext context, void *pointer)
+Size
+GenerationGetChunkSpace(void *pointer)
{
- GenerationChunk *chunk = GenerationPointerGetChunk(pointer);
- Size result;
+ MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
+ Size chunksize;
- VALGRIND_MAKE_MEM_DEFINED(chunk, GENERATIONCHUNK_PRIVATE_LEN);
- result = chunk->size + Generation_CHUNKHDRSZ;
- VALGRIND_MAKE_MEM_NOACCESS(chunk, GENERATIONCHUNK_PRIVATE_LEN);
- return result;
+ if (MemoryChunkIsExternal(chunk))
+ {
+ GenerationBlock *block = ExternalChunkGetBlock(chunk);
+
+ chunksize = block->endptr - (char *) pointer;
+ }
+ else
+ chunksize = MemoryChunkGetValue(chunk);
+
+ return Generation_CHUNKHDRSZ + chunksize;
}
/*
* GenerationIsEmpty
* Is a GenerationContext empty of any allocated space?
*/
-static bool
+bool
GenerationIsEmpty(MemoryContext context)
{
GenerationContext *set = (GenerationContext *) context;
@@ -903,7 +897,7 @@ GenerationIsEmpty(MemoryContext context)
* XXX freespace only accounts for empty space at the end of the block, not
* space of freed chunks (which is unknown).
*/
-static void
+void
GenerationStats(MemoryContext context,
MemoryStatsPrintFunc printfunc, void *passthru,
MemoryContextCounters *totals, bool print_to_stderr)
@@ -961,7 +955,7 @@ GenerationStats(MemoryContext context,
* 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
+void
GenerationCheck(MemoryContext context)
{
GenerationContext *gen = (GenerationContext *) context;
@@ -976,6 +970,7 @@ GenerationCheck(MemoryContext context)
int nfree,
nchunks;
char *ptr;
+ bool has_external_chunk = false;
total_allocated += block->blksize;
@@ -987,6 +982,11 @@ GenerationCheck(MemoryContext context)
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;
@@ -994,42 +994,47 @@ GenerationCheck(MemoryContext context)
while (ptr < block->freeptr)
{
- GenerationChunk *chunk = (GenerationChunk *) ptr;
+ MemoryChunk *chunk = (MemoryChunk *) ptr;
+ GenerationBlock *chunkblock;
+ Size chunksize;
/* Allow access to private part of chunk header. */
VALGRIND_MAKE_MEM_DEFINED(chunk, GENERATIONCHUNK_PRIVATE_LEN);
+ if (MemoryChunkIsExternal(chunk))
+ {
+ chunkblock = ExternalChunkGetBlock(chunk);
+ chunksize = block->endptr - (char *) MemoryChunkGetPointer(chunk);
+ has_external_chunk = true;
+ }
+ else
+ {
+ chunkblock = MemoryChunkGetBlock(chunk);
+ chunksize = MemoryChunkGetValue(chunk);
+ }
+
/* move to the next chunk */
- ptr += (chunk->size + Generation_CHUNKHDRSZ);
+ ptr += (chunksize + Generation_CHUNKHDRSZ);
nchunks += 1;
/* chunks have both block and context pointers, so check both */
- if (chunk->block != block)
+ if (chunkblock != block)
elog(WARNING, "problem in Generation %s: bogus block link in block %p, chunk %p",
name, block, chunk);
- /*
- * Check for valid context pointer. Note this is an incomplete
- * test, since palloc(0) produces an allocated chunk with
- * requested_size == 0.
- */
- if ((chunk->requested_size > 0 && chunk->context != gen) ||
- (chunk->context != gen && chunk->context != NULL))
- elog(WARNING, "problem in Generation %s: bogus context link in block %p, chunk %p",
- name, block, chunk);
-
- /* now make sure the chunk size is correct */
- if (chunk->size < chunk->requested_size ||
- chunk->size != MAXALIGN(chunk->size))
- elog(WARNING, "problem in Generation %s: bogus chunk size in block %p, chunk %p",
- name, block, chunk);
/* is chunk allocated? */
- if (chunk->context != NULL)
+ if (chunk->requested_size != InvalidAllocSize)
{
- /* check sentinel, but only in allocated blocks */
- if (chunk->requested_size < chunk->size &&
+ /* 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 */
+ if (chunk->requested_size < chunksize &&
!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);
@@ -1041,7 +1046,7 @@ GenerationCheck(MemoryContext context)
* If chunk is allocated, disallow external access to private part
* of chunk header.
*/
- if (chunk->context != NULL)
+ if (chunk->requested_size != InvalidAllocSize)
VALGRIND_MAKE_MEM_NOACCESS(chunk, GENERATIONCHUNK_PRIVATE_LEN);
}
@@ -1056,6 +1061,11 @@ GenerationCheck(MemoryContext context)
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);
+
}
Assert(total_allocated == context->mem_allocated);