On Tue, Jan 27, 2015 at 5:34 PM, Andres Freund <and...@2ndquadrant.com> wrote:
> On 2015-01-27 17:27:53 +0900, Michael Paquier wrote:
>> Alvaro Herrera wrote:
>> >> So how about something like
>> >>
>> >> #define ALLOCFLAG_HUGE                      0x01
>> >> #define ALLOCFLAG_NO_ERROR_ON_OOM   0x02
>> >> void *
>> >> MemoryContextAllocFlags(MemoryContext context, Size size, int flags);
>> The flag for huge allocations may be useful, but I don't actually see
>> much value in the flag ALLOC_NO_OOM if the stuff in aset.c returns
>> unconditionally NULL in case of an OOM and we let palloc complain
>> about an OOM when allocation returns NULL. Something I am missing
>> perhaps?
>
> I guess the idea is to have *user facing* MemoryContextAllocExtended()
> that can do both huge and no-oom allocations. Otherwise we need palloc
> like wrappers for all combinations.
> We're certainly not just going to ignore memory allocation failures
> generally in in MemoryContextAllocExtended()....
As a result of all the comments on this thread, here are 3 patches
implementing incrementally the different ideas from everybody:
1) 0001 modifies aset.c to return unconditionally NULL in case of an
OOM instead of reporting an error. All the OOM error reports are moved
to mcxt.c (MemoryContextAlloc* and palloc*)
2) 0002 adds the noerror routines for frontend and backend.
3) 0003 adds MemoryContextAllocExtended that can be called with the
following control flags:
#define ALLOC_HUGE             0x01    /* huge allocation */
#define ALLOC_ZERO             0x02    /* clear allocated memory */
#define ALLOC_NO_OOM   0x04    /* no failure if out-of-memory */
#define ALLOC_ALIGNED  0x08    /* request length suitable for MemSetLoop */
This groups MemoryContextAlloc, MemoryContextAllocHuge,
MemoryContextAllocZero and MemoryContextAllocZeroAligned under the
same central routine.
Regards,
-- 
Michael
From 337c439554ce66486cf9d29dced6c72d034b8f8d Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@otacoo.com>
Date: Wed, 28 Jan 2015 22:10:13 +0900
Subject: [PATCH 1/3] Make allocation return functions return NULL on OOM

On counterpart, higher-level APIs in mcxt.c return an explicit error
message when memory requests cannot be completed.
---
 src/backend/utils/mmgr/aset.c | 30 ++++++++++++---------------
 src/backend/utils/mmgr/mcxt.c | 48 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 17 deletions(-)

diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index 85b3c9a..bf6f09a 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -642,8 +642,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,10 +673,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 %zu.", size)));
+			return NULL;
 		}
 		block->aset = set;
 		block->freeptr = block->endptr = ((char *) block) + blksize;
@@ -867,10 +864,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 %zu.", size)));
+			return NULL;
 		}
 
 		block->aset = set;
@@ -1002,9 +996,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.
@@ -1109,10 +1104,7 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
 		if (block == NULL)
 		{
 			MemoryContextStats(TopMemoryContext);
-			ereport(ERROR,
-					(errcode(ERRCODE_OUT_OF_MEMORY),
-					 errmsg("out of memory"),
-					 errdetail("Failed on request of size %zu.", size)));
+			return NULL;
 		}
 		block->freeptr = block->endptr = ((char *) block) + blksize;
 
@@ -1179,6 +1171,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
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index aa0d458..f6cf0cc 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -623,6 +623,12 @@ MemoryContextAlloc(MemoryContext context, Size size)
 	context->isReset = false;
 
 	ret = (*context->methods->alloc) (context, size);
+	if (ret == NULL)
+		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;
@@ -649,6 +655,12 @@ MemoryContextAllocZero(MemoryContext context, Size size)
 	context->isReset = false;
 
 	ret = (*context->methods->alloc) (context, size);
+	if (ret == NULL)
+		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);
@@ -677,6 +689,12 @@ MemoryContextAllocZeroAligned(MemoryContext context, Size size)
 	context->isReset = false;
 
 	ret = (*context->methods->alloc) (context, size);
+	if (ret == NULL)
+		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);
@@ -699,6 +717,12 @@ palloc(Size size)
 	CurrentMemoryContext->isReset = false;
 
 	ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
+	if (ret == NULL)
+		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;
@@ -719,6 +743,12 @@ palloc0(Size size)
 	CurrentMemoryContext->isReset = false;
 
 	ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
+	if (ret == NULL)
+		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);
@@ -789,6 +819,12 @@ repalloc(void *pointer, Size size)
 	Assert(!context->isReset);
 
 	ret = (*context->methods->realloc) (context, pointer, size);
+	if (ret == NULL)
+		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;
@@ -814,6 +850,12 @@ MemoryContextAllocHuge(MemoryContext context, Size size)
 	context->isReset = false;
 
 	ret = (*context->methods->alloc) (context, size);
+	if (ret == NULL)
+		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;
@@ -854,6 +896,12 @@ repalloc_huge(void *pointer, Size size)
 	Assert(!context->isReset);
 
 	ret = (*context->methods->realloc) (context, pointer, size);
+	if (ret == NULL)
+		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;
-- 
2.2.2

From 13a05f5b28ab00864a9d96fae5a72402cf43184b Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@otacoo.com>
Date: Wed, 28 Jan 2015 22:46:04 +0900
Subject: [PATCH 2/3] Add *_noerror routines for palloc memory allocation

The following routines are added for frontend and backend:
- palloc_noerror, equivalent of palloc but returning NULL if memory request
could not be completed
- palloc0_noerror, equivalent of palloc0 but returning NULL if memory
request could not be completed
- repalloc_noerror, equivalent of repalloc bur returning NULL if memory
request could not be completed
---
 src/backend/utils/mmgr/mcxt.c    | 129 ++++++++++++++++++++++++---------------
 src/common/fe_memutils.c         |  73 ++++++++++++++++------
 src/include/common/fe_memutils.h |   3 +
 src/include/utils/palloc.h       |   3 +
 4 files changed, 140 insertions(+), 68 deletions(-)

diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index f6cf0cc..f8907a8 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -56,6 +56,10 @@ MemoryContext PortalContext = NULL;
 
 static void MemoryContextStatsInternal(MemoryContext context, int level);
 
+/* wrapper routines for allocation */
+static inline void* palloc_internal(Size size, bool noerror);
+static inline void* repalloc_internal(void *pointer, Size size, bool noerror);
+
 /*
  * 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
@@ -702,8 +706,8 @@ MemoryContextAllocZeroAligned(MemoryContext context, Size size)
 	return ret;
 }
 
-void *
-palloc(Size size)
+static inline void *
+palloc_internal(Size size, bool noerror)
 {
 	/* duplicates MemoryContextAlloc to avoid increased overhead */
 	void	   *ret;
@@ -717,42 +721,90 @@ palloc(Size size)
 	CurrentMemoryContext->isReset = false;
 
 	ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
-	if (ret == NULL)
+	if (!noerror && ret == NULL)
 		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);
+	if (ret != NULL)
+		VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size);
 
 	return ret;
 }
 
-void *
-palloc0(Size size)
+static inline void *
+repalloc_internal(void *pointer, Size size, bool noerror)
 {
-	/* duplicates MemoryContextAllocZero to avoid increased overhead */
+	MemoryContext context;
 	void	   *ret;
 
-	AssertArg(MemoryContextIsValid(CurrentMemoryContext));
-	AssertNotInCriticalSection(CurrentMemoryContext);
-
 	if (!AllocSizeIsValid(size))
 		elog(ERROR, "invalid memory alloc request size %zu", size);
 
-	CurrentMemoryContext->isReset = false;
+	/*
+	 * 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));
 
-	ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
-	if (ret == NULL)
+	/*
+	 * OK, it's probably safe to look at the chunk header.
+	 */
+	context = ((StandardChunkHeader *)
+			   ((char *) pointer - STANDARDCHUNKHEADERSIZE))->context;
+
+	AssertArg(MemoryContextIsValid(context));
+	AssertNotInCriticalSection(context);
+
+	/* isReset must be false already */
+	Assert(!context->isReset);
+
+	ret = (*context->methods->realloc) (context, pointer, size);
+	if (!noerror && ret == NULL)
 		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);
+	if (ret != NULL)
+		VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
+
+	return ret;
+
+}
+
+void *
+palloc(Size size)
+{
+	return palloc_internal(size, false);
+}
+
+void *
+palloc_noerror(Size size)
+{
+	return palloc_internal(size, true);
+}
+
+void *
+palloc0(Size size)
+{
+	void	   *ret;
 
+	ret = palloc_internal(size, false);
 	MemSetAligned(ret, 0, size);
+	return ret;
+}
 
+void *
+palloc0_noerror(Size size)
+{
+	void	   *ret;
+
+	ret = palloc_internal(size, true);
+	MemSetAligned(ret, 0, size);
 	return ret;
 }
 
@@ -787,47 +839,24 @@ pfree(void *pointer)
 
 /*
  * repalloc
- *		Adjust the size of a previously allocated chunk.
+ *		Adjust the size of a previously allocated chunk, failing if
+ *		request could not be completed.
  */
 void *
 repalloc(void *pointer, Size size)
 {
-	MemoryContext context;
-	void	   *ret;
-
-	if (!AllocSizeIsValid(size))
-		elog(ERROR, "invalid memory alloc request size %zu", 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;
-
-	AssertArg(MemoryContextIsValid(context));
-	AssertNotInCriticalSection(context);
-
-	/* isReset must be false already */
-	Assert(!context->isReset);
-
-	ret = (*context->methods->realloc) (context, pointer, size);
-	if (ret == NULL)
-		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 repalloc_internal(pointer, size, false);
+}
 
-	return ret;
+/*
+ * repalloc_noerror
+ *		Adjust the size of a previously allocated chunk, returning NULL
+ *		if request could not be completed.
+ */
+void *
+repalloc_noerror(void *pointer, Size size)
+{
+	return repalloc_internal(pointer, size, true);
 }
 
 /*
diff --git a/src/common/fe_memutils.c b/src/common/fe_memutils.c
index 345221e..9a312c4 100644
--- a/src/common/fe_memutils.c
+++ b/src/common/fe_memutils.c
@@ -19,8 +19,8 @@
 
 #include "postgres_fe.h"
 
-void *
-pg_malloc(size_t size)
+static inline void *
+pg_malloc_internal(size_t size, bool noerror)
 {
 	void	   *tmp;
 
@@ -28,7 +28,24 @@ pg_malloc(size_t size)
 	if (size == 0)
 		size = 1;
 	tmp = malloc(size);
-	if (!tmp)
+	if (!tmp && !noerror)
+	{
+		fprintf(stderr, _("out of memory\n"));
+		exit(EXIT_FAILURE);
+	}
+	return tmp;
+}
+
+static inline void *
+pg_realloc_internal(void *ptr, size_t size, bool noerror)
+{
+	void	   *tmp;
+
+	/* Avoid unportable behavior of realloc(NULL, 0) */
+	if (ptr == NULL && size == 0)
+		size = 1;
+	tmp = realloc(ptr, size);
+	if (!tmp && !noerror)
 	{
 		fprintf(stderr, _("out of memory\n"));
 		exit(EXIT_FAILURE);
@@ -37,6 +54,12 @@ pg_malloc(size_t size)
 }
 
 void *
+pg_malloc(size_t size)
+{
+	return pg_malloc_internal(size, false);
+}
+
+void *
 pg_malloc0(size_t size)
 {
 	void	   *tmp;
@@ -49,20 +72,10 @@ pg_malloc0(size_t size)
 void *
 pg_realloc(void *ptr, size_t size)
 {
-	void	   *tmp;
-
-	/* Avoid unportable behavior of realloc(NULL, 0) */
-	if (ptr == NULL && size == 0)
-		size = 1;
-	tmp = realloc(ptr, size);
-	if (!tmp)
-	{
-		fprintf(stderr, _("out of memory\n"));
-		exit(EXIT_FAILURE);
-	}
-	return tmp;
+	return pg_realloc_internal(ptr, size, false);
 }
 
+
 /*
  * "Safe" wrapper around strdup().
  */
@@ -100,13 +113,31 @@ pg_free(void *ptr)
 void *
 palloc(Size size)
 {
-	return pg_malloc(size);
+	return pg_malloc_internal(size, false);
+}
+
+void *
+palloc_noerror(Size size)
+{
+	return pg_malloc_internal(size, true);
 }
 
 void *
 palloc0(Size size)
 {
-	return pg_malloc0(size);
+	void	   *tmp;
+	tmp = pg_malloc_internal(size, false);
+	MemSet(tmp, 0, size);
+	return tmp;
+}
+
+void *
+palloc0_noerror(Size size)
+{
+	void	   *tmp;
+	tmp = pg_malloc_internal(size, true);
+	MemSet(tmp, 0, size);
+	return tmp;
 }
 
 void
@@ -124,5 +155,11 @@ pstrdup(const char *in)
 void *
 repalloc(void *pointer, Size size)
 {
-	return pg_realloc(pointer, size);
+	return pg_realloc_internal(pointer, size, false);
+}
+
+void *
+repalloc_noerror(void *pointer, Size size)
+{
+	return pg_realloc_internal(pointer, size, true);
 }
diff --git a/src/include/common/fe_memutils.h b/src/include/common/fe_memutils.h
index f7114c2..0151e6e 100644
--- a/src/include/common/fe_memutils.h
+++ b/src/include/common/fe_memutils.h
@@ -19,8 +19,11 @@ extern void pg_free(void *pointer);
 /* Equivalent functions, deliberately named the same as backend functions */
 extern char *pstrdup(const char *in);
 extern void *palloc(Size size);
+extern void *palloc_noerror(Size size);
 extern void *palloc0(Size size);
+extern void *palloc0_noerror(Size size);
 extern void *repalloc(void *pointer, Size size);
+extern void *repalloc_noerror(void *pointer, Size size);
 extern void pfree(void *pointer);
 
 /* sprintf into a palloc'd buffer --- these are in psprintf.c */
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index ca03f2b..3634a7f 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -50,8 +50,11 @@ extern void *MemoryContextAllocZero(MemoryContext context, Size size);
 extern void *MemoryContextAllocZeroAligned(MemoryContext context, Size size);
 
 extern void *palloc(Size size);
+extern void *palloc_noerror(Size size);
 extern void *palloc0(Size size);
+extern void *palloc0_noerror(Size size);
 extern void *repalloc(void *pointer, Size size);
+extern void *repalloc_noerror(void *pointer, Size size);
 extern void pfree(void *pointer);
 
 /*
-- 
2.2.2

From 1ff0406b11a6e54cdb0a4312c199dbe7800fea82 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@otacoo.com>
Date: Wed, 28 Jan 2015 23:27:54 +0900
Subject: [PATCH 3/3] Create MemoryContextAllocExtended central routine for
 memory allocation

This new routine is the central point of all the MemoryContextAlloc*
functions controlled by a set of flags allowing a far wider control
of how allocation can be done in a memory context.
---
 src/backend/utils/mmgr/mcxt.c | 107 ++++++++++++------------------------------
 src/include/utils/palloc.h    |  11 +++++
 2 files changed, 41 insertions(+), 77 deletions(-)

diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index f8907a8..01201fb 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -607,21 +607,20 @@ MemoryContextCreate(NodeTag tag, Size size,
 }
 
 /*
- * MemoryContextAlloc
- *		Allocate space within the specified context.
- *
- * This could be turned into a macro, but we'd have to import
- * nodes/memnodes.h into postgres.h which seems a bad idea.
+ * MemoryContextAllocExtended
+ *		Allocation space within the specified context using flag options
+ *		specified by caller.
  */
 void *
-MemoryContextAlloc(MemoryContext context, Size size)
+MemoryContextAllocExtended(MemoryContext context, Size size, int flags)
 {
 	void	   *ret;
 
 	AssertArg(MemoryContextIsValid(context));
 	AssertNotInCriticalSection(context);
 
-	if (!AllocSizeIsValid(size))
+	if (((flags & ALLOC_HUGE) != 0 && !AllocHugeSizeIsValid(size)) ||
+		!AllocSizeIsValid(size))
 		elog(ERROR, "invalid memory alloc request size %zu", size);
 
 	context->isReset = false;
@@ -635,75 +634,48 @@ MemoryContextAlloc(MemoryContext context, Size size)
 
 	VALGRIND_MEMPOOL_ALLOC(context, ret, size);
 
+	/*
+	 * MemSetAligned and MemSetLoop should not be called in parallel
+	 * (see c.h for more details).
+	 */
+	Assert((flags & ALLOC_ALIGNED) == 0 || (flags & ALLOC_HUGE) == 0);
+
+	if ((flags & ALLOC_ZERO) != 0)
+		MemSetAligned(ret, 0, size);
+	if ((flags & ALLOC_ALIGNED) != 0)
+		MemSetLoop(ret, 0, size);
+
 	return ret;
 }
 
 /*
+ * MemoryContextAlloc
+ *		Allocate space within the specified context.
+ */
+void *
+MemoryContextAlloc(MemoryContext context, Size size)
+{
+	return MemoryContextAllocExtended(context, size, 0);
+}
+
+/*
  * MemoryContextAllocZero
  *		Like MemoryContextAlloc, but clears allocated memory
- *
- *	We could just call MemoryContextAlloc then clear the memory, but this
- *	is a very common combination, so we provide the combined operation.
  */
 void *
 MemoryContextAllocZero(MemoryContext context, Size size)
 {
-	void	   *ret;
-
-	AssertArg(MemoryContextIsValid(context));
-	AssertNotInCriticalSection(context);
-
-	if (!AllocSizeIsValid(size))
-		elog(ERROR, "invalid memory alloc request size %zu", size);
-
-	context->isReset = false;
-
-	ret = (*context->methods->alloc) (context, size);
-	if (ret == NULL)
-		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);
-
-	return ret;
+	return MemoryContextAllocExtended(context, size, ALLOC_ZERO);
 }
 
 /*
  * MemoryContextAllocZeroAligned
  *		MemoryContextAllocZero where length is suitable for MemSetLoop
- *
- *	This might seem overly specialized, but it's not because newNode()
- *	is so often called with compile-time-constant sizes.
  */
 void *
 MemoryContextAllocZeroAligned(MemoryContext context, Size size)
 {
-	void	   *ret;
-
-	AssertArg(MemoryContextIsValid(context));
-	AssertNotInCriticalSection(context);
-
-	if (!AllocSizeIsValid(size))
-		elog(ERROR, "invalid memory alloc request size %zu", size);
-
-	context->isReset = false;
-
-	ret = (*context->methods->alloc) (context, size);
-	if (ret == NULL)
-		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;
+	return MemoryContextAllocExtended(context, size, ALLOC_ALIGNED);
 }
 
 static inline void *
@@ -868,26 +840,7 @@ repalloc_noerror(void *pointer, Size size)
 void *
 MemoryContextAllocHuge(MemoryContext context, Size size)
 {
-	void	   *ret;
-
-	AssertArg(MemoryContextIsValid(context));
-	AssertNotInCriticalSection(context);
-
-	if (!AllocHugeSizeIsValid(size))
-		elog(ERROR, "invalid memory alloc request size %zu", size);
-
-	context->isReset = false;
-
-	ret = (*context->methods->alloc) (context, size);
-	if (ret == NULL)
-		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;
+	return MemoryContextAllocExtended(context, size, ALLOC_HUGE);
 }
 
 /*
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index 3634a7f..a8bf9a1 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -43,8 +43,19 @@ typedef struct MemoryContextData *MemoryContext;
 extern PGDLLIMPORT MemoryContext CurrentMemoryContext;
 
 /*
+ * Additional options for MemoryContextAllocExtended()
+ */
+#define ALLOC_HUGE		0x01	/* huge allocation */
+#define ALLOC_ZERO		0x02	/* clear allocated memory */
+#define ALLOC_NO_OOM	0x04	/* no failure if out-of-memory */
+#define ALLOC_ALIGNED	0x08	/* request length suitable for MemSetLoop */
+
+/*
  * Fundamental memory-allocation operations (more are in utils/memutils.h)
  */
+extern void *MemoryContextAllocExtended(MemoryContext context,
+										Size size,
+										int flags);
 extern void *MemoryContextAlloc(MemoryContext context, Size size);
 extern void *MemoryContextAllocZero(MemoryContext context, Size size);
 extern void *MemoryContextAllocZeroAligned(MemoryContext context, Size size);
-- 
2.2.2

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to