Skip to content

Commit 743112a

Browse files
committed
Adjust memory allocation functions to allow sibling calls
Many modern compilers are able to optimize function calls to functions where the parameters of the called function match a leading subset of the calling function's parameters. If there are no instructions in the calling function after the function is called, then the compiler is free to avoid any stack frame setup and implement the function call as a "jmp" rather than a "call". This is called sibling call optimization. Here we adjust the memory allocation functions in mcxt.c to allow this optimization. This requires moving some responsibility into the memory context implementations themselves. It's now the responsibility of the MemoryContext to check for malloc failures. This is good as it both allows the sibling call optimization, but also because most small and medium allocations won't call malloc and just allocate memory to an existing block. That can't fail, so checking for NULLs in that case isn't required. Also, traditionally it's been the responsibility of palloc and the other allocation functions in mcxt.c to check for invalid allocation size requests. Here we also move the responsibility of checking that into the MemoryContext. This isn't to allow the sibling call optimization, but more because most of our allocators handle large allocations separately and we can just add the size check when doing large allocations. We no longer check this for non-large allocations at all. To make checking the allocation request sizes and ERROR handling easier, add some helper functions to mcxt.c for the allocators to use. Author: Andres Freund Reviewed-by: David Rowley Discussion: https://postgr.es/m/20210719195950.gavgs6ujzmjfaiig@alap3.anarazel.de
1 parent 17a3f79 commit 743112a

File tree

7 files changed

+196
-166
lines changed

7 files changed

+196
-166
lines changed

src/backend/utils/mmgr/alignedalloc.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ AlignedAllocFree(void *pointer)
5757
* memory will be uninitialized.
5858
*/
5959
void *
60-
AlignedAllocRealloc(void *pointer, Size size)
60+
AlignedAllocRealloc(void *pointer, Size size, int flags)
6161
{
6262
MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer);
6363
Size alignto;
@@ -97,14 +97,17 @@ AlignedAllocRealloc(void *pointer, Size size)
9797
#endif
9898

9999
ctx = GetMemoryChunkContext(unaligned);
100-
newptr = MemoryContextAllocAligned(ctx, size, alignto, 0);
100+
newptr = MemoryContextAllocAligned(ctx, size, alignto, flags);
101101

102102
/*
103103
* We may memcpy beyond the end of the original allocation request size,
104104
* so we must mark the entire allocation as defined.
105105
*/
106-
VALGRIND_MAKE_MEM_DEFINED(pointer, old_size);
107-
memcpy(newptr, pointer, Min(size, old_size));
106+
if (likely(newptr != NULL))
107+
{
108+
VALGRIND_MAKE_MEM_DEFINED(pointer, old_size);
109+
memcpy(newptr, pointer, Min(size, old_size));
110+
}
108111
pfree(unaligned);
109112

110113
return newptr;

src/backend/utils/mmgr/aset.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ AllocSetDelete(MemoryContext context)
700700
* return space that is marked NOACCESS - AllocSetRealloc has to beware!
701701
*/
702702
void *
703-
AllocSetAlloc(MemoryContext context, Size size)
703+
AllocSetAlloc(MemoryContext context, Size size, int flags)
704704
{
705705
AllocSet set = (AllocSet) context;
706706
AllocBlock block;
@@ -717,6 +717,9 @@ AllocSetAlloc(MemoryContext context, Size size)
717717
*/
718718
if (size > set->allocChunkLimit)
719719
{
720+
/* only check size in paths where the limits could be hit */
721+
MemoryContextCheckSize(context, size, flags);
722+
720723
#ifdef MEMORY_CONTEXT_CHECKING
721724
/* ensure there's always space for the sentinel byte */
722725
chunk_size = MAXALIGN(size + 1);
@@ -727,7 +730,7 @@ AllocSetAlloc(MemoryContext context, Size size)
727730
blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
728731
block = (AllocBlock) malloc(blksize);
729732
if (block == NULL)
730-
return NULL;
733+
return MemoryContextAllocationFailure(context, size, flags);
731734

732735
context->mem_allocated += blksize;
733736

@@ -940,7 +943,7 @@ AllocSetAlloc(MemoryContext context, Size size)
940943
}
941944

942945
if (block == NULL)
943-
return NULL;
946+
return MemoryContextAllocationFailure(context, size, flags);
944947

945948
context->mem_allocated += blksize;
946949

@@ -1106,7 +1109,7 @@ AllocSetFree(void *pointer)
11061109
* request size.)
11071110
*/
11081111
void *
1109-
AllocSetRealloc(void *pointer, Size size)
1112+
AllocSetRealloc(void *pointer, Size size, int flags)
11101113
{
11111114
AllocBlock block;
11121115
AllocSet set;
@@ -1139,6 +1142,9 @@ AllocSetRealloc(void *pointer, Size size)
11391142

11401143
set = block->aset;
11411144

1145+
/* only check size in paths where the limits could be hit */
1146+
MemoryContextCheckSize((MemoryContext) set, size, flags);
1147+
11421148
oldchksize = block->endptr - (char *) pointer;
11431149

11441150
#ifdef MEMORY_CONTEXT_CHECKING
@@ -1165,7 +1171,7 @@ AllocSetRealloc(void *pointer, Size size)
11651171
{
11661172
/* Disallow access to the chunk header. */
11671173
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
1168-
return NULL;
1174+
return MemoryContextAllocationFailure(&set->header, size, flags);
11691175
}
11701176

11711177
/* updated separately, not to underflow when (oldblksize > blksize) */
@@ -1325,15 +1331,15 @@ AllocSetRealloc(void *pointer, Size size)
13251331
AllocPointer newPointer;
13261332
Size oldsize;
13271333

1328-
/* allocate new chunk */
1329-
newPointer = AllocSetAlloc((MemoryContext) set, size);
1334+
/* allocate new chunk (this also checks size is valid) */
1335+
newPointer = AllocSetAlloc((MemoryContext) set, size, flags);
13301336

13311337
/* leave immediately if request was not completed */
13321338
if (newPointer == NULL)
13331339
{
13341340
/* Disallow access to the chunk header. */
13351341
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
1336-
return NULL;
1342+
return MemoryContextAllocationFailure((MemoryContext) set, size, flags);
13371343
}
13381344

13391345
/*

src/backend/utils/mmgr/generation.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ GenerationDelete(MemoryContext context)
344344
* return space that is marked NOACCESS - GenerationRealloc has to beware!
345345
*/
346346
void *
347-
GenerationAlloc(MemoryContext context, Size size)
347+
GenerationAlloc(MemoryContext context, Size size, int flags)
348348
{
349349
GenerationContext *set = (GenerationContext *) context;
350350
GenerationBlock *block;
@@ -367,9 +367,12 @@ GenerationAlloc(MemoryContext context, Size size)
367367
{
368368
Size blksize = required_size + Generation_BLOCKHDRSZ;
369369

370+
/* only check size in paths where the limits could be hit */
371+
MemoryContextCheckSize((MemoryContext) set, size, flags);
372+
370373
block = (GenerationBlock *) malloc(blksize);
371374
if (block == NULL)
372-
return NULL;
375+
return MemoryContextAllocationFailure(context, size, flags);
373376

374377
context->mem_allocated += blksize;
375378

@@ -472,7 +475,7 @@ GenerationAlloc(MemoryContext context, Size size)
472475
block = (GenerationBlock *) malloc(blksize);
473476

474477
if (block == NULL)
475-
return NULL;
478+
return MemoryContextAllocationFailure(context, size, flags);
476479

477480
context->mem_allocated += blksize;
478481

@@ -737,7 +740,7 @@ GenerationFree(void *pointer)
737740
* into the old chunk - in that case we just update chunk header.
738741
*/
739742
void *
740-
GenerationRealloc(void *pointer, Size size)
743+
GenerationRealloc(void *pointer, Size size, int flags)
741744
{
742745
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
743746
GenerationContext *set;
@@ -839,15 +842,15 @@ GenerationRealloc(void *pointer, Size size)
839842
return pointer;
840843
}
841844

842-
/* allocate new chunk */
843-
newPointer = GenerationAlloc((MemoryContext) set, size);
845+
/* allocate new chunk (this also checks size is valid) */
846+
newPointer = GenerationAlloc((MemoryContext) set, size, flags);
844847

845848
/* leave immediately if request was not completed */
846849
if (newPointer == NULL)
847850
{
848851
/* Disallow access to the chunk header. */
849852
VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
850-
return NULL;
853+
return MemoryContextAllocationFailure((MemoryContext) set, size, flags);
851854
}
852855

853856
/*

0 commit comments

Comments
 (0)