On Fri, Jan 16, 2015 at 8:47 AM, Michael Paquier <michael.paqu...@gmail.com> wrote: > Voting for palloc_noerror() as well. And here is an updated patch using this naming, added to the next CF as well. -- Michael
From b636c809c2f2cb4177bedc2e5a4883a79b61fbc6 Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@otacoo.com> Date: Tue, 13 Jan 2015 15:40:38 +0900 Subject: [PATCH] Add memory allocation APIs able to return NULL instead of ERROR
The following functions are added to the existing set for frontend and backend: - palloc_noerror - palloc0_noerror - repalloc_noerror --- src/backend/utils/mmgr/aset.c | 529 +++++++++++++++++++++++---------------- src/backend/utils/mmgr/mcxt.c | 124 +++++---- src/common/fe_memutils.c | 72 ++++-- src/include/common/fe_memutils.h | 3 + src/include/nodes/memnodes.h | 2 + src/include/utils/palloc.h | 3 + 6 files changed, 451 insertions(+), 282 deletions(-) diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 85b3c9a..974e018 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -243,11 +243,22 @@ typedef struct AllocChunkData ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ)) /* + * Wrappers for allocation functions. + */ +static void *set_alloc_internal(MemoryContext context, + Size size, bool noerror); +static void *set_realloc_internal(MemoryContext context, void *pointer, + Size size, bool noerror); + +/* * These functions implement the MemoryContext API for AllocSet contexts. */ static void *AllocSetAlloc(MemoryContext context, Size size); +static void *AllocSetAllocNoError(MemoryContext context, Size size); static void AllocSetFree(MemoryContext context, void *pointer); static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size); +static void *AllocSetReallocNoError(MemoryContext context, + void *pointer, Size size); static void AllocSetInit(MemoryContext context); static void AllocSetReset(MemoryContext context); static void AllocSetDelete(MemoryContext context); @@ -264,8 +275,10 @@ static void AllocSetCheck(MemoryContext context); */ static MemoryContextMethods AllocSetMethods = { AllocSetAlloc, + AllocSetAllocNoError, AllocSetFree, AllocSetRealloc, + AllocSetReallocNoError, AllocSetInit, AllocSetReset, AllocSetDelete, @@ -517,140 +530,16 @@ AllocSetContextCreate(MemoryContext parent, } /* - * AllocSetInit - * Context-type-specific initialization routine. - * - * This is called by MemoryContextCreate() after setting up the - * generic MemoryContext fields and before linking the new context - * into the context tree. We must do whatever is needed to make the - * new context minimally valid for deletion. We must *not* risk - * failure --- thus, for example, allocating more memory is not cool. - * (AllocSetContextCreate can allocate memory when it gets control - * back, however.) - */ -static void -AllocSetInit(MemoryContext context) -{ - /* - * Since MemoryContextCreate already zeroed the context node, we don't - * have to do anything here: it's already OK. - */ -} - -/* - * AllocSetReset - * Frees all memory which is allocated in the given 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, - * we don't thrash malloc() when a context is repeatedly reset after small - * allocations, which is typical behavior for per-tuple contexts. - */ -static void -AllocSetReset(MemoryContext context) -{ - AllocSet set = (AllocSet) context; - AllocBlock block; - - AssertArg(AllocSetIsValid(set)); - -#ifdef MEMORY_CONTEXT_CHECKING - /* Check for corruption and leaks before freeing */ - AllocSetCheck(context); -#endif - - /* Clear chunk freelists */ - MemSetAligned(set->freelist, 0, sizeof(set->freelist)); - - block = set->blocks; - - /* New blocks list is either empty or just the keeper block */ - set->blocks = set->keeper; - - while (block != NULL) - { - AllocBlock next = block->next; - - if (block == set->keeper) - { - /* Reset the block, but don't return it to malloc */ - char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ; - -#ifdef CLOBBER_FREED_MEMORY - wipe_mem(datastart, block->freeptr - datastart); -#else - /* wipe_mem() would have done this */ - VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart); -#endif - block->freeptr = datastart; - block->next = NULL; - } - else - { - /* Normal case, release the block */ -#ifdef CLOBBER_FREED_MEMORY - wipe_mem(block, block->freeptr - ((char *) block)); -#endif - free(block); - } - block = next; - } - - /* Reset block size allocation sequence, too */ - set->nextBlockSize = set->initBlockSize; -} - -/* - * AllocSetDelete - * Frees all memory which is allocated in the given set, - * in preparation for deletion of the set. - * - * Unlike AllocSetReset, this *must* free all resources of the set. - * But note we are not responsible for deleting the context node itself. - */ -static void -AllocSetDelete(MemoryContext context) -{ - AllocSet set = (AllocSet) context; - AllocBlock block = set->blocks; - - AssertArg(AllocSetIsValid(set)); - -#ifdef MEMORY_CONTEXT_CHECKING - /* Check for corruption and leaks before freeing */ - AllocSetCheck(context); -#endif - - /* Make it look empty, just in case... */ - MemSetAligned(set->freelist, 0, sizeof(set->freelist)); - set->blocks = NULL; - set->keeper = NULL; - - while (block != NULL) - { - AllocBlock next = block->next; - -#ifdef CLOBBER_FREED_MEMORY - wipe_mem(block, block->freeptr - ((char *) block)); -#endif - free(block); - block = next; - } -} - -/* - * AllocSetAlloc - * Returns pointer to allocated memory of given size; memory is added - * to the set. + * set_alloc_internal + * Wrapper for memory allocation routines. * * No request may exceed: * MAXALIGN_DOWN(SIZE_MAX) - ALLOC_BLOCKHDRSZ - ALLOC_CHUNKHDRSZ * All callers use a much-lower limit. */ static void * -AllocSetAlloc(MemoryContext context, Size size) +set_alloc_internal(MemoryContext context, + Size size, bool noerror) { AllocSet set = (AllocSet) context; AllocBlock block; @@ -673,10 +562,13 @@ 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))); + if (noerror) + return NULL; + else + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Failed on request of size %zu.", size))); } block->aset = set; block->freeptr = block->endptr = ((char *) block) + blksize; @@ -867,10 +759,13 @@ 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))); + if (noerror) + return NULL; + else + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Failed on request of size %zu.", size))); } block->aset = set; @@ -928,83 +823,8 @@ AllocSetAlloc(MemoryContext context, Size size) } /* - * AllocSetFree - * Frees allocated memory; memory is removed from the set. - */ -static void -AllocSetFree(MemoryContext context, void *pointer) -{ - AllocSet set = (AllocSet) context; - AllocChunk chunk = AllocPointerGetChunk(pointer); - - AllocFreeInfo(set, chunk); - -#ifdef MEMORY_CONTEXT_CHECKING - VALGRIND_MAKE_MEM_DEFINED(&chunk->requested_size, - sizeof(chunk->requested_size)); - /* Test for someone scribbling on unused space in chunk */ - if (chunk->requested_size < chunk->size) - if (!sentinel_ok(pointer, chunk->requested_size)) - elog(WARNING, "detected write past chunk end in %s %p", - set->header.name, chunk); -#endif - - if (chunk->size > set->allocChunkLimit) - { - /* - * Big chunks are certain to have been allocated as single-chunk - * blocks. Find the containing block and return it to malloc(). - */ - AllocBlock block = set->blocks; - AllocBlock prevblock = NULL; - - while (block != NULL) - { - if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ)) - break; - prevblock = block; - block = block->next; - } - if (block == NULL) - 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; - else - prevblock->next = block->next; -#ifdef CLOBBER_FREED_MEMORY - wipe_mem(block, block->freeptr - ((char *) block)); -#endif - free(block); - } - else - { - /* Normal case, put the chunk into appropriate freelist */ - int fidx = AllocSetFreeIndex(chunk->size); - - chunk->aset = (void *) set->freelist[fidx]; - -#ifdef CLOBBER_FREED_MEMORY - wipe_mem(pointer, chunk->size); -#endif - -#ifdef MEMORY_CONTEXT_CHECKING - /* Reset requested_size to 0 in chunks that are on freelist */ - chunk->requested_size = 0; -#endif - set->freelist[fidx] = chunk; - } -} - -/* - * 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. + * set_realloc_internal + * Wrapper for memory reallocation routines. * * Without MEMORY_CONTEXT_CHECKING, we don't know the old request size. This * makes our Valgrind client requests less-precise, hazarding false negatives. @@ -1012,7 +832,8 @@ AllocSetFree(MemoryContext context, void *pointer) * request size.) */ static void * -AllocSetRealloc(MemoryContext context, void *pointer, Size size) +set_realloc_internal(MemoryContext context, void *pointer, + Size size, bool noerror) { AllocSet set = (AllocSet) context; AllocChunk chunk = AllocPointerGetChunk(pointer); @@ -1029,8 +850,8 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) #endif /* - * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the - * allocated area already is >= the new size. (In particular, we always + * Chunk sizes are aligned to power of 2 in set_alloc_internal(). Maybe + * the allocated area already is >= the new size. (In particular, we always * fall out here if the requested size is a decrease.) */ if (oldsize >= size) @@ -1109,10 +930,15 @@ 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))); + + /* allocation failed */ + if (noerror) + return NULL; + else + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Failed on request of size %zu.", size))); } block->freeptr = block->endptr = ((char *) block) + blksize; @@ -1177,10 +1003,17 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) AllocPointer newPointer; /* allocate new chunk */ - newPointer = AllocSetAlloc((MemoryContext) set, size); + newPointer = set_alloc_internal((MemoryContext) set, size, noerror); + + /* leave if allocation did not complete properly */ + if (newPointer == NULL) + { + Assert(noerror); + return NULL; + } /* - * AllocSetAlloc() just made the region NOACCESS. Change it to + * set_alloc_internal() just made the region NOACCESS. Change it to * UNDEFINED for the moment; memcpy() will then transfer definedness * from the old allocation to the new. If we know the old allocation, * copy just that much. Otherwise, make the entire old chunk defined @@ -1203,6 +1036,258 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) } } + +/* + * AllocSetInit + * Context-type-specific initialization routine. + * + * This is called by MemoryContextCreate() after setting up the + * generic MemoryContext fields and before linking the new context + * into the context tree. We must do whatever is needed to make the + * new context minimally valid for deletion. We must *not* risk + * failure --- thus, for example, allocating more memory is not cool. + * (AllocSetContextCreate can allocate memory when it gets control + * back, however.) + */ +static void +AllocSetInit(MemoryContext context) +{ + /* + * Since MemoryContextCreate already zeroed the context node, we don't + * have to do anything here: it's already OK. + */ +} + +/* + * AllocSetReset + * Frees all memory which is allocated in the given 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, + * we don't thrash malloc() when a context is repeatedly reset after small + * allocations, which is typical behavior for per-tuple contexts. + */ +static void +AllocSetReset(MemoryContext context) +{ + AllocSet set = (AllocSet) context; + AllocBlock block; + + AssertArg(AllocSetIsValid(set)); + +#ifdef MEMORY_CONTEXT_CHECKING + /* Check for corruption and leaks before freeing */ + AllocSetCheck(context); +#endif + + /* Clear chunk freelists */ + MemSetAligned(set->freelist, 0, sizeof(set->freelist)); + + block = set->blocks; + + /* New blocks list is either empty or just the keeper block */ + set->blocks = set->keeper; + + while (block != NULL) + { + AllocBlock next = block->next; + + if (block == set->keeper) + { + /* Reset the block, but don't return it to malloc */ + char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ; + +#ifdef CLOBBER_FREED_MEMORY + wipe_mem(datastart, block->freeptr - datastart); +#else + /* wipe_mem() would have done this */ + VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart); +#endif + block->freeptr = datastart; + block->next = NULL; + } + else + { + /* Normal case, release the block */ +#ifdef CLOBBER_FREED_MEMORY + wipe_mem(block, block->freeptr - ((char *) block)); +#endif + free(block); + } + block = next; + } + + /* Reset block size allocation sequence, too */ + set->nextBlockSize = set->initBlockSize; +} + +/* + * AllocSetDelete + * Frees all memory which is allocated in the given set, + * in preparation for deletion of the set. + * + * Unlike AllocSetReset, this *must* free all resources of the set. + * But note we are not responsible for deleting the context node itself. + */ +static void +AllocSetDelete(MemoryContext context) +{ + AllocSet set = (AllocSet) context; + AllocBlock block = set->blocks; + + AssertArg(AllocSetIsValid(set)); + +#ifdef MEMORY_CONTEXT_CHECKING + /* Check for corruption and leaks before freeing */ + AllocSetCheck(context); +#endif + + /* Make it look empty, just in case... */ + MemSetAligned(set->freelist, 0, sizeof(set->freelist)); + set->blocks = NULL; + set->keeper = NULL; + + while (block != NULL) + { + AllocBlock next = block->next; + +#ifdef CLOBBER_FREED_MEMORY + wipe_mem(block, block->freeptr - ((char *) block)); +#endif + free(block); + block = next; + } +} + +/* + * AllocSetAlloc + * Returns pointer to allocated memory of given size; memory is added + * to the set. This fails with an out-of-memory error if request cannot + * be completed properly. + */ +static void * +AllocSetAlloc(MemoryContext context, Size size) +{ + return set_alloc_internal(context, size, false); +} + +/* + * AllocSetAllocNoError + * Returns pointer to allocated memory of given size; memory is added + * to the set. This returns NULL if request cannot be completed + * properly. + * + * No request may exceed: + * MAXALIGN_DOWN(SIZE_MAX) - ALLOC_BLOCKHDRSZ - ALLOC_CHUNKHDRSZ + * All callers use a much-lower limit. + */ +static void * +AllocSetAllocNoError(MemoryContext context, Size size) +{ + return set_alloc_internal(context, size, true); +} + +/* + * AllocSetFree + * Frees allocated memory; memory is removed from the set. + */ +static void +AllocSetFree(MemoryContext context, void *pointer) +{ + AllocSet set = (AllocSet) context; + AllocChunk chunk = AllocPointerGetChunk(pointer); + + AllocFreeInfo(set, chunk); + +#ifdef MEMORY_CONTEXT_CHECKING + VALGRIND_MAKE_MEM_DEFINED(&chunk->requested_size, + sizeof(chunk->requested_size)); + /* Test for someone scribbling on unused space in chunk */ + if (chunk->requested_size < chunk->size) + if (!sentinel_ok(pointer, chunk->requested_size)) + elog(WARNING, "detected write past chunk end in %s %p", + set->header.name, chunk); +#endif + + if (chunk->size > set->allocChunkLimit) + { + /* + * Big chunks are certain to have been allocated as single-chunk + * blocks. Find the containing block and return it to malloc(). + */ + AllocBlock block = set->blocks; + AllocBlock prevblock = NULL; + + while (block != NULL) + { + if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ)) + break; + prevblock = block; + block = block->next; + } + if (block == NULL) + 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; + else + prevblock->next = block->next; +#ifdef CLOBBER_FREED_MEMORY + wipe_mem(block, block->freeptr - ((char *) block)); +#endif + free(block); + } + else + { + /* Normal case, put the chunk into appropriate freelist */ + int fidx = AllocSetFreeIndex(chunk->size); + + chunk->aset = (void *) set->freelist[fidx]; + +#ifdef CLOBBER_FREED_MEMORY + wipe_mem(pointer, chunk->size); +#endif + +#ifdef MEMORY_CONTEXT_CHECKING + /* Reset requested_size to 0 in chunks that are on freelist */ + chunk->requested_size = 0; +#endif + set->freelist[fidx] = chunk; + } +} + +/* + * 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. If request cannot + * be completed, this fails with an out-of-memory error. + */ +static void * +AllocSetRealloc(MemoryContext context, void *pointer, Size size) +{ + return set_realloc_internal(context, pointer, size, false); +} + +/* + * AllocSetReallocNoError + * 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. If request cannot + * be completed, this returns NULL. + */ +static void * +AllocSetReallocNoError(MemoryContext context, void *pointer, Size size) +{ + return set_realloc_internal(context, pointer, size, true); +} + /* * AllocSetGetChunkSpace * Given a currently-allocated chunk, determine the total space diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c index aa0d458..37c0669 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 void* palloc_internal(Size size, bool noerror); +static 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 @@ -684,8 +688,8 @@ MemoryContextAllocZeroAligned(MemoryContext context, Size size) return ret; } -void * -palloc(Size size) +static void* +palloc_internal(Size size, bool noerror) { /* duplicates MemoryContextAlloc to avoid increased overhead */ void *ret; @@ -698,31 +702,85 @@ palloc(Size size) CurrentMemoryContext->isReset = false; - ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size); + if (noerror) + ret = (*CurrentMemoryContext->methods->alloc_noerror) + (CurrentMemoryContext, size); + else + ret = (*CurrentMemoryContext->methods->alloc) + (CurrentMemoryContext, size); VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size); return ret; } -void * -palloc0(Size size) +static 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); - VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size); + /* + * 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); + + if (noerror) + ret = (*context->methods->realloc_noerror) (context, pointer, size); + else + ret = (*context->methods->realloc) (context, pointer, size); + + 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; } @@ -757,41 +815,23 @@ pfree(void *pointer) /* * repalloc - * Adjust the size of a previously allocated chunk. + * Adjust the size of a previously allocated chunk, failing on OOM. */ 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); - VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size); + return repalloc_internal(pointer, size, false); +} - return ret; +/* + * repalloc + * Adjust the size of a previously allocated chunk, returning NULL + * on OOM. + */ +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..fd343ba 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 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 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,18 +72,7 @@ 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); } /* @@ -100,13 +112,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_noeror(Size size) +{ + void *tmp; + tmp = pg_malloc_internal(size, true); + MemSet(tmp, 0, size); + return tmp; } void @@ -124,5 +154,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/nodes/memnodes.h b/src/include/nodes/memnodes.h index ca9c3de..6d61acb 100644 --- a/src/include/nodes/memnodes.h +++ b/src/include/nodes/memnodes.h @@ -36,9 +36,11 @@ typedef struct MemoryContextMethods { void *(*alloc) (MemoryContext context, Size size); + void *(*alloc_noerror) (MemoryContext context, Size size); /* call this free_p in case someone #define's free() */ void (*free_p) (MemoryContext context, void *pointer); void *(*realloc) (MemoryContext context, void *pointer, Size size); + void *(*realloc_noerror) (MemoryContext context, void *pointer, Size size); void (*init) (MemoryContext context); void (*reset) (MemoryContext context); void (*delete_context) (MemoryContext context); 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
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers