* 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 AllocSetReset(MemoryContext context);
static void AllocSetDelete(MemoryContext context);
-static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer);
static bool AllocSetIsEmpty(MemoryContext context);
static void AllocSetStats(MemoryContext context, int level, bool print,
MemoryContextCounters *totals);
*/
static MemoryContextMethods AllocSetMethods = {
AllocSetAlloc,
- AllocSetFree,
- AllocSetRealloc,
AllocSetReset,
AllocSetDelete,
- AllocSetGetChunkSpace,
AllocSetIsEmpty,
AllocSetStats
#ifdef MEMORY_CONTEXT_CHECKING
* AllocSetFree
* Frees allocated memory; memory is removed from the set.
*/
-static void
-AllocSetFree(MemoryContext context, void *pointer)
+void
+AllocSetFree(void *pointer, chunk_unsigned context_id, chunk_unsigned size)
{
AllocSet set = (AllocSet) context;
AllocChunk chunk = AllocPointerGetChunk(pointer);
* (In principle, we could use VALGRIND_GET_VBITS() to rediscover the old
* request size.)
*/
-static void *
-AllocSetRealloc(MemoryContext context, void *pointer, Size size)
+void *
+AllocSetRealloc(void *pointer, chunk_unsigned context_id, chunk_unsigned size)
{
AllocSet set = (AllocSet) context;
AllocChunk chunk = AllocPointerGetChunk(pointer);
Size oldsize = chunk->size;
+ AssertNotInCriticalSection(&context->header);
+
#ifdef MEMORY_CONTEXT_CHECKING
VALGRIND_MAKE_MEM_DEFINED(&chunk->requested_size,
sizeof(chunk->requested_size));
* Given a currently-allocated chunk, determine the total space
* it occupies (including all memory-allocation overhead).
*/
-static Size
+Size
AllocSetGetChunkSpace(MemoryContext context, void *pointer)
{
AllocChunk chunk = AllocPointerGetChunk(pointer);
bool print, int max_children,
MemoryContextCounters *totals);
-/*
- * You should not do memory allocations within a critical section, because
- * an out-of-memory error will be escalated to a PANIC. To enforce that
- * rule, the allocation functions Assert that.
- */
-#define AssertNotInCriticalSection(context) \
- Assert(CritSectionCount == 0 || (context)->allowInCritSection)
-
/*****************************************************************************
* EXPORTED ROUTINES *
*****************************************************************************/
header = (StandardChunkHeader *)
((char *) pointer - STANDARDCHUNKHEADERSIZE);
- AssertArg(MemoryContextIsValid(header->context));
-
- return (*header->context->methods->get_chunk_space) (header->context,
- pointer);
+ switch (header->chunk_context_type)
+ {
+ case CHUNK_CONTEXT_TYPE_ASET:
+ return AllocSetGetChunkSpace(pointer,
+ header->chunk_context_id,
+ header->chunk_size);
+
+ default:
+ elog(ERROR, "unknown memory context type: %d",
+ header->chunk_context_type);
+ }
}
/*
header = (StandardChunkHeader *)
((char *) pointer - STANDARDCHUNKHEADERSIZE);
- AssertArg(MemoryContextIsValid(header->context));
+ switch (header->chunk_context_type)
+ {
+ case CHUNK_CONTEXT_TYPE_ASET:
+ return AllocSetGetChunkContext(pointer, header->chunk_context_id);
- return header->context;
+ default:
+ elog(ERROR, "unknown memory context type: %d",
+ header->chunk_context_type);
+ }
}
/*
MemoryContextContains(MemoryContext context, void *pointer)
{
StandardChunkHeader *header;
+ MemoryContext chunk_context;
/*
* Try to detect bogus pointers handed to us, poorly though we can.
header = (StandardChunkHeader *)
((char *) pointer - STANDARDCHUNKHEADERSIZE);
- return header->context == context;
+ switch (header->chunk_context_type)
+ {
+ case CHUNK_CONTEXT_TYPE_ASET:
+ if (!IsA(context, AllocSetContext))
+ return false;
+ chunk_context =
+ AllocSetGetChunkContext(pointer, header->chunk_context_id);
+
+ default:
+ chunk_context = NULL;
+ break;
+ }
+
+ return context == chunk_context;
}
/*--------------------
void
pfree(void *pointer)
{
- MemoryContext context;
+ StandardChunkHeader *header;
/*
* Try to detect bogus pointers handed to us, poorly though we can.
/*
* OK, it's probably safe to look at the chunk header.
*/
- context = ((StandardChunkHeader *)
- ((char *) pointer - STANDARDCHUNKHEADERSIZE))->context;
+ header = (StandardChunkHeader *)
+ ((char *) pointer - STANDARDCHUNKHEADERSIZE);
- AssertArg(MemoryContextIsValid(context));
+ switch (header->chunk_context_type)
+ {
+ case CHUNK_CONTEXT_TYPE_ASET:
+ return AllocSetFree(pointer,
+ header->chunk_context_id,
+ header->chunk_size);
+
+ default:
+ elog(WARNING, "can't free chunk of unknown type %d at %p",
+ header->chunk_context_type, pointer);
+ }
- (*context->methods->free_p) (context, pointer);
VALGRIND_MEMPOOL_FREE(context, pointer);
}
void *
repalloc(void *pointer, Size size)
{
- MemoryContext context;
+ StandardChunkHeader *header;
void *ret;
if (!AllocSizeIsValid(size))
/*
* OK, it's probably safe to look at the chunk header.
*/
- context = ((StandardChunkHeader *)
- ((char *) pointer - STANDARDCHUNKHEADERSIZE))->context;
+ header = (StandardChunkHeader *)
+ ((char *) pointer - STANDARDCHUNKHEADERSIZE);
AssertArg(MemoryContextIsValid(context));
AssertNotInCriticalSection(context);
/* isReset must be false already */
Assert(!context->isReset);
- ret = (*context->methods->realloc) (context, pointer, size);
+ switch (header->chunk_context_type)
+ {
+ case CHUNK_CONTEXT_TYPE_ASET:
+ ret = AllocSetRealloc(pointer,
+ header->chunk_context_id,
+ header->chunk_size);
+ break;
+
+ default:
+ elog(ERROR, "can't reallocate chunk of unknown type %d at %p",
+ header->chunk_context_type, pointer);
+ ret = NULL;
+ break;
+ }
+
if (ret == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
void *
repalloc_huge(void *pointer, Size size)
{
- MemoryContext context;
+ StandardChunkHeader *header;
void *ret;
if (!AllocHugeSizeIsValid(size))
/*
* OK, it's probably safe to look at the chunk header.
*/
- context = ((StandardChunkHeader *)
- ((char *) pointer - STANDARDCHUNKHEADERSIZE))->context;
+ header = (StandardChunkHeader *)
+ ((char *) pointer - STANDARDCHUNKHEADERSIZE);
AssertArg(MemoryContextIsValid(context));
AssertNotInCriticalSection(context);
/* isReset must be false already */
Assert(!context->isReset);
- ret = (*context->methods->realloc) (context, pointer, size);
+ switch (header->chunk_context_type)
+ {
+ case CHUNK_CONTEXT_TYPE_ASET:
+ ret = AllocSetRealloc(pointer,
+ header->chunk_context_id,
+ header->chunk_size);
+ break;
+
+ default:
+ elog(ERROR, "can't reallocate chunk of unknown type %d at %p",
+ header->chunk_context_type, pointer);
+ ret = NULL;
+ break;
+ }
+
if (ret == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
{
void *(*alloc) (MemoryContext context, Size size);
/* call this free_p in case someone #define's free() */
- void (*free_p) (MemoryContext context, void *pointer);
- void *(*realloc) (MemoryContext context, void *pointer, Size size);
void (*reset) (MemoryContext context);
void (*delete_context) (MemoryContext context);
- Size (*get_chunk_space) (MemoryContext context, void *pointer);
bool (*is_empty) (MemoryContext context);
void (*stats) (MemoryContext context, int level, bool print,
MemoryContextCounters *totals);
#define AllocHugeSizeIsValid(size) ((Size) (size) <= MaxAllocHugeSize)
+/*
+ * You should not do memory allocations within a critical section, because
+ * an out-of-memory error will be escalated to a PANIC. To enforce that
+ * rule, the allocation functions Assert that.
+ */
+#define AssertNotInCriticalSection(context) \
+ Assert(CritSectionCount == 0 || (context)->allowInCritSection)
+
+/*
+ * To save space, we cram a compact representation of the memory context
+ * type, the specific memory context in use, and the size of the chunk into
+ * a single 4-byte or 8-byte word prior to the start of the chunk.
+ * It won't be possible to do this in all cases for lack of bit space;
+ * but individual memory context implementations can use an extended header
+ * which precedes the standard header in cases where it is needed.
+ *
+ * We allow three bits to represent the context type. Considering that
+ * we not had more than one context type any time in recent memory, this
+ * seems likely to be adequate for some time to come.
+ *
+ * On systems with 8-byte alignment, we allow 29 bits for the context ID and
+ * 32 bits for the size. This allows for up to half a billion context IDs and
+ * sizes of up to 1 byte less than 4GB. Thus, extended headers should
+ * almost never be required. On systems with 4-byte alignment, we allow 11
+ * bits for the context ID and 18 bits for the size, which allows for up to
+ * 2048 possible context IDs and allocations of up to 1 byte less than 256kB.
+ * This should be good enough that most allocations won't need an extended
+ * header, but some will.
+ */
+#define CHUNK_CONTEXT_TYPE_BITS 3
+#if MAXIMUM_ALIGNOF == 8
+typedef uint64 chunk_unsigned;
+#define CHUNK_CONTEXT_ID_BITS 29
+#define CHUNK_SIZE_BITS 32
+#else
+typedef uint32 chunk_unsigned;
+#define CHUNK_CONTEXT_ID_BITS 11
+#define CHUNK_SIZE_BITS 18
+#endif
+
+/* Context type codes; must be less than 1 << CHUNK_CONTEXT_TYPE_BITS. */
+#define CHUNK_CONTEXT_TYPE_ASET 0
+
/*
* All chunks allocated by any memory context manager are required to be
- * preceded by a StandardChunkHeader at a spacing of STANDARDCHUNKHEADERSIZE.
- * A currently-allocated chunk must contain a backpointer to its owning
- * context as well as the allocated size of the chunk. The backpointer is
- * used by pfree() and repalloc() to find the context to call. The allocated
- * size is not absolutely essential, but it's expected to be needed by any
- * reasonable implementation.
+ * preceded by a StandardChunkHeader at a spacing of STANDARDCHUNKHEADERSIZE,
+ * which should be 8 bytes on systems where MAXIMUM_ALIGNOF is 8, and 4 bytes
+ * everywhere else.
+ *
+ * The memory context machinery looks at chunk_context_type to figure out
+ * which routine to call when memory is freed.
+ *
+ * The interpretation of the chunk_context_id is partially at the discretion
+ * of the individual context implementation, but should not differ for two
+ * chunks that are in fact part of the same context.
+ *
+ * The chunk_size should be the actual size of the allocated chunk, unless
+ * it is zero, in which case the size may be anything.
*/
typedef struct StandardChunkHeader
{
- MemoryContext context; /* owning context */
- Size size; /* size of data space allocated in chunk */
-#ifdef MEMORY_CONTEXT_CHECKING
- /* when debugging memory usage, also store actual requested size */
- Size requested_size;
-#endif
+ chunk_unsigned chunk_context_type:CHUNK_CONTEXT_TYPE_BITS;
+ chunk_unsigned chunk_context_id:CHUNK_CONTEXT_ID_BITS;
+ chunk_unsigned chunk_size:CHUNK_SIZE_BITS;
} StandardChunkHeader;
#define STANDARDCHUNKHEADERSIZE MAXALIGN(sizeof(StandardChunkHeader))
-
/*
* Standard top-level memory contexts.
*
Size initBlockSize,
Size maxBlockSize);
+/* Private methods only intended to be called from mcxt.c */
+extern void AllocSetFree(void *pointer,
+ chunk_unsigned context_id,
+ chunk_unsigned size);
+extern void *AllocSetRealloc(void *pointer,
+ chunk_unsigned context_id,
+ chunk_unsigned size);
+extern Size AllocSetGetChunkSpace(void *pointer,
+ chunk_unsigned context_id,
+ chunk_unsigned size);
+extern MemoryContext AllocSetGetChunkContext(void *pointer,
+ chunk_unsigned context_id);
+
/*
* Recommended default alloc parameters, suitable for "ordinary" contexts
* that might hold quite a lot of data.