Importing the latest changes in the MemoryManager API from PostgreSQL code.
authorMuhammad Usama <m.usama@gmail.com>
Thu, 29 Jun 2017 15:30:59 +0000 (20:30 +0500)
committerMuhammad Usama <m.usama@gmail.com>
Thu, 29 Jun 2017 15:32:21 +0000 (20:32 +0500)
Pgpool-II has borrowed the MemoryManager from PostgreSQL source and recently
there have been some performance updates made to the PostgreSQL's
memory manager, from which Pgpool-II can also benefit from.
This need was identified by the reporter of issue: "315: High CPU usage when
commiting large transactions and using in (shared) memory cache"

src/include/parser/pg_config_manual.h
src/include/pool_type.h
src/include/utils/memnodes.h
src/include/utils/memutils.h
src/include/utils/palloc.h
src/utils/mmgr/aset.c
src/utils/mmgr/mcxt.c

index a7c83fe04d3c17f967c43ffa68ba016796978e34..d7e153b2f0cc3600ca5e4f2353685a221367eb7f 100644 (file)
 #define PG_PRINTF_ATTRIBUTE printf
 #endif
 
+/* from postgresql/src/include/c.h */
+/* GCC and XLC support format attributes */
+#if defined(__GNUC__) || defined(__IBMC__)
+#define pg_attribute_format_arg(a) __attribute__((format_arg(a)))
+#define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a)))
+#else
+#define pg_attribute_format_arg(a)
+#define pg_attribute_printf(f,a)
+#endif
 /*
  * Maximum length for identifiers (e.g. table names, column names,
  * function names).  Names actually are limited to one less byte than this,
index 34efbaf3016a18ecbec4de098642403bb82817ff..84013e6e6ec0dc41efaa2dba69149f5392459199 100644 (file)
@@ -29,6 +29,7 @@
 #include "config.h"
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <stddef.h>
 #include "libpq-fe.h"
 /* Define common boolean type. C++ and BEOS already has it so exclude them. */
 #ifdef c_plusplus
@@ -57,6 +58,21 @@ typedef char bool;
 #endif /* not C++ */
 #endif /* __BEOS__ */
 
+/* ----------------------------------------------------------------
+ *              Section 5:  offsetof, lengthof, endof, alignment
+ * ----------------------------------------------------------------
+ */
+/*
+ * offsetof
+ *      Offset of a structure/union field within that structure/union.
+ *
+ *      XXX This is supposed to be part of stddef.h, but isn't on
+ *      some systems (like SunOS 4).
+ */
+#ifndef offsetof
+#define offsetof(type, field)   ((long) &((type *)0)->field)
+#endif
+
 #define PointerIsValid(pointer) ((const void*)(pointer) != NULL)
 typedef signed char int8;              /* == 8 bits */
 typedef signed short int16;            /* == 16 bits */
index 553ccf8fd0ed88700c0dbc53d35048ace18d6210..4e15274b32b3e3a66069422a9628f5c50290e7f6 100644 (file)
@@ -4,8 +4,7 @@
  *       POSTGRES memory context node definitions.
  *
  *
- * Portions Copyright (c) 2003-2014, PgPool Global Development Group
- * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * src/include/nodes/memnodes.h
 #define MEMNODES_H
 
 #include "parser/nodes.h"
+/*
+ * MemoryContextCounters
+ *             Summarization state for MemoryContextStats collection.
+ *
+ * The set of counters in this struct is biased towards AllocSet; if we ever
+ * add any context types that are based on fundamentally different approaches,
+ * we might need more or different counters here.  A possible API spec then
+ * would be to print only nonzero counters, but for now we just summarize in
+ * the format historically used by AllocSet.
+ */
+typedef struct MemoryContextCounters
+{
+       Size            nblocks;                /* Total number of malloc blocks */
+       Size            freechunks;             /* Total number of free chunks */
+       Size            totalspace;             /* Total bytes requested from malloc */
+       Size            freespace;              /* The unused portion of totalspace */
+} MemoryContextCounters;
 
 /*
  * MemoryContext
@@ -45,21 +61,27 @@ typedef struct MemoryContextMethods
        void            (*delete_context) (MemoryContext context);
        Size            (*get_chunk_space) (MemoryContext context, void *pointer);
        bool            (*is_empty) (MemoryContext context);
-       void            (*stats) (MemoryContext context, int level);
+       void            (*stats) (MemoryContext context, int level, bool print,
+                                                 MemoryContextCounters *totals);
 #ifdef MEMORY_CONTEXT_CHECKING
        void            (*check) (MemoryContext context);
 #endif
 } MemoryContextMethods;
 
+
 typedef struct MemoryContextData
 {
        NodeTag         type;                   /* identifies exact kind of context */
-       MemoryContextMethods *methods;          /* virtual function table */
+       /* these two fields are placed here to minimize alignment wastage: */
+       bool            isReset;                /* T = no space alloced since last reset */
+       bool            allowInCritSection; /* allow palloc in critical section */
+       MemoryContextMethods *methods;  /* virtual function table */
        MemoryContext parent;           /* NULL if no parent (toplevel context) */
        MemoryContext firstchild;       /* head of linked list of children */
+       MemoryContext prevchild;        /* previous child of same parent */
        MemoryContext nextchild;        /* next child of same parent */
        char       *name;                       /* context name (just for debugging) */
-       bool            isReset;                /* T = no space alloced since last reset */
+       MemoryContextCallback *reset_cbs;       /* list of reset/delete callbacks */
 } MemoryContextData;
 
 /* utils/palloc.h contains typedef struct MemoryContextData *MemoryContext */
@@ -73,6 +95,6 @@ typedef struct MemoryContextData
  */
 #define MemoryContextIsValid(context) \
        ((context) != NULL && \
-        (IsA((context), AllocSetContext)))
+        (IsA((context), AllocSetContext) || IsA((context), SlabContext)))
 
-#endif   /* MEMNODES_H */
+#endif                                                 /* MEMNODES_H */
index a40af1e6626cd354aaf0f978bc93766b59ddf387..0df8cd1389ad6ebf656e809119a4f08a2571bf60 100644 (file)
@@ -7,7 +7,7 @@
  *       of the API of the memory management subsystem.
  *
  *
- * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * src/include/utils/memutils.h
@@ -37,7 +37,7 @@
  * MemoryContextAllocHuge().  Both limits permit code to assume that it may
  * compute twice an allocation's size without overflow.
  */
-#define MaxAllocSize   ((Size) 0x3fffffff)             /* 1 gigabyte - 1 */
+#define MaxAllocSize   ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
 
 #define AllocSizeIsValid(size) ((Size) (size) <= MaxAllocSize)
 
 
 #define AllocHugeSizeIsValid(size)     ((Size) (size) <= MaxAllocHugeSize)
 
-/*
- * 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.
- */
-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
-} StandardChunkHeader;
-
-#define STANDARDCHUNKHEADERSIZE  MAXALIGN(sizeof(StandardChunkHeader))
-
 
 /*
  * Standard top-level memory contexts.
@@ -79,10 +58,9 @@ extern MemoryContext ProcessLoopContext;
 extern MemoryContext CacheMemoryContext;
 extern MemoryContext MessageContext;
 extern MemoryContext QueryContext;
-extern MemoryContext CurTransactionContext;
 
-/* This is a transient link to the active portal's memory context: */
-extern PGDLLIMPORT MemoryContext PortalContext;
+/* Backwards compatibility macro */
+#define MemoryContextResetAndDeleteChildren(ctx) MemoryContextReset(ctx)
 
 
 /*
@@ -91,22 +69,60 @@ extern PGDLLIMPORT MemoryContext PortalContext;
 extern void MemoryContextInit(void);
 extern void MemoryContextReset(MemoryContext context);
 extern void MemoryContextDelete(MemoryContext context);
+extern void MemoryContextResetOnly(MemoryContext context);
 extern void MemoryContextResetChildren(MemoryContext context);
 extern void MemoryContextDeleteChildren(MemoryContext context);
-extern void MemoryContextResetAndDeleteChildren(MemoryContext context);
 extern void MemoryContextSetParent(MemoryContext context,
                                           MemoryContext new_parent);
 extern Size GetMemoryChunkSpace(void *pointer);
-extern MemoryContext GetMemoryChunkContext(void *pointer);
 extern MemoryContext MemoryContextGetParent(MemoryContext context);
 extern bool MemoryContextIsEmpty(MemoryContext context);
 extern void MemoryContextStats(MemoryContext context);
+extern void MemoryContextStatsDetail(MemoryContext context, int max_children);
+extern void MemoryContextAllowInCriticalSection(MemoryContext context,
+                                                                       bool allow);
 
 #ifdef MEMORY_CONTEXT_CHECKING
 extern void MemoryContextCheck(MemoryContext context);
 #endif
 extern bool MemoryContextContains(MemoryContext context, void *pointer);
 
+/*
+ * GetMemoryChunkContext
+ *             Given a currently-allocated chunk, determine the context
+ *             it belongs to.
+ *
+ * All chunks allocated by any memory context manager are required to be
+ * preceded by the corresponding MemoryContext stored, without padding, in the
+ * preceding sizeof(void*) bytes.  A currently-allocated chunk must contain a
+ * backpointer to its owning context.  The backpointer is used by pfree() and
+ * repalloc() to find the context to call.
+ */
+#ifndef FRONTEND
+static inline MemoryContext
+GetMemoryChunkContext(void *pointer)
+{
+       MemoryContext context;
+
+       /*
+        * Try to detect bogus pointers handed to us, poorly though we can.
+        * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
+        * allocated chunk.
+        */
+       Assert(pointer != NULL);
+       Assert(pointer == (void *) MAXALIGN(pointer));
+
+       /*
+        * OK, it's probably safe to look at the context.
+        */
+       context = *(MemoryContext *) (((char *) pointer) - sizeof(void *));
+
+       AssertArg(MemoryContextIsValid(context));
+
+       return context;
+}
+#endif
+
 /*
  * This routine handles the context-type-independent part of memory
  * context creation.  It's intended to be called from context-type-
@@ -129,6 +145,12 @@ extern MemoryContext AllocSetContextCreate(MemoryContext parent,
                                          Size initBlockSize,
                                          Size maxBlockSize);
 
+/* slab.c */
+extern MemoryContext SlabContextCreate(MemoryContext parent,
+                                 const char *name,
+                                 Size blockSize,
+                                 Size chunkSize);
+
 /*
  * Recommended default alloc parameters, suitable for "ordinary" contexts
  * that might hold quite a lot of data.
@@ -136,13 +158,36 @@ extern MemoryContext AllocSetContextCreate(MemoryContext parent,
 #define ALLOCSET_DEFAULT_MINSIZE   0
 #define ALLOCSET_DEFAULT_INITSIZE  (8 * 1024)
 #define ALLOCSET_DEFAULT_MAXSIZE   (8 * 1024 * 1024)
+#define ALLOCSET_DEFAULT_SIZES \
+       ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE
 
 /*
- * Recommended alloc parameters for "small" contexts that are not expected
+ * Recommended alloc parameters for "small" contexts that are never expected
  * to contain much data (for example, a context to contain a query plan).
  */
 #define ALLOCSET_SMALL_MINSIZE  0
 #define ALLOCSET_SMALL_INITSIZE  (1 * 1024)
 #define ALLOCSET_SMALL_MAXSIZE  (8 * 1024)
+#define ALLOCSET_SMALL_SIZES \
+       ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE
+
+/*
+ * Recommended alloc parameters for contexts that should start out small,
+ * but might sometimes grow big.
+ */
+#define ALLOCSET_START_SMALL_SIZES \
+       ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE
+
+
+/*
+ * Threshold above which a request in an AllocSet context is certain to be
+ * allocated separately (and thereby have constant allocation overhead).
+ * Few callers should be interested in this, but tuplesort/tuplestore need
+ * to know it.
+ */
+#define ALLOCSET_SEPARATE_THRESHOLD  8192
+
+#define SLAB_DEFAULT_BLOCK_SIZE                (8 * 1024)
+#define SLAB_LARGE_BLOCK_SIZE          (8 * 1024 * 1024)
 
-#endif   /* MEMUTILS_H */
+#endif                                                 /* MEMUTILS_H */
index 45d544e3f19b6137d4c861856207d9fa84fc8bc4..4b6359ca865d7a7b683d90f946cb6709d1cfd354 100644 (file)
@@ -6,9 +6,9 @@
  * This file contains the basic memory allocation interface that is
  * needed by almost every backend module.  It is included directly by
  * postgres.h, so the definitions here are automatically available
- * everywhere. Keep it lean!
+ * everywhere.  Keep it lean!
  *
- * Memory allocation occurs within "contexts". Every chunk obtained from
+ * Memory allocation occurs within "contexts".  Every chunk obtained from
  * palloc()/MemoryContextAlloc() is allocated within a specific context.
  * The entire contents of a context can be freed easily and quickly by
  * resetting or deleting the context --- this is both faster and less
@@ -18,8 +18,7 @@
  * everything that should be freed.  See utils/mmgr/README for more info.
  *
  *
- * Portions Copyright (c) 2003-2015, PgPool Global Development Group
- * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * src/include/utils/palloc.h
 
 #include <stdarg.h>
 #include "parser/pg_config_manual.h"
-
 /*
- * Type MemoryContextData is declared in nodes/memnodes.h.     Most users
+ * Type MemoryContextData is declared in nodes/memnodes.h.  Most users
  * of memory allocation should just treat it as an abstract type, so we
  * do not provide the struct contents here.
  */
 typedef struct MemoryContextData *MemoryContext;
 
-#ifndef FRONTEND
+/*
+ * A memory context can have callback functions registered on it.  Any such
+ * function will be called once just before the context is next reset or
+ * deleted.  The MemoryContextCallback struct describing such a callback
+ * typically would be allocated within the context itself, thereby avoiding
+ * any need to manage it explicitly (the reset/delete action will free it).
+ */
+typedef void (*MemoryContextCallbackFunction) (void *arg);
+
+typedef struct MemoryContextCallback
+{
+       MemoryContextCallbackFunction func; /* function to call */
+       void       *arg;                        /* argument to pass it */
+       struct MemoryContextCallback *next; /* next in list of callbacks */
+} MemoryContextCallback;
 
 /*
  * CurrentMemoryContext is the default allocation context for palloc().
- * We declare it here so that palloc() can be a macro. Avoid accessing it
- * directly!  Instead, use MemoryContextSwitchTo() to change the setting.
+ * Avoid accessing it directly!  Instead, use MemoryContextSwitchTo()
+ * to change the setting.
  */
 extern PGDLLIMPORT MemoryContext CurrentMemoryContext;
 
+/*
+ * Flags for MemoryContextAllocExtended.
+ */
+#define MCXT_ALLOC_HUGE                        0x01    /* allow huge allocation (> 1 GB) */
+#define MCXT_ALLOC_NO_OOM              0x02    /* no failure if out-of-memory */
+#define MCXT_ALLOC_ZERO                        0x04    /* zero allocated memory */
+
 /*
  * Fundamental memory-allocation operations (more are in utils/memutils.h)
  */
 extern void *MemoryContextAlloc(MemoryContext context, Size size);
 extern void *MemoryContextAllocZero(MemoryContext context, Size size);
 extern void *MemoryContextAllocZeroAligned(MemoryContext context, Size size);
+extern void *MemoryContextAllocExtended(MemoryContext context,
+                                                  Size size, int flags);
 
-/* Higher-limit allocators. */
-extern void *MemoryContextAllocHuge(MemoryContext context, Size size);
-extern void *repalloc_huge(void *pointer, Size size);
+extern void *palloc(Size size);
+extern void *palloc0(Size size);
+extern void *palloc_extended(Size size, int flags);
+extern void *repalloc(void *pointer, Size size);
+extern void pfree(void *pointer);
 
 /*
  * The result of palloc() is always word-aligned, so we can skip testing
@@ -72,17 +95,19 @@ extern void *repalloc_huge(void *pointer, Size size);
                MemoryContextAllocZeroAligned(CurrentMemoryContext, sz) : \
                MemoryContextAllocZero(CurrentMemoryContext, sz) )
 
+/* Higher-limit allocators. */
+extern void *MemoryContextAllocHuge(MemoryContext context, Size size);
+extern void *repalloc_huge(void *pointer, Size size);
+
 /*
- * MemoryContextSwitchTo can't be a macro in standard C compilers.
- * But we can make it an inline function if the compiler supports it.
- * See STATIC_IF_INLINE in c.h.
+ * Although this header file is nominally backend-only, certain frontend
+ * programs like pg_controldata include it via postgres.h.  For some compilers
+ * it's necessary to hide the inline definition of MemoryContextSwitchTo in
+ * this scenario; hence the #ifndef FRONTEND.
  */
 
-#ifndef PG_USE_INLINE
-extern MemoryContext MemoryContextSwitchTo(MemoryContext context);
-#endif   /* !PG_USE_INLINE */
-#if defined(PG_USE_INLINE) || defined(MCXT_INCLUDE_DEFINITIONS)
-STATIC_IF_INLINE MemoryContext
+#ifndef FRONTEND
+static inline MemoryContext
 MemoryContextSwitchTo(MemoryContext context)
 {
        MemoryContext old = CurrentMemoryContext;
@@ -90,28 +115,24 @@ MemoryContextSwitchTo(MemoryContext context)
        CurrentMemoryContext = context;
        return old;
 }
-#endif   /* PG_USE_INLINE || MCXT_INCLUDE_DEFINITIONS */
+#endif                                                 /* FRONTEND */
+
+/* Registration of memory context reset/delete callbacks */
+extern void MemoryContextRegisterResetCallback(MemoryContext context,
+                                                                  MemoryContextCallback *cb);
 
 /*
  * These are like standard strdup() except the copied string is
  * allocated in a context, not with malloc().
  */
 extern char *MemoryContextStrdup(MemoryContext context, const char *string);
-#endif   /* !FRONTEND */
-
 extern char *pstrdup(const char *in);
 extern char *pnstrdup(const char *in, Size len);
-extern void *palloc(Size size);
-extern void *palloc0(Size size);
-extern void pfree(void *pointer);
-extern void *repalloc(void *pointer, Size size);
-/* sprintf into a palloc'd buffer --- these are in psprintf.c */
-extern char *
-psprintf(const char *fmt,...)
-__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
-extern size_t
-pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
-__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 0)));
 
+extern char *pchomp(const char *in);
+
+/* sprintf into a palloc'd buffer --- these are in psprintf.c */
+extern char *psprintf(const char *fmt,...) pg_attribute_printf(1, 2);
+extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0);
 
-#endif   /* PALLOC_H */
+#endif                                                 /* PALLOC_H */
index 37b60493708033153b90c29b0a1e3f0a68f78a50..7dbe589a698b86f3f1b6fe2dd911375f2ccb3f3f 100644 (file)
@@ -7,7 +7,7 @@
  * type.
  *
  *
- * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
  *     request, even if it was much larger than necessary.  This led to more
  *     and more wasted space in allocated chunks over time.  To fix, get rid
  *     of the midrange behavior: we now handle only "small" power-of-2-size
- *     chunks as chunks.  Anything "large" is passed off to malloc().  Change
+ *     chunks as chunks.  Anything "large" is passed off to malloc().  Change
  *     the number of freelists to change the small/large boundary.
  *
- *
- *     About CLOBBER_FREED_MEMORY:
- *
- *     If this symbol is defined, all freed memory is overwritten with 0x7F's.
- *     This is useful for catching places that reference already-freed memory.
- *
- *     About MEMORY_CONTEXT_CHECKING:
- *
- *     Since we usually round request sizes up to the next power of 2, there
- *     is often some unused space immediately after a requested data area.
- *     Thus, if someone makes the common error of writing past what they've
- *     requested, the problem is likely to go unnoticed ... until the day when
- *     there *isn't* any wasted space, perhaps because of different memory
- *     alignment on a new platform, or some other effect.      To catch this sort
- *     of problem, the MEMORY_CONTEXT_CHECKING option stores 0x7E just beyond
- *     the requested space whenever the request is less than the actual chunk
- *     size, and verifies that the byte is undamaged when the chunk is freed.
- *
- *
- *     About USE_VALGRIND and Valgrind client requests:
- *
- *     Valgrind provides "client request" macros that exchange information with
- *     the host Valgrind (if any).  Under !USE_VALGRIND, memdebug.h stubs out
- *     currently-used macros.
- *
- *     When running under Valgrind, we want a NOACCESS memory region both before
- *     and after the allocation.  The chunk header is tempting as the preceding
- *     region, but mcxt.c expects to able to examine the standard chunk header
- *     fields.  Therefore, we use, when available, the requested_size field and
- *     any subsequent padding.  requested_size is made NOACCESS before returning
- *     a chunk pointer to a caller.  However, to reduce client request traffic,
- *     it is kept DEFINED in chunks on the free list.
- *
- *     The rounded-up capacity of the chunk usually acts as a post-allocation
- *     NOACCESS region.  If the request consumes precisely the entire chunk,
- *     there is no such region; another chunk header may immediately follow.  In
- *     that case, Valgrind will not detect access beyond the end of the chunk.
- *
- *     See also the cooperating Valgrind client requests in mcxt.c.
- *
  *-------------------------------------------------------------------------
  */
 
@@ -91,6 +51,7 @@
 #include "utils/elog.h"
 #include <string.h>
 #include <stdint.h>
+
 /* Define this to detail debug alloc information */
 /* #define HAVE_ALLOCINFO */
 
  *
  * With the current parameters, request sizes up to 8K are treated as chunks,
  * larger requests go into dedicated blocks.  Change ALLOCSET_NUM_FREELISTS
- * to adjust the boundary point.  (But in contexts with small maxBlockSize,
- * we may set the allocChunkLimit to less than 8K, so as to avoid space
- * wastage.)
+ * to adjust the boundary point; and adjust ALLOCSET_SEPARATE_THRESHOLD in
+ * memutils.h to agree.  (Note: in contexts with small maxBlockSize, we may
+ * set the allocChunkLimit to less than 8K, so as to avoid space wastage.)
  *--------------------
  */
 
  */
 
 #define ALLOC_BLOCKHDRSZ       MAXALIGN(sizeof(AllocBlockData))
-#define ALLOC_CHUNKHDRSZ       MAXALIGN(sizeof(AllocChunkData))
+#define ALLOC_CHUNKHDRSZ       sizeof(struct AllocChunkData)
 
-/* Portion of ALLOC_CHUNKHDRSZ examined outside aset.c. */
-#define ALLOC_CHUNK_PUBLIC     \
-       (offsetof(AllocChunkData, size) + sizeof(Size))
-
-/* Portion of ALLOC_CHUNKHDRSZ excluding trailing padding. */
-#ifdef MEMORY_CONTEXT_CHECKING
-#define ALLOC_CHUNK_USED       \
-       (offsetof(AllocChunkData, requested_size) + sizeof(Size))
-#else
-#define ALLOC_CHUNK_USED       \
-       (offsetof(AllocChunkData, size) + sizeof(Size))
-#endif
-
-typedef struct AllocBlockData *AllocBlock;             /* forward reference */
+typedef struct AllocBlockData *AllocBlock;     /* forward reference */
 typedef struct AllocChunkData *AllocChunk;
 
 /*
@@ -177,7 +125,7 @@ typedef struct AllocSetContext
        MemoryContextData header;       /* Standard memory-context fields */
        /* Info about storage allocated in this context: */
        AllocBlock      blocks;                 /* head of list of blocks in this set */
-       AllocChunk      freelist[ALLOCSET_NUM_FREELISTS];               /* free chunk lists */
+       AllocChunk      freelist[ALLOCSET_NUM_FREELISTS];       /* free chunk lists */
        /* Allocation parameters for this context: */
        Size            initBlockSize;  /* initial block size */
        Size            maxBlockSize;   /* maximum block size */
@@ -191,7 +139,7 @@ typedef AllocSetContext *AllocSet;
 /*
  * AllocBlock
  *             An AllocBlock is the unit of memory that is obtained by aset.c
- *             from malloc().  It contains one or more AllocChunks, which are
+ *             from malloc().  It contains one or more AllocChunks, which are
  *             the units requested by palloc() and freed by pfree().  AllocChunks
  *             cannot be returned to malloc() individually, instead they are put
  *             on freelists by pfree() and re-used by the next palloc() that has
@@ -203,29 +151,35 @@ typedef AllocSetContext *AllocSet;
 typedef struct AllocBlockData
 {
        AllocSet        aset;                   /* aset that owns this block */
-       AllocBlock      next;                   /* next block in aset's blocks list */
+       AllocBlock      prev;                   /* prev block in aset's blocks list, if any */
+       AllocBlock      next;                   /* next block in aset's blocks list, if any */
        char       *freeptr;            /* start of free space in this block */
        char       *endptr;                     /* end of space in this block */
-}      AllocBlockData;
+}                      AllocBlockData;
 
 /*
  * AllocChunk
  *             The prefix of each piece of memory in an AllocBlock
- *
- * NB: this MUST match StandardChunkHeader as defined by utils/memutils.h.
  */
 typedef struct AllocChunkData
 {
-       /* aset is the owning aset if allocated, or the freelist link if free */
-       void       *aset;
        /* 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;
+#if MAXIMUM_ALIGNOF > 4 && SIZEOF_VOID_P == 4
+       Size            padding;
 #endif
-}      AllocChunkData;
+
+#endif                                                 /* MEMORY_CONTEXT_CHECKING */
+
+       /* aset is the owning aset if allocated, or the freelist link if free */
+       void       *aset;
+
+       /* there must not be any padding to reach a MAXALIGN boundary here! */
+}                      AllocChunkData;
 
 /*
  * AllocPointerIsValid
@@ -255,7 +209,8 @@ 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);
+static void AllocSetStats(MemoryContext context, int level, bool print,
+                         MemoryContextCounters *totals);
 
 #ifdef MEMORY_CONTEXT_CHECKING
 static void AllocSetCheck(MemoryContext context);
@@ -297,10 +252,10 @@ static const unsigned char LogTable256[256] =
  */
 #ifdef HAVE_ALLOCINFO
 #define AllocFreeInfo(_cxt, _chunk) \
-                       fprintf(stderr, "AllocFree: %s: %p, %d\n", \
+                       fprintf(stderr, "AllocFree: %s: %p, %zu\n", \
                                (_cxt)->header.name, (_chunk), (_chunk)->size)
 #define AllocAllocInfo(_cxt, _chunk) \
-                       fprintf(stderr, "AllocAlloc: %s: %p, %d\n", \
+                       fprintf(stderr, "AllocAlloc: %s: %p, %zu\n", \
                                (_cxt)->header.name, (_chunk), (_chunk)->size)
 #else
 #define AllocFreeInfo(_cxt, _chunk)
@@ -328,7 +283,7 @@ AllocSetFreeIndex(Size size)
 
                /*
                 * At this point we need to obtain log2(tsize)+1, ie, the number of
-                * not-all-zero bits at the right.      We used to do this with a
+                * not-all-zero bits at the right.  We used to do this with a
                 * shift-and-count loop, but this function is enough of a hotspot to
                 * justify micro-optimization effort.  The best approach seems to be
                 * to use a lookup table.  Note that this code assumes that
@@ -346,77 +301,6 @@ AllocSetFreeIndex(Size size)
        return idx;
 }
 
-#ifdef CLOBBER_FREED_MEMORY
-
-/* Wipe freed memory for debugging purposes */
-static void
-wipe_mem(void *ptr, size_t size)
-{
-       VALGRIND_MAKE_MEM_UNDEFINED(ptr, size);
-       memset(ptr, 0x7F, size);
-       VALGRIND_MAKE_MEM_NOACCESS(ptr, size);
-}
-#endif
-
-#ifdef MEMORY_CONTEXT_CHECKING
-static void
-set_sentinel(void *base, Size offset)
-{
-       char       *ptr = (char *) base + offset;
-
-       VALGRIND_MAKE_MEM_UNDEFINED(ptr, 1);
-       *ptr = 0x7E;
-       VALGRIND_MAKE_MEM_NOACCESS(ptr, 1);
-}
-
-static bool
-sentinel_ok(const void *base, Size offset)
-{
-       const char *ptr = (const char *) base + offset;
-       bool            ret;
-
-       VALGRIND_MAKE_MEM_DEFINED(ptr, 1);
-       ret = *ptr == 0x7E;
-       VALGRIND_MAKE_MEM_NOACCESS(ptr, 1);
-
-       return ret;
-}
-#endif
-
-#ifdef RANDOMIZE_ALLOCATED_MEMORY
-
-/*
- * Fill a just-allocated piece of memory with "random" data.  It's not really
- * very random, just a repeating sequence with a length that's prime.  What
- * we mainly want out of it is to have a good probability that two palloc's
- * of the same number of bytes start out containing different data.
- *
- * The region may be NOACCESS, so make it UNDEFINED first to avoid errors as
- * we fill it.  Filling the region makes it DEFINED, so make it UNDEFINED
- * again afterward.  Whether to finally make it UNDEFINED or NOACCESS is
- * fairly arbitrary.  UNDEFINED is more convenient for AllocSetRealloc(), and
- * other callers have no preference.
- */
-static void
-randomize_mem(char *ptr, size_t size)
-{
-       static int      save_ctr = 1;
-       size_t          remaining = size;
-       int                     ctr;
-
-       ctr = save_ctr;
-       VALGRIND_MAKE_MEM_UNDEFINED(ptr, size);
-       while (remaining-- > 0)
-       {
-               *ptr++ = ctr;
-               if (++ctr > 251)
-                       ctr = 1;
-       }
-       VALGRIND_MAKE_MEM_UNDEFINED(ptr - size, size);
-       save_ctr = ctr;
-}
-#endif   /* RANDOMIZE_ALLOCATED_MEMORY */
-
 
 /*
  * Public routines
@@ -428,10 +312,14 @@ randomize_mem(char *ptr, size_t size)
  *             Create a new AllocSet context.
  *
  * parent: parent context, or NULL if top-level context
- * name: name of context (for debugging --- string will be copied)
+ * name: name of context (for debugging only, need not be unique)
  * minContextSize: minimum context size
  * initBlockSize: initial allocation block size
  * maxBlockSize: maximum allocation block size
+ *
+ * Notes: the name string will be copied into context-lifespan storage.
+ * Most callers should abstract the context size parameters using a macro
+ * such as ALLOCSET_DEFAULT_SIZES.
  */
 MemoryContext
 AllocSetContextCreate(MemoryContext parent,
@@ -440,30 +328,43 @@ AllocSetContextCreate(MemoryContext parent,
                                          Size initBlockSize,
                                          Size maxBlockSize)
 {
-       AllocSet        context;
+       AllocSet        set;
 
-       /* Do the type-independent part of context creation */
-       context = (AllocSet) MemoryContextCreate(T_AllocSetContext,
-                                                                                        sizeof(AllocSetContext),
-                                                                                        &AllocSetMethods,
-                                                                                        parent,
-                                                                                        name);
+       StaticAssertStmt(offsetof(AllocChunkData, aset) + sizeof(MemoryContext) ==
+                                        MAXALIGN(sizeof(AllocChunkData)),
+                                        "padding calculation in AllocChunkData is wrong");
 
        /*
-        * Make sure alloc parameters are reasonable, and save them.
-        *
-        * We somewhat arbitrarily enforce a minimum 1K block size.
+        * First, validate allocation parameters.  (If we're going to throw an
+        * error, we should do so before the context is created, not after.)  We
+        * somewhat arbitrarily enforce a minimum 1K block size.
         */
-       initBlockSize = MAXALIGN(initBlockSize);
-       if (initBlockSize < 1024)
-               initBlockSize = 1024;
-       maxBlockSize = MAXALIGN(maxBlockSize);
-       if (maxBlockSize < initBlockSize)
-               maxBlockSize = initBlockSize;
-       Assert(AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
-       context->initBlockSize = initBlockSize;
-       context->maxBlockSize = maxBlockSize;
-       context->nextBlockSize = initBlockSize;
+       if (initBlockSize != MAXALIGN(initBlockSize) ||
+               initBlockSize < 1024)
+               elog(ERROR, "invalid initBlockSize for memory context: %zu",
+                        initBlockSize);
+       if (maxBlockSize != MAXALIGN(maxBlockSize) ||
+               maxBlockSize < initBlockSize ||
+               !AllocHugeSizeIsValid(maxBlockSize))    /* must be safe to double */
+               elog(ERROR, "invalid maxBlockSize for memory context: %zu",
+                        maxBlockSize);
+       if (minContextSize != 0 &&
+               (minContextSize != MAXALIGN(minContextSize) ||
+                minContextSize <= ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ))
+               elog(ERROR, "invalid minContextSize for memory context: %zu",
+                        minContextSize);
+
+       /* Do the type-independent part of context creation */
+       set = (AllocSet) MemoryContextCreate(T_AllocSetContext,
+                                                                                sizeof(AllocSetContext),
+                                                                                &AllocSetMethods,
+                                                                                parent,
+                                                                                name);
+
+       /* Save allocation parameters */
+       set->initBlockSize = initBlockSize;
+       set->maxBlockSize = maxBlockSize;
+       set->nextBlockSize = initBlockSize;
 
        /*
         * Compute the allocation chunk size limit for this context.  It can't be
@@ -478,18 +379,23 @@ AllocSetContextCreate(MemoryContext parent,
         * We have to have allocChunkLimit a power of two, because the requested
         * and actually-allocated sizes of any chunk must be on the same side of
         * the limit, else we get confused about whether the chunk is "big".
+        *
+        * Also, allocChunkLimit must not exceed ALLOCSET_SEPARATE_THRESHOLD.
         */
-       context->allocChunkLimit = ALLOC_CHUNK_LIMIT;
-       while ((Size) (context->allocChunkLimit + ALLOC_CHUNKHDRSZ) >
+       StaticAssertStmt(ALLOC_CHUNK_LIMIT == ALLOCSET_SEPARATE_THRESHOLD,
+                                        "ALLOC_CHUNK_LIMIT != ALLOCSET_SEPARATE_THRESHOLD");
+
+       set->allocChunkLimit = ALLOC_CHUNK_LIMIT;
+       while ((Size) (set->allocChunkLimit + ALLOC_CHUNKHDRSZ) >
                   (Size) ((maxBlockSize - ALLOC_BLOCKHDRSZ) / ALLOC_CHUNK_FRACTION))
-               context->allocChunkLimit >>= 1;
+               set->allocChunkLimit >>= 1;
 
        /*
         * Grab always-allocated space, if requested
         */
-       if (minContextSize > ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)
+       if (minContextSize > 0)
        {
-               Size            blksize = MAXALIGN(minContextSize);
+               Size            blksize = minContextSize;
                AllocBlock      block;
 
                block = (AllocBlock) malloc(blksize);
@@ -502,20 +408,23 @@ AllocSetContextCreate(MemoryContext parent,
                                         errdetail("Failed while creating memory context \"%s\".",
                                                           name)));
                }
-               block->aset = context;
+               block->aset = set;
                block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
                block->endptr = ((char *) block) + blksize;
-               block->next = context->blocks;
-               context->blocks = block;
+               block->prev = NULL;
+               block->next = set->blocks;
+               if (block->next)
+                       block->next->prev = block;
+               set->blocks = block;
                /* Mark block as not to be released at reset time */
-               context->keeper = block;
+               set->keeper = block;
 
                /* Mark unallocated space NOACCESS; leave the block header alone. */
                VALGRIND_MAKE_MEM_NOACCESS(block->freeptr,
                                                                   blksize - ALLOC_BLOCKHDRSZ);
        }
 
-       return (MemoryContext) context;
+       return (MemoryContext) set;
 }
 
 /*
@@ -546,7 +455,7 @@ AllocSetInit(MemoryContext context)
  * 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,
+ * 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.
  */
@@ -587,6 +496,7 @@ AllocSetReset(MemoryContext context)
                        VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
 #endif
                        block->freeptr = datastart;
+                       block->prev = NULL;
                        block->next = NULL;
                }
                else
@@ -644,8 +554,8 @@ AllocSetDelete(MemoryContext context)
 
 /*
  * AllocSetAlloc
- *             Returns pointer to allocated memory of given size; memory is added
- *             to the set.
+ *             Returns pointer to allocated memory of given size or NULL if
+ *             request could not be completed; memory is added to the set.
  *
  * No request may exceed:
  *             MAXALIGN_DOWN(SIZE_MAX) - ALLOC_BLOCKHDRSZ - ALLOC_CHUNKHDRSZ
@@ -673,14 +583,7 @@ AllocSetAlloc(MemoryContext context, Size 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)));
-               }
+                       return NULL;
                block->aset = set;
                block->freeptr = block->endptr = ((char *) block) + blksize;
 
@@ -700,16 +603,20 @@ AllocSetAlloc(MemoryContext context, Size size)
 #endif
 
                /*
-                * Stick the new block underneath the active allocation block, so that
-                * we don't lose the use of the space remaining therein.
+                * Stick the new block underneath the active allocation block, if any,
+                * so that we don't lose the use of the space remaining therein.
                 */
                if (set->blocks != NULL)
                {
+                       block->prev = set->blocks;
                        block->next = set->blocks->next;
+                       if (block->next)
+                               block->next->prev = block;
                        set->blocks->next = block;
                }
                else
                {
+                       block->prev = NULL;
                        block->next = NULL;
                        set->blocks = block;
                }
@@ -717,13 +624,13 @@ AllocSetAlloc(MemoryContext context, Size size)
                AllocAllocInfo(set, chunk);
 
                /*
-                * Chunk header public fields remain DEFINED.  The requested
-                * allocation itself can be NOACCESS or UNDEFINED; our caller will
-                * soon make it UNDEFINED.  Make extra space at the end of the chunk,
-                * if any, NOACCESS.
+                * Chunk's metadata fields remain DEFINED.  The requested allocation
+                * itself can be NOACCESS or UNDEFINED; our caller will soon make it
+                * UNDEFINED.  Make extra space at the end of the chunk, if any,
+                * NOACCESS.
                 */
-               VALGRIND_MAKE_MEM_NOACCESS((char *) chunk + ALLOC_CHUNK_PUBLIC,
-                                                chunk_size + ALLOC_CHUNKHDRSZ - ALLOC_CHUNK_PUBLIC);
+               VALGRIND_MAKE_MEM_NOACCESS((char *) chunk + ALLOC_CHUNKHDRSZ,
+                                                                  chunk_size - ALLOC_CHUNKHDRSZ);
 
                return AllocChunkGetPointer(chunk);
        }
@@ -797,7 +704,7 @@ AllocSetAlloc(MemoryContext context, Size size)
 
                                /*
                                 * 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
+                                * freelist than the one we need to put this chunk on.  The
                                 * exception is when availchunk is exactly a power of 2.
                                 */
                                if (availchunk != ((Size) 1 << (a_fidx + ALLOC_MINBITS)))
@@ -810,14 +717,14 @@ AllocSetAlloc(MemoryContext context, Size size)
                                chunk = (AllocChunk) (block->freeptr);
 
                                /* Prepare to initialize the chunk header. */
-                               VALGRIND_MAKE_MEM_UNDEFINED(chunk, ALLOC_CHUNK_USED);
+                               VALGRIND_MAKE_MEM_UNDEFINED(chunk, ALLOC_CHUNKHDRSZ);
 
                                block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ);
                                availspace -= (availchunk + ALLOC_CHUNKHDRSZ);
 
                                chunk->size = availchunk;
 #ifdef MEMORY_CONTEXT_CHECKING
-                               chunk->requested_size = 0;              /* mark it free */
+                               chunk->requested_size = 0;      /* mark it free */
 #endif
                                chunk->aset = (void *) set->freelist[a_fidx];
                                set->freelist[a_fidx] = chunk;
@@ -868,14 +775,7 @@ AllocSetAlloc(MemoryContext context, Size size)
                }
 
                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)));
-               }
+                       return NULL;
 
                block->aset = set;
                block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
@@ -897,7 +797,10 @@ AllocSetAlloc(MemoryContext context, Size size)
                VALGRIND_MAKE_MEM_NOACCESS(block->freeptr,
                                                                   blksize - ALLOC_BLOCKHDRSZ);
 
+               block->prev = NULL;
                block->next = set->blocks;
+               if (block->next)
+                       block->next->prev = block;
                set->blocks = block;
        }
 
@@ -907,7 +810,7 @@ AllocSetAlloc(MemoryContext context, Size size)
        chunk = (AllocChunk) (block->freeptr);
 
        /* Prepare to initialize the chunk header. */
-       VALGRIND_MAKE_MEM_UNDEFINED(chunk, ALLOC_CHUNK_USED);
+       VALGRIND_MAKE_MEM_UNDEFINED(chunk, ALLOC_CHUNKHDRSZ);
 
        block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
        Assert(block->freeptr <= block->endptr);
@@ -957,29 +860,28 @@ AllocSetFree(MemoryContext context, void *pointer)
        {
                /*
                 * Big chunks are certain to have been allocated as single-chunk
-                * blocks.      Find the containing block and return it to malloc().
+                * blocks.  Just unlink that block and return it to malloc().
                 */
-               AllocBlock      block = set->blocks;
-               AllocBlock      prevblock = NULL;
+               AllocBlock      block = (AllocBlock) (((char *) chunk) - ALLOC_BLOCKHDRSZ);
 
-               while (block != NULL)
-               {
-                       if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ))
-                               break;
-                       prevblock = block;
-                       block = block->next;
-               }
-               if (block == NULL)
+               /*
+                * Try to verify that we have a sane block pointer: it should
+                * reference the correct aset, and freeptr and endptr should point
+                * just past the chunk.
+                */
+               if (block->aset != set ||
+                       block->freeptr != block->endptr ||
+                       block->freeptr != ((char *) block) +
+                       (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ))
                        elog(ERROR, "could not find block containing chunk %p", chunk);
-               /* let's just make sure chunk is the only one in the block */
-               Assert(block->freeptr == ((char *) block) +
-                          (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
 
                /* OK, remove block from aset's list and free it */
-               if (prevblock == NULL)
-                       set->blocks = block->next;
+               if (block->prev)
+                       block->prev->next = block->next;
                else
-                       prevblock->next = block->next;
+                       set->blocks = block->next;
+               if (block->next)
+                       block->next->prev = block->prev;
 #ifdef CLOBBER_FREED_MEMORY
                wipe_mem(block, block->freeptr - ((char *) block));
 #endif
@@ -1006,9 +908,10 @@ AllocSetFree(MemoryContext context, void *pointer)
 
 /*
  * AllocSetRealloc
- *             Returns new pointer to allocated memory of given size; this memory
- *             is added to the set.  Memory associated with given pointer is copied
- *             into the new memory, and the old memory is freed.
+ *             Returns new pointer to allocated memory of given size or NULL if
+ *             request could not be completed; this memory is added to the set.
+ *             Memory associated with given pointer is copied into the new memory,
+ *             and the old memory is freed.
  *
  * Without MEMORY_CONTEXT_CHECKING, we don't know the old request size.  This
  * makes our Valgrind client requests less-precise, hazarding false negatives.
@@ -1084,50 +987,42 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
        if (oldsize > set->allocChunkLimit)
        {
                /*
-                * The chunk must have been allocated as a single-chunk block.  Find
-                * the containing block and use realloc() to make it bigger with
-                * minimum space wastage.
+                * The chunk must have been allocated as a single-chunk block.  Use
+                * realloc() to make the containing block bigger with minimum space
+                * wastage.
                 */
-               AllocBlock      block = set->blocks;
-               AllocBlock      prevblock = NULL;
+               AllocBlock      block = (AllocBlock) (((char *) chunk) - ALLOC_BLOCKHDRSZ);
                Size            chksize;
                Size            blksize;
 
-               while (block != NULL)
-               {
-                       if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ))
-                               break;
-                       prevblock = block;
-                       block = block->next;
-               }
-               if (block == NULL)
+               /*
+                * Try to verify that we have a sane block pointer: it should
+                * reference the correct aset, and freeptr and endptr should point
+                * just past the chunk.
+                */
+               if (block->aset != set ||
+                       block->freeptr != block->endptr ||
+                       block->freeptr != ((char *) block) +
+                       (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ))
                        elog(ERROR, "could not find block containing chunk %p", chunk);
-               /* let's just make sure chunk is the only one in the block */
-               Assert(block->freeptr == ((char *) block) +
-                          (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
 
                /* Do the realloc */
                chksize = MAXALIGN(size);
                blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
                block = (AllocBlock) realloc(block, 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)));
-               }
+                       return NULL;
                block->freeptr = block->endptr = ((char *) block) + blksize;
 
                /* Update pointers since block has likely been moved */
                chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);
                pointer = AllocChunkGetPointer(chunk);
-               if (prevblock == NULL)
-                       set->blocks = block;
+               if (block->prev)
+                       block->prev->next = block;
                else
-                       prevblock->next = block;
+                       set->blocks = block;
+               if (block->next)
+                       block->next->prev = block;
                chunk->size = chksize;
 
 #ifdef MEMORY_CONTEXT_CHECKING
@@ -1151,7 +1046,7 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
 
                /* set mark to catch clobber of "unused" space */
                if (size < chunk->size)
-                       set_sentinel(AllocChunkGetPointer(chunk), size);
+                       set_sentinel(pointer, size);
 #else                                                  /* !MEMORY_CONTEXT_CHECKING */
 
                /*
@@ -1164,7 +1059,8 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
 
                /* Make any trailing alignment padding NOACCESS. */
                VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size, chksize - size);
-               return AllocChunkGetPointer(chunk);
+
+               return pointer;
        }
        else
        {
@@ -1184,6 +1080,10 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
                /* allocate new chunk */
                newPointer = AllocSetAlloc((MemoryContext) set, size);
 
+               /* leave immediately if request was not completed */
+               if (newPointer == NULL)
+                       return NULL;
+
                /*
                 * AllocSetAlloc() just made the region NOACCESS.  Change it to
                 * UNDEFINED for the moment; memcpy() will then transfer definedness
@@ -1241,20 +1141,23 @@ AllocSetIsEmpty(MemoryContext context)
 
 /*
  * AllocSetStats
- *             Displays stats about memory consumption of an allocset.
+ *             Compute stats about memory consumption of an allocset.
+ *
+ * level: recursion level (0 at top level); used for print indentation.
+ * print: true to print stats to stderr.
+ * totals: if not NULL, add stats about this allocset into *totals.
  */
 static void
-AllocSetStats(MemoryContext context, int level)
+AllocSetStats(MemoryContext context, int level, bool print,
+                         MemoryContextCounters *totals)
 {
        AllocSet        set = (AllocSet) context;
-       long            nblocks = 0;
-       long            nchunks = 0;
-       long            totalspace = 0;
-       long            freespace = 0;
+       Size            nblocks = 0;
+       Size            freechunks = 0;
+       Size            totalspace = 0;
+       Size            freespace = 0;
        AllocBlock      block;
-       AllocChunk      chunk;
        int                     fidx;
-       int                     i;
 
        for (block = set->blocks; block != NULL; block = block->next)
        {
@@ -1264,21 +1167,35 @@ AllocSetStats(MemoryContext context, int level)
        }
        for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++)
        {
+               AllocChunk      chunk;
+
                for (chunk = set->freelist[fidx]; chunk != NULL;
                         chunk = (AllocChunk) chunk->aset)
                {
-                       nchunks++;
+                       freechunks++;
                        freespace += chunk->size + ALLOC_CHUNKHDRSZ;
                }
        }
 
-       for (i = 0; i < level; i++)
-               fprintf(stderr, "  ");
+       if (print)
+       {
+               int                     i;
+
+               for (i = 0; i < level; i++)
+                       fprintf(stderr, "  ");
+               fprintf(stderr,
+                               "%s: %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n",
+                               set->header.name, totalspace, nblocks, freespace, freechunks,
+                               totalspace - freespace);
+       }
 
-       fprintf(stderr,
-                       "%s: %lu total in %ld blocks; %lu free (%ld chunks); %lu used\n",
-                       set->header.name, totalspace, nblocks, freespace, nchunks,
-                       totalspace - freespace);
+       if (totals)
+       {
+               totals->nblocks += nblocks;
+               totals->freechunks += freechunks;
+               totals->totalspace += totalspace;
+               totals->freespace += freespace;
+       }
 }
 
 
@@ -1297,9 +1214,12 @@ AllocSetCheck(MemoryContext context)
 {
        AllocSet        set = (AllocSet) context;
        char       *name = set->header.name;
+       AllocBlock      prevblock;
        AllocBlock      block;
 
-       for (block = set->blocks; block != NULL; block = block->next)
+       for (prevblock = NULL, block = set->blocks;
+                block != NULL;
+                prevblock = block, block = block->next)
        {
                char       *bpoz = ((char *) block) + ALLOC_BLOCKHDRSZ;
                long            blk_used = block->freeptr - bpoz;
@@ -1316,6 +1236,16 @@ AllocSetCheck(MemoryContext context)
                                         name, block);
                }
 
+               /*
+                * Check block header fields
+                */
+               if (block->aset != set ||
+                       block->prev != prevblock ||
+                       block->freeptr < bpoz ||
+                       block->freeptr > block->endptr)
+                       elog(WARNING, "problem in alloc set %s: corrupt header in block %p",
+                                name, block);
+
                /*
                 * Chunk walker
                 */
@@ -1325,10 +1255,10 @@ AllocSetCheck(MemoryContext context)
                        Size            chsize,
                                                dsize;
 
-                       chsize = chunk->size;           /* aligned chunk size */
+                       chsize = chunk->size;   /* aligned chunk size */
                        VALGRIND_MAKE_MEM_DEFINED(&chunk->requested_size,
                                                                          sizeof(chunk->requested_size));
-                       dsize = chunk->requested_size;          /* real data */
+                       dsize = chunk->requested_size;  /* real data */
                        if (dsize > 0)          /* not on a free list */
                                VALGRIND_MAKE_MEM_NOACCESS(&chunk->requested_size,
                                                                                   sizeof(chunk->requested_size));
@@ -1340,8 +1270,8 @@ AllocSetCheck(MemoryContext context)
                                elog(WARNING, "problem in alloc set %s: req size > alloc size for chunk %p in block %p",
                                         name, chunk, block);
                        if (chsize < (1 << ALLOC_MINBITS))
-                               elog(WARNING, "problem in alloc set %s: bad size %lu for chunk %p in block %p",
-                                        name, (unsigned long) chsize, chunk, block);
+                               elog(WARNING, "problem in alloc set %s: bad size %zu for chunk %p in block %p",
+                                        name, chsize, chunk, block);
 
                        /* single-chunk block? */
                        if (chsize > set->allocChunkLimit &&
@@ -1378,4 +1308,4 @@ AllocSetCheck(MemoryContext context)
        }
 }
 
-#endif   /* MEMORY_CONTEXT_CHECKING */
+#endif                                                 /* MEMORY_CONTEXT_CHECKING */
index 0eae52012b2577ce5e26a40e9dc14cc91a9b6125..7eb3d0954da66f1125363da75f6ff95afc98b9c7 100644 (file)
@@ -9,7 +9,7 @@
  * context's MemoryContextMethods struct.
  *
  *
- * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
@@ -19,9 +19,6 @@
  *-------------------------------------------------------------------------
  */
 
-/* see palloc.h.  Must be before postgres.h */
-#define MCXT_INCLUDE_DEFINITIONS
-
 #include <stdint.h>
 #include <string.h>
 #include "pool_type.h"
@@ -52,13 +49,20 @@ MemoryContext ProcessLoopContext = NULL; /* This context resets at every main lo
 MemoryContext CacheMemoryContext = NULL;
 MemoryContext MessageContext = NULL;
 MemoryContext QueryContext = NULL;
-MemoryContext CurTransactionContext = NULL;
 
-/* This is a transient link to the active portal's memory context: */
-MemoryContext PortalContext = NULL;
 
-static void MemoryContextStatsInternal(MemoryContext context, int level);
+static void MemoryContextCallResetCallbacks(MemoryContext context);
+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                                                                                                              *
@@ -76,7 +80,8 @@ static void MemoryContextStatsInternal(MemoryContext context, int level);
  * In normal multi-backend operation, this is called once during
  * postmaster startup, and not at all by individual backend startup
  * (since the backends inherit an already-initialized context subsystem
- * by virtue of being forked off the postmaster).
+ * by virtue of being forked off the postmaster).  But in an EXEC_BACKEND
+ * build, each process must do this for itself.
  *
  * In a standalone backend this must be called during backend startup.
  */
@@ -86,16 +91,13 @@ MemoryContextInit(void)
        AssertState(TopMemoryContext == NULL);
 
        /*
-        * Initialize TopMemoryContext as an AllocSetContext with slow growth rate
-        * --- we don't really expect much to be allocated in it.
-        *
-        * (There is special-case code in MemoryContextCreate() for this call.)
+        * First, initialize TopMemoryContext, which will hold the MemoryContext
+        * nodes for all other contexts.  (There is special-case code in
+        * MemoryContextCreate() to handle this call.)
         */
        TopMemoryContext = AllocSetContextCreate((MemoryContext) NULL,
                                                                                         "TopMemoryContext",
-                                                                                        0,
-                                                                                        8 * 1024,
-                                                                                        8 * 1024);
+                                                                                        ALLOCSET_DEFAULT_SIZES);
 
        /*
         * Not having any other place to point CurrentMemoryContext, make it point
@@ -109,22 +111,25 @@ MemoryContextInit(void)
         * require it to contain at least 8K at all times. This is the only case
         * where retained memory in a context is *essential* --- we want to be
         * sure ErrorContext still has some memory even if we've run out
-        * elsewhere!
+        * elsewhere! Also, allow allocations in ErrorContext within a critical
+        * section. Otherwise a PANIC will cause an assertion failure in the error
+        * reporting code, before printing out the real cause of the failure.
+        *
+        * This should be the last step in this function, as elog.c assumes memory
+        * management works once ErrorContext is non-null.
         */
        ErrorContext = AllocSetContextCreate(TopMemoryContext,
                                                                                 "ErrorContext",
                                                                                 8 * 1024,
                                                                                 8 * 1024,
                                                                                 8 * 1024);
+       MemoryContextAllowInCriticalSection(ErrorContext, true);
 }
 
 /*
  * MemoryContextReset
- *             Release all space allocated within a context and its descendants,
- *             but don't delete the contexts themselves.
- *
- * The type-specific reset routine handles the context itself, but we
- * have to do the recursion for the children.
+ *             Release all space allocated within a context and delete all its
+ *             descendant contexts (but not the named context itself).
  */
 void
 MemoryContextReset(MemoryContext context)
@@ -133,11 +138,27 @@ MemoryContextReset(MemoryContext context)
 
        /* save a function call in common case where there are no children */
        if (context->firstchild != NULL)
-               MemoryContextResetChildren(context);
+               MemoryContextDeleteChildren(context);
+
+       /* save a function call if no pallocs since startup or last reset */
+       if (!context->isReset)
+               MemoryContextResetOnly(context);
+}
+
+/*
+ * MemoryContextResetOnly
+ *             Release all space allocated within a context.
+ *             Nothing is done to the context's descendant contexts.
+ */
+void
+MemoryContextResetOnly(MemoryContext context)
+{
+       AssertArg(MemoryContextIsValid(context));
 
        /* Nothing to do if no pallocs since startup or last reset */
        if (!context->isReset)
        {
+               MemoryContextCallResetCallbacks(context);
                (*context->methods->reset) (context);
                context->isReset = true;
                VALGRIND_DESTROY_MEMPOOL(context);
@@ -159,7 +180,10 @@ MemoryContextResetChildren(MemoryContext context)
        AssertArg(MemoryContextIsValid(context));
 
        for (child = context->firstchild; child != NULL; child = child->nextchild)
-               MemoryContextReset(child);
+       {
+               MemoryContextResetChildren(child);
+               MemoryContextResetOnly(child);
+       }
 }
 
 /*
@@ -169,7 +193,7 @@ MemoryContextResetChildren(MemoryContext context)
  *
  * The type-specific delete routine removes all subsidiary storage
  * for the context, but we have to delete the context node itself,
- * as well as recurse to get the children.     We must also delink the
+ * as well as recurse to get the children.  We must also delink the
  * node from its parent, if it has one.
  */
 void
@@ -183,6 +207,14 @@ MemoryContextDelete(MemoryContext context)
 
        MemoryContextDeleteChildren(context);
 
+       /*
+        * It's not entirely clear whether 'tis better to do this before or after
+        * delinking the context; but an error in a callback will likely result in
+        * leaking the whole context (if it's not a root context) if we do it
+        * after, so let's do it before.
+        */
+       MemoryContextCallResetCallbacks(context);
+
        /*
         * We delink the context from its parent before deleting it, so that if
         * there's an error we won't have deleted/busted contexts still attached
@@ -214,20 +246,53 @@ MemoryContextDeleteChildren(MemoryContext context)
 }
 
 /*
- * MemoryContextResetAndDeleteChildren
- *             Release all space allocated within a context and delete all
- *             its descendants.
+ * MemoryContextRegisterResetCallback
+ *             Register a function to be called before next context reset/delete.
+ *             Such callbacks will be called in reverse order of registration.
+ *
+ * The caller is responsible for allocating a MemoryContextCallback struct
+ * to hold the info about this callback request, and for filling in the
+ * "func" and "arg" fields in the struct to show what function to call with
+ * what argument.  Typically the callback struct should be allocated within
+ * the specified context, since that means it will automatically be freed
+ * when no longer needed.
  *
- * This is a common combination case where we want to preserve the
- * specific context but get rid of absolutely everything under it.
+ * There is no API for deregistering a callback once registered.  If you
+ * want it to not do anything anymore, adjust the state pointed to by its
+ * "arg" to indicate that.
  */
 void
-MemoryContextResetAndDeleteChildren(MemoryContext context)
+MemoryContextRegisterResetCallback(MemoryContext context,
+                                                                  MemoryContextCallback *cb)
 {
        AssertArg(MemoryContextIsValid(context));
 
-       MemoryContextDeleteChildren(context);
-       MemoryContextReset(context);
+       /* Push onto head so this will be called before older registrants. */
+       cb->next = context->reset_cbs;
+       context->reset_cbs = cb;
+       /* Mark the context as non-reset (it probably is already). */
+       context->isReset = false;
+}
+
+/*
+ * MemoryContextCallResetCallbacks
+ *             Internal function to call all registered callbacks for context.
+ */
+static void
+MemoryContextCallResetCallbacks(MemoryContext context)
+{
+       MemoryContextCallback *cb;
+
+       /*
+        * We pop each callback from the list before calling.  That way, if an
+        * error occurs inside the callback, we won't try to call it a second time
+        * in the likely event that we reset or delete the context later.
+        */
+       while ((cb = context->reset_cbs) != NULL)
+       {
+               context->reset_cbs = cb->next;
+               (*cb->func) (cb->arg);
+       }
 }
 
 /*
@@ -254,26 +319,25 @@ MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
        AssertArg(MemoryContextIsValid(context));
        AssertArg(context != new_parent);
 
+       /* Fast path if it's got correct parent already */
+       if (new_parent == context->parent)
+               return;
+
        /* Delink from existing parent, if any */
        if (context->parent)
        {
                MemoryContext parent = context->parent;
 
-               if (context == parent->firstchild)
-                       parent->firstchild = context->nextchild;
+               if (context->prevchild != NULL)
+                       context->prevchild->nextchild = context->nextchild;
                else
                {
-                       MemoryContext child;
-
-                       for (child = parent->firstchild; child; child = child->nextchild)
-                       {
-                               if (context == child->nextchild)
-                               {
-                                       child->nextchild = context->nextchild;
-                                       break;
-                               }
-                       }
+                       Assert(parent->firstchild == context);
+                       parent->firstchild = context->nextchild;
                }
+
+               if (context->nextchild != NULL)
+                       context->nextchild->prevchild = context->prevchild;
        }
 
        /* And relink */
@@ -281,16 +345,39 @@ MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
        {
                AssertArg(MemoryContextIsValid(new_parent));
                context->parent = new_parent;
+               context->prevchild = NULL;
                context->nextchild = new_parent->firstchild;
+               if (new_parent->firstchild != NULL)
+                       new_parent->firstchild->prevchild = context;
                new_parent->firstchild = context;
        }
        else
        {
                context->parent = NULL;
+               context->prevchild = NULL;
                context->nextchild = NULL;
        }
 }
 
+/*
+ * MemoryContextAllowInCriticalSection
+ *             Allow/disallow allocations in this memory context within a critical
+ *             section.
+ *
+ * Normally, memory allocations are not allowed within a critical section,
+ * because a failure would lead to PANIC.  There are a few exceptions to
+ * that, like allocations related to debugging code that is not supposed to
+ * be enabled in production.  This function can be used to exempt specific
+ * memory contexts from the assertion in palloc().
+ */
+void
+MemoryContextAllowInCriticalSection(MemoryContext context, bool allow)
+{
+       AssertArg(MemoryContextIsValid(context));
+
+       context->allowInCritSection = allow;
+}
+
 /*
  * GetMemoryChunkSpace
  *             Given a currently-allocated chunk, determine the total space
@@ -302,55 +389,10 @@ MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
 Size
 GetMemoryChunkSpace(void *pointer)
 {
-       StandardChunkHeader *header;
-
-       /*
-        * Try to detect bogus pointers handed to us, poorly though we can.
-        * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
-        * allocated chunk.
-        */
-       Assert(pointer != NULL);
-       Assert(pointer == (void *) MAXALIGN(pointer));
-
-       /*
-        * OK, it's probably safe to look at the chunk header.
-        */
-       header = (StandardChunkHeader *)
-               ((char *) pointer - STANDARDCHUNKHEADERSIZE);
-
-       AssertArg(MemoryContextIsValid(header->context));
-
-       return (*header->context->methods->get_chunk_space) (header->context,
-                                                                                                                pointer);
-}
-
-/*
- * GetMemoryChunkContext
- *             Given a currently-allocated chunk, determine the context
- *             it belongs to.
- */
-MemoryContext
-GetMemoryChunkContext(void *pointer)
-{
-       StandardChunkHeader *header;
+       MemoryContext context = GetMemoryChunkContext(pointer);
 
-       /*
-        * Try to detect bogus pointers handed to us, poorly though we can.
-        * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
-        * allocated chunk.
-        */
-       Assert(pointer != NULL);
-       Assert(pointer == (void *) MAXALIGN(pointer));
-
-       /*
-        * OK, it's probably safe to look at the chunk header.
-        */
-       header = (StandardChunkHeader *)
-               ((char *) pointer - STANDARDCHUNKHEADERSIZE);
-
-       AssertArg(MemoryContextIsValid(header->context));
-
-       return header->context;
+       return (context->methods->get_chunk_space) (context,
+                                                                                               pointer);
 }
 
 /*
@@ -388,25 +430,106 @@ MemoryContextIsEmpty(MemoryContext context)
  * MemoryContextStats
  *             Print statistics about the named context and all its descendants.
  *
- * This is just a debugging utility, so it's not fancy.  The statistics
- * are merely sent to stderr.
+ * This is just a debugging utility, so it's not very fancy.  However, we do
+ * make some effort to summarize when the output would otherwise be very long.
+ * The statistics are sent to stderr.
  */
 void
 MemoryContextStats(MemoryContext context)
 {
-       MemoryContextStatsInternal(context, 0);
+       /* A hard-wired limit on the number of children is usually good enough */
+       MemoryContextStatsDetail(context, 100);
+}
+
+/*
+ * MemoryContextStatsDetail
+ *
+ * Entry point for use if you want to vary the number of child contexts shown.
+ */
+void
+MemoryContextStatsDetail(MemoryContext context, int max_children)
+{
+       MemoryContextCounters grand_totals;
+
+       memset(&grand_totals, 0, sizeof(grand_totals));
+
+       MemoryContextStatsInternal(context, 0, true, max_children, &grand_totals);
+
+       fprintf(stderr,
+                       "Grand total: %zu bytes in %zd blocks; %zu free (%zd chunks); %zu used\n",
+                       grand_totals.totalspace, grand_totals.nblocks,
+                       grand_totals.freespace, grand_totals.freechunks,
+                       grand_totals.totalspace - grand_totals.freespace);
 }
 
+/*
+ * MemoryContextStatsInternal
+ *             One recursion level for MemoryContextStats
+ *
+ * Print this context if print is true, but in any case accumulate counts into
+ * *totals (if given).
+ */
 static void
-MemoryContextStatsInternal(MemoryContext context, int level)
+MemoryContextStatsInternal(MemoryContext context, int level,
+                                                  bool print, int max_children,
+                                                  MemoryContextCounters *totals)
 {
+       MemoryContextCounters local_totals;
        MemoryContext child;
+       int                     ichild;
 
        AssertArg(MemoryContextIsValid(context));
 
-       (*context->methods->stats) (context, level);
-       for (child = context->firstchild; child != NULL; child = child->nextchild)
-               MemoryContextStatsInternal(child, level + 1);
+       /* Examine the context itself */
+       (*context->methods->stats) (context, level, print, totals);
+
+       /*
+        * Examine children.  If there are more than max_children of them, we do
+        * not print the rest explicitly, but just summarize them.
+        */
+       memset(&local_totals, 0, sizeof(local_totals));
+
+       for (child = context->firstchild, ichild = 0;
+                child != NULL;
+                child = child->nextchild, ichild++)
+       {
+               if (ichild < max_children)
+                       MemoryContextStatsInternal(child, level + 1,
+                                                                          print, max_children,
+                                                                          totals);
+               else
+                       MemoryContextStatsInternal(child, level + 1,
+                                                                          false, max_children,
+                                                                          &local_totals);
+       }
+
+       /* Deal with excess children */
+       if (ichild > max_children)
+       {
+               if (print)
+               {
+                       int                     i;
+
+                       for (i = 0; i <= level; i++)
+                               fprintf(stderr, "  ");
+                       fprintf(stderr,
+                                       "%d more child contexts containing %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n",
+                                       ichild - max_children,
+                                       local_totals.totalspace,
+                                       local_totals.nblocks,
+                                       local_totals.freespace,
+                                       local_totals.freechunks,
+                                       local_totals.totalspace - local_totals.freespace);
+               }
+
+               if (totals)
+               {
+                       totals->nblocks += local_totals.nblocks;
+                       totals->freechunks += local_totals.freechunks;
+                       totals->totalspace += local_totals.totalspace;
+                       totals->freespace += local_totals.freespace;
+               }
+       }
 }
 
 /*
@@ -443,9 +566,13 @@ MemoryContextCheck(MemoryContext context)
 bool
 MemoryContextContains(MemoryContext context, void *pointer)
 {
-       StandardChunkHeader *header;
+       MemoryContext ptr_context;
 
        /*
+        * NB: Can't use GetMemoryChunkContext() here - that performs assertions
+        * that aren't acceptable here since we might be passed memory not
+        * allocated by any memory context.
+        *
         * Try to detect bogus pointers handed to us, poorly though we can.
         * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
         * allocated chunk.
@@ -454,12 +581,11 @@ MemoryContextContains(MemoryContext context, void *pointer)
                return false;
 
        /*
-        * OK, it's probably safe to look at the chunk header.
+        * OK, it's probably safe to look at the context.
         */
-       header = (StandardChunkHeader *)
-               ((char *) pointer - STANDARDCHUNKHEADERSIZE);
+       ptr_context = *(MemoryContext *) (((char *) pointer) - sizeof(void *));
 
-       return header->context == context;
+       return ptr_context == context;
 }
 
 /*--------------------
@@ -473,22 +599,22 @@ MemoryContextContains(MemoryContext context, void *pointer)
  * we want to be sure that we don't leave the context tree invalid
  * in case of failure (such as insufficient memory to allocate the
  * context node itself).  The procedure goes like this:
- *     1.      Context-type-specific routine first calls MemoryContextCreate(),
+ *     1.  Context-type-specific routine first calls MemoryContextCreate(),
  *             passing the appropriate tag/size/methods values (the methods
  *             pointer will ordinarily point to statically allocated data).
  *             The parent and name parameters usually come from the caller.
- *     2.      MemoryContextCreate() attempts to allocate the context node,
+ *     2.  MemoryContextCreate() attempts to allocate the context node,
  *             plus space for the name.  If this fails we can ereport() with no
  *             damage done.
- *     3.      We fill in all of the type-independent MemoryContext fields.
- *     4.      We call the type-specific init routine (using the methods pointer).
+ *     3.  We fill in all of the type-independent MemoryContext fields.
+ *     4.  We call the type-specific init routine (using the methods pointer).
  *             The init routine is required to make the node minimally valid
  *             with zero chance of failure --- it can't allocate more memory,
  *             for example.
- *     5.      Now we have a minimally valid node that can behave correctly
+ *     5.  Now we have a minimally valid node that can behave correctly
  *             when told to reset or delete itself.  We link the node to its
  *             parent (if any), making the node part of the context tree.
- *     6.      We return to the context-type-specific routine, which finishes
+ *     6.  We return to the context-type-specific routine, which finishes
  *             up type-specific initialization.  This routine can now do things
  *             that might fail (like allocate more memory), so long as it's
  *             sure the node is left in a state that delete will handle.
@@ -500,7 +626,7 @@ MemoryContextContains(MemoryContext context, void *pointer)
  *
  * Normally, the context node and the name are allocated from
  * TopMemoryContext (NOT from the parent context, since the node must
- * survive resets of its parent context!).     However, this routine is itself
+ * survive resets of its parent context!).  However, this routine is itself
  * used to create TopMemoryContext!  If we see that TopMemoryContext is NULL,
  * we assume we are creating TopMemoryContext and use malloc() to allocate
  * the node.
@@ -519,6 +645,9 @@ MemoryContextCreate(NodeTag tag, Size size,
        MemoryContext node;
        Size            needed = size + strlen(name) + 1;
 
+       /* creating new memory contexts is not allowed in a critical section */
+       Assert(CritSectionCount == 0);
+
        /* Get space for node and name */
        if (TopMemoryContext != NULL)
        {
@@ -539,6 +668,7 @@ MemoryContextCreate(NodeTag tag, Size size,
        node->methods = methods;
        node->parent = NULL;            /* for the moment */
        node->firstchild = NULL;
+       node->prevchild = NULL;
        node->nextchild = NULL;
        node->isReset = true;
        node->name = ((char *) node) + size;
@@ -553,7 +683,11 @@ MemoryContextCreate(NodeTag tag, Size size,
        {
                node->parent = parent;
                node->nextchild = parent->firstchild;
+               if (parent->firstchild != NULL)
+                       parent->firstchild->prevchild = node;
                parent->firstchild = node;
+               /* inherit allowInCritSection flag from parent */
+               node->allowInCritSection = parent->allowInCritSection;
        }
 
        VALGRIND_CREATE_MEMPOOL(node, 0, false);
@@ -575,14 +709,23 @@ MemoryContextAlloc(MemoryContext context, Size size)
        void       *ret;
 
        AssertArg(MemoryContextIsValid(context));
+       AssertNotInCriticalSection(context);
 
        if (!AllocSizeIsValid(size))
-               elog(ERROR, "invalid memory alloc request size %lu",
-                        (unsigned long) size);
+               elog(ERROR, "invalid memory alloc request size %zu", size);
 
        context->isReset = false;
 
        ret = (*context->methods->alloc) (context, size);
+       if (ret == NULL)
+       {
+               MemoryContextStats(TopMemoryContext);
+               ereport(ERROR,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("out of memory"),
+                                errdetail("Failed on request of size %zu.", size)));
+       }
+
        VALGRIND_MEMPOOL_ALLOC(context, ret, size);
 
        return ret;
@@ -601,14 +744,23 @@ MemoryContextAllocZero(MemoryContext context, Size size)
        void       *ret;
 
        AssertArg(MemoryContextIsValid(context));
+       AssertNotInCriticalSection(context);
 
        if (!AllocSizeIsValid(size))
-               elog(ERROR, "invalid memory alloc request size %lu",
-                        (unsigned long) size);
+               elog(ERROR, "invalid memory alloc request size %zu", size);
 
        context->isReset = false;
 
        ret = (*context->methods->alloc) (context, size);
+       if (ret == NULL)
+       {
+               MemoryContextStats(TopMemoryContext);
+               ereport(ERROR,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("out of memory"),
+                                errdetail("Failed on request of size %zu.", size)));
+       }
+
        VALGRIND_MEMPOOL_ALLOC(context, ret, size);
 
        MemSetAligned(ret, 0, size);
@@ -629,14 +781,23 @@ MemoryContextAllocZeroAligned(MemoryContext context, Size size)
        void       *ret;
 
        AssertArg(MemoryContextIsValid(context));
+       AssertNotInCriticalSection(context);
 
        if (!AllocSizeIsValid(size))
-               elog(ERROR, "invalid memory alloc request size %lu",
-                        (unsigned long) size);
+               elog(ERROR, "invalid memory alloc request size %zu", size);
 
        context->isReset = false;
 
        ret = (*context->methods->alloc) (context, size);
+       if (ret == NULL)
+       {
+               MemoryContextStats(TopMemoryContext);
+               ereport(ERROR,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("out of memory"),
+                                errdetail("Failed on request of size %zu.", size)));
+       }
+
        VALGRIND_MEMPOOL_ALLOC(context, ret, size);
 
        MemSetLoop(ret, 0, size);
@@ -644,6 +805,46 @@ MemoryContextAllocZeroAligned(MemoryContext context, Size size)
        return ret;
 }
 
+/*
+ * MemoryContextAllocExtended
+ *             Allocate space within the specified context using the given flags.
+ */
+void *
+MemoryContextAllocExtended(MemoryContext context, Size size, int flags)
+{
+       void       *ret;
+
+       AssertArg(MemoryContextIsValid(context));
+       AssertNotInCriticalSection(context);
+
+       if (((flags & MCXT_ALLOC_HUGE) != 0 && !AllocHugeSizeIsValid(size)) ||
+               ((flags & MCXT_ALLOC_HUGE) == 0 && !AllocSizeIsValid(size)))
+               elog(ERROR, "invalid memory alloc request size %zu", size);
+
+       context->isReset = false;
+
+       ret = (*context->methods->alloc) (context, size);
+       if (ret == NULL)
+       {
+               if ((flags & MCXT_ALLOC_NO_OOM) == 0)
+               {
+                       MemoryContextStats(TopMemoryContext);
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_OUT_OF_MEMORY),
+                                        errmsg("out of memory"),
+                                        errdetail("Failed on request of size %zu.", size)));
+               }
+               return NULL;
+       }
+
+       VALGRIND_MEMPOOL_ALLOC(context, ret, size);
+
+       if ((flags & MCXT_ALLOC_ZERO) != 0)
+               MemSetAligned(ret, 0, size);
+
+       return ret;
+}
+
 void *
 palloc(Size size)
 {
@@ -651,14 +852,23 @@ palloc(Size size)
        void       *ret;
 
        AssertArg(MemoryContextIsValid(CurrentMemoryContext));
+       AssertNotInCriticalSection(CurrentMemoryContext);
 
        if (!AllocSizeIsValid(size))
-               elog(ERROR, "invalid memory alloc request size %lu",
-                        (unsigned long) size);
+               elog(ERROR, "invalid memory alloc request size %zu", size);
 
        CurrentMemoryContext->isReset = false;
 
        ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
+       if (ret == NULL)
+       {
+               MemoryContextStats(TopMemoryContext);
+               ereport(ERROR,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("out of memory"),
+                                errdetail("Failed on request of size %zu.", size)));
+       }
+
        VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size);
 
        return ret;
@@ -671,14 +881,23 @@ palloc0(Size size)
        void       *ret;
 
        AssertArg(MemoryContextIsValid(CurrentMemoryContext));
+       AssertNotInCriticalSection(CurrentMemoryContext);
 
        if (!AllocSizeIsValid(size))
-               elog(ERROR, "invalid memory alloc request size %lu",
-                        (unsigned long) size);
+               elog(ERROR, "invalid memory alloc request size %zu", size);
 
        CurrentMemoryContext->isReset = false;
 
        ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
+       if (ret == NULL)
+       {
+               MemoryContextStats(TopMemoryContext);
+               ereport(ERROR,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("out of memory"),
+                                errdetail("Failed on request of size %zu.", size)));
+       }
+
        VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size);
 
        MemSetAligned(ret, 0, size);
@@ -686,6 +905,43 @@ palloc0(Size size)
        return ret;
 }
 
+void *
+palloc_extended(Size size, int flags)
+{
+       /* duplicates MemoryContextAllocExtended to avoid increased overhead */
+       void       *ret;
+
+       AssertArg(MemoryContextIsValid(CurrentMemoryContext));
+       AssertNotInCriticalSection(CurrentMemoryContext);
+
+       if (((flags & MCXT_ALLOC_HUGE) != 0 && !AllocHugeSizeIsValid(size)) ||
+               ((flags & MCXT_ALLOC_HUGE) == 0 && !AllocSizeIsValid(size)))
+               elog(ERROR, "invalid memory alloc request size %zu", size);
+
+       CurrentMemoryContext->isReset = false;
+
+       ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
+       if (ret == NULL)
+       {
+               if ((flags & MCXT_ALLOC_NO_OOM) == 0)
+               {
+                       MemoryContextStats(TopMemoryContext);
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_OUT_OF_MEMORY),
+                                        errmsg("out of memory"),
+                                        errdetail("Failed on request of size %zu.", size)));
+               }
+               return NULL;
+       }
+
+       VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size);
+
+       if ((flags & MCXT_ALLOC_ZERO) != 0)
+               MemSetAligned(ret, 0, size);
+
+       return ret;
+}
+
 /*
  * pfree
  *             Release an allocated chunk.
@@ -693,26 +949,7 @@ palloc0(Size size)
 void
 pfree(void *pointer)
 {
-       MemoryContext context;
-
-       /*
-        * Try to detect bogus pointers handed to us, poorly though we can.
-        * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
-        * allocated chunk.
-        */
-       Assert(pointer != NULL);
-       Assert(pointer == (void *) MAXALIGN(pointer));
-
-       if (pointer == NULL)
-               return;
-
-       /*
-        * OK, it's probably safe to look at the chunk header.
-        */
-       context = ((StandardChunkHeader *)
-                          ((char *) pointer - STANDARDCHUNKHEADERSIZE))->context;
-
-       AssertArg(MemoryContextIsValid(context));
+       MemoryContext context = GetMemoryChunkContext(pointer);
 
        (*context->methods->free_p) (context, pointer);
        VALGRIND_MEMPOOL_FREE(context, pointer);
@@ -729,31 +966,29 @@ repalloc(void *pointer, Size size)
        void       *ret;
 
        if (!AllocSizeIsValid(size))
-               elog(ERROR, "invalid memory alloc request size %lu",
-                        (unsigned long) size);
-    /* pgpool hack by usama */
-    if(pointer == NULL)
-        return palloc(size);
-       /*
-        * Try to detect bogus pointers handed to us, poorly though we can.
-        * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
-        * allocated chunk.
-        */
-       Assert(pointer != NULL);
-       Assert(pointer == (void *) MAXALIGN(pointer));
+               elog(ERROR, "invalid memory alloc request size %zu", size);
 
-       /*
-        * OK, it's probably safe to look at the chunk header.
-        */
-       context = ((StandardChunkHeader *)
-                          ((char *) pointer - STANDARDCHUNKHEADERSIZE))->context;
+       /* pgpool hack by Muhammad Usama <m.usama@gmail.com> */
+       if(pointer == NULL)
+               return palloc(size);
 
-       AssertArg(MemoryContextIsValid(context));
+       context = GetMemoryChunkContext(pointer);
+
+       AssertNotInCriticalSection(context);
 
        /* isReset must be false already */
        Assert(!context->isReset);
 
        ret = (*context->methods->realloc) (context, pointer, size);
+       if (ret == NULL)
+       {
+               MemoryContextStats(TopMemoryContext);
+               ereport(ERROR,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("out of memory"),
+                                errdetail("Failed on request of size %zu.", size)));
+       }
+
        VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
 
        return ret;
@@ -771,14 +1006,23 @@ MemoryContextAllocHuge(MemoryContext context, Size size)
        void       *ret;
 
        AssertArg(MemoryContextIsValid(context));
+       AssertNotInCriticalSection(context);
 
        if (!AllocHugeSizeIsValid(size))
-               elog(ERROR, "invalid memory alloc request size %lu",
-                        (unsigned long) size);
+               elog(ERROR, "invalid memory alloc request size %zu", size);
 
        context->isReset = false;
 
        ret = (*context->methods->alloc) (context, size);
+       if (ret == NULL)
+       {
+               MemoryContextStats(TopMemoryContext);
+               ereport(ERROR,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("out of memory"),
+                                errdetail("Failed on request of size %zu.", size)));
+       }
+
        VALGRIND_MEMPOOL_ALLOC(context, ret, size);
 
        return ret;
@@ -792,33 +1036,27 @@ MemoryContextAllocHuge(MemoryContext context, Size size)
 void *
 repalloc_huge(void *pointer, Size size)
 {
-       MemoryContext context;
+       MemoryContext context = GetMemoryChunkContext(pointer);
        void       *ret;
 
        if (!AllocHugeSizeIsValid(size))
-               elog(ERROR, "invalid memory alloc request size %lu",
-                        (unsigned long) size);
-
-       /*
-        * Try to detect bogus pointers handed to us, poorly though we can.
-        * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
-        * allocated chunk.
-        */
-       Assert(pointer != NULL);
-       Assert(pointer == (void *) MAXALIGN(pointer));
-
-       /*
-        * OK, it's probably safe to look at the chunk header.
-        */
-       context = ((StandardChunkHeader *)
-                          ((char *) pointer - STANDARDCHUNKHEADERSIZE))->context;
+               elog(ERROR, "invalid memory alloc request size %zu", size);
 
-       AssertArg(MemoryContextIsValid(context));
+       AssertNotInCriticalSection(context);
 
        /* isReset must be false already */
        Assert(!context->isReset);
 
        ret = (*context->methods->realloc) (context, pointer, size);
+       if (ret == NULL)
+       {
+               MemoryContextStats(TopMemoryContext);
+               ereport(ERROR,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("out of memory"),
+                                errdetail("Failed on request of size %zu.", size)));
+       }
+
        VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
 
        return ret;
@@ -861,3 +1099,17 @@ pnstrdup(const char *in, Size len)
        out[len] = '\0';
        return out;
 }
+
+/*
+ * Make copy of string with all trailing newline characters removed.
+ */
+char *
+pchomp(const char *in)
+{
+       size_t          n;
+
+       n = strlen(in);
+       while (n > 0 && in[n - 1] == '\n')
+               n--;
+       return pnstrdup(in, n);
+}