#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,
#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
#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 */
* 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
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 */
*/
#define MemoryContextIsValid(context) \
((context) != NULL && \
- (IsA((context), AllocSetContext)))
+ (IsA((context), AllocSetContext) || IsA((context), SlabContext)))
-#endif /* MEMNODES_H */
+#endif /* MEMNODES_H */
* 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
* 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.
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)
/*
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-
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.
#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 */
* 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
* 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
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;
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 */
* 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.
- *
*-------------------------------------------------------------------------
*/
#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;
/*
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 */
/*
* 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
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
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);
*/
#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)
/*
* 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
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
* 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,
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
* 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);
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;
}
/*
* 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.
*/
VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
#endif
block->freeptr = datastart;
+ block->prev = NULL;
block->next = NULL;
}
else
/*
* 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
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;
#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;
}
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);
}
/*
* 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)))
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;
}
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;
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;
}
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);
{
/*
* 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
/*
* 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.
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
/* 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 */
/*
/* Make any trailing alignment padding NOACCESS. */
VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size, chksize - size);
- return AllocChunkGetPointer(chunk);
+
+ return pointer;
}
else
{
/* 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
/*
* 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)
{
}
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;
+ }
}
{
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;
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
*/
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));
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 &&
}
}
-#endif /* MEMORY_CONTEXT_CHECKING */
+#endif /* MEMORY_CONTEXT_CHECKING */
* 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
*
*
*-------------------------------------------------------------------------
*/
-/* see palloc.h. Must be before postgres.h */
-#define MCXT_INCLUDE_DEFINITIONS
-
#include <stdint.h>
#include <string.h>
#include "pool_type.h"
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 *
* 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.
*/
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
* 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)
/* 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);
AssertArg(MemoryContextIsValid(context));
for (child = context->firstchild; child != NULL; child = child->nextchild)
- MemoryContextReset(child);
+ {
+ MemoryContextResetChildren(child);
+ MemoryContextResetOnly(child);
+ }
}
/*
*
* 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
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
}
/*
- * 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);
+ }
}
/*
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 */
{
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
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);
}
/*
* 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;
+ }
+ }
}
/*
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.
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;
}
/*--------------------
* 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.
*
* 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.
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)
{
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;
{
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);
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;
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);
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);
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)
{
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;
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);
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.
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);
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;
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;
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;
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);
+}