Hack, hack.
authorRobert Haas <rhaas@postgresql.org>
Mon, 21 Dec 2015 18:04:13 +0000 (13:04 -0500)
committerRobert Haas <rhaas@postgresql.org>
Mon, 21 Dec 2015 18:04:13 +0000 (13:04 -0500)
src/backend/utils/mmgr/aset.c
src/backend/utils/mmgr/mcxt.c
src/include/nodes/memnodes.h
src/include/utils/memutils.h

index 5f83182649486d2aa4e5871ae429b8745234c939..e13072849d610b740f5a92ddb28ae60999dc7795 100644 (file)
@@ -246,11 +246,8 @@ typedef struct AllocChunkData
  * 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);
@@ -264,11 +261,8 @@ static void AllocSetCheck(MemoryContext context);
  */
 static MemoryContextMethods AllocSetMethods = {
        AllocSetAlloc,
-       AllocSetFree,
-       AllocSetRealloc,
        AllocSetReset,
        AllocSetDelete,
-       AllocSetGetChunkSpace,
        AllocSetIsEmpty,
        AllocSetStats
 #ifdef MEMORY_CONTEXT_CHECKING
@@ -990,8 +984,8 @@ AllocSetAlloc(MemoryContext context, Size size)
  * 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);
@@ -1071,13 +1065,15 @@ AllocSetFree(MemoryContext context, void *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));
@@ -1266,7 +1262,7 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size 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);
index 550cbf8ac0e94f3de4c89770c415c00fc9ca0187..f9ee83488aa5b535d7841e4c12561545e27d9307 100644 (file)
@@ -56,14 +56,6 @@ static void MemoryContextStatsInternal(MemoryContext context, int level,
                                                   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                                                                                                              *
  *****************************************************************************/
@@ -406,10 +398,17 @@ GetMemoryChunkSpace(void *pointer)
        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);
+       }
 }
 
 /*
@@ -436,9 +435,15 @@ GetMemoryChunkContext(void *pointer)
        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);
+       }
 }
 
 /*
@@ -613,6 +618,7 @@ bool
 MemoryContextContains(MemoryContext context, void *pointer)
 {
        StandardChunkHeader *header;
+       MemoryContext   chunk_context;
 
        /*
         * Try to detect bogus pointers handed to us, poorly though we can.
@@ -628,7 +634,20 @@ MemoryContextContains(MemoryContext context, void *pointer)
        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;
 }
 
 /*--------------------
@@ -920,7 +939,7 @@ palloc_extended(Size size, int flags)
 void
 pfree(void *pointer)
 {
-       MemoryContext context;
+       StandardChunkHeader *header;
 
        /*
         * Try to detect bogus pointers handed to us, poorly though we can.
@@ -933,12 +952,21 @@ pfree(void *pointer)
        /*
         * 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);
 }
 
@@ -949,7 +977,7 @@ pfree(void *pointer)
 void *
 repalloc(void *pointer, Size size)
 {
-       MemoryContext context;
+       StandardChunkHeader *header;
        void       *ret;
 
        if (!AllocSizeIsValid(size))
@@ -966,8 +994,8 @@ repalloc(void *pointer, Size 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);
@@ -975,7 +1003,21 @@ repalloc(void *pointer, Size size)
        /* 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),
@@ -1029,7 +1071,7 @@ MemoryContextAllocHuge(MemoryContext context, Size size)
 void *
 repalloc_huge(void *pointer, Size size)
 {
-       MemoryContext context;
+       StandardChunkHeader *header;
        void       *ret;
 
        if (!AllocHugeSizeIsValid(size))
@@ -1046,8 +1088,8 @@ repalloc_huge(void *pointer, Size 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);
@@ -1055,7 +1097,21 @@ repalloc_huge(void *pointer, Size size)
        /* 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),
index 82450d0d8c241dcbb40d5d126e09ba520070738b..41c913bef9b441bcff227d30603154a55af477ae 100644 (file)
@@ -55,11 +55,8 @@ typedef struct MemoryContextMethods
 {
        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);
index 567923a5db493d835f9a52a8a28ca648212bcc9a..4d105793d9e69f703cfe76b1f4307cf69020e00c 100644 (file)
 
 #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.
  *
@@ -132,6 +178,19 @@ extern MemoryContext AllocSetContextCreate(MemoryContext parent,
                                          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.