Commit 9fa6f00b authored by Tom Lane's avatar Tom Lane

Rethink MemoryContext creation to improve performance.

This patch makes a number of interrelated changes to reduce the overhead
involved in creating/deleting memory contexts.  The key ideas are:

* Include the AllocSetContext header of an aset.c context in its first
malloc request, rather than allocating it separately in TopMemoryContext.
This means that we now always create an initial or "keeper" block in an
aset, even if it never receives any allocation requests.

* Create freelists in which we can save and recycle recently-destroyed
asets (this idea is due to Robert Haas).

* In the common case where the name of a context is a constant string,
just store a pointer to it in the context header, rather than copying
the string.

The first change eliminates a palloc/pfree cycle per context, and
also avoids bloat in TopMemoryContext, at the price that creating
a context now involves a malloc/free cycle even if the context never
receives any allocations.  That would be a loser for some common
usage patterns, but recycling short-lived contexts via the freelist
eliminates that pain.

Avoiding copying constant strings not only saves strlen() and strcpy()
overhead, but is an essential part of the freelist optimization because
it makes the context header size constant.  Currently we make no
attempt to use the freelist for contexts with non-constant names.
(Perhaps someday we'll need to think harder about that, but in current
usage, most contexts with custom names are long-lived anyway.)

The freelist management in this initial commit is pretty simplistic,
and we might want to refine it later --- but in common workloads that
will never matter because the freelists will never get full anyway.

To create a context with a non-constant name, one is now required to
call AllocSetContextCreateExtended and specify the MEMCONTEXT_COPY_NAME
option.  AllocSetContextCreate becomes a wrapper macro, and it includes
a test that will complain about non-string-literal context name
parameters on gcc and similar compilers.

An unfortunate side effect of making AllocSetContextCreate a macro is
that one is now *required* to use the size parameter abstraction macros
(ALLOCSET_DEFAULT_SIZES and friends) with it; the pre-9.6 habit of
writing out individual size parameters no longer works unless you
switch to AllocSetContextCreateExtended.

Internally to the memory-context-related modules, the context creation
APIs are simplified, removing the rather baroque original design whereby
a context-type module called mcxt.c which then called back into the
context-type module.  That saved a bit of code duplication, but not much,
and it prevented context-type modules from exercising control over the
allocation of context headers.

In passing, I converted the test-and-elog validation of aset size
parameters into Asserts to save a few more cycles.  The original thought
was that callers might compute size parameters on the fly, but in practice
nobody does that, so it's useless to expend cycles on checking those
numbers in production builds.

Also, mark the memory context method-pointer structs "const",
just for cleanliness.

Discussion: https://postgr.es/m/2264.1512870796@sss.pgh.pa.us
parent 632b03da
...@@ -295,9 +295,7 @@ bt_check_every_level(Relation rel, bool readonly) ...@@ -295,9 +295,7 @@ bt_check_every_level(Relation rel, bool readonly)
/* Create context for page */ /* Create context for page */
state->targetcontext = AllocSetContextCreate(CurrentMemoryContext, state->targetcontext = AllocSetContextCreate(CurrentMemoryContext,
"amcheck context", "amcheck context",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_SIZES);
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
state->checkstrategy = GetAccessStrategy(BAS_BULKREAD); state->checkstrategy = GetAccessStrategy(BAS_BULKREAD);
/* Get true root block from meta-page */ /* Get true root block from meta-page */
......
...@@ -997,8 +997,9 @@ AtStart_Memory(void) ...@@ -997,8 +997,9 @@ AtStart_Memory(void)
*/ */
if (TransactionAbortContext == NULL) if (TransactionAbortContext == NULL)
TransactionAbortContext = TransactionAbortContext =
AllocSetContextCreate(TopMemoryContext, AllocSetContextCreateExtended(TopMemoryContext,
"TransactionAbortContext", "TransactionAbortContext",
0,
32 * 1024, 32 * 1024,
32 * 1024, 32 * 1024,
32 * 1024); 32 * 1024);
......
...@@ -520,8 +520,9 @@ RelationBuildPartitionDesc(Relation rel) ...@@ -520,8 +520,9 @@ RelationBuildPartitionDesc(Relation rel)
} }
/* Now build the actual relcache partition descriptor */ /* Now build the actual relcache partition descriptor */
rel->rd_pdcxt = AllocSetContextCreate(CacheMemoryContext, rel->rd_pdcxt = AllocSetContextCreateExtended(CacheMemoryContext,
RelationGetRelationName(rel), RelationGetRelationName(rel),
MEMCONTEXT_COPY_NAME,
ALLOCSET_DEFAULT_SIZES); ALLOCSET_DEFAULT_SIZES);
oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt); oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt);
......
...@@ -259,9 +259,7 @@ publicationListToArray(List *publist) ...@@ -259,9 +259,7 @@ publicationListToArray(List *publist)
/* Create memory context for temporary allocations. */ /* Create memory context for temporary allocations. */
memcxt = AllocSetContextCreate(CurrentMemoryContext, memcxt = AllocSetContextCreate(CurrentMemoryContext,
"publicationListToArray to array", "publicationListToArray to array",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_SIZES);
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcxt = MemoryContextSwitchTo(memcxt); oldcxt = MemoryContextSwitchTo(memcxt);
datums = (Datum *) palloc(sizeof(Datum) * list_length(publist)); datums = (Datum *) palloc(sizeof(Datum) * list_length(publist));
......
...@@ -57,9 +57,7 @@ DiscreteKnapsack(int max_weight, int num_items, ...@@ -57,9 +57,7 @@ DiscreteKnapsack(int max_weight, int num_items,
{ {
MemoryContext local_ctx = AllocSetContextCreate(CurrentMemoryContext, MemoryContext local_ctx = AllocSetContextCreate(CurrentMemoryContext,
"Knapsack", "Knapsack",
ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_SIZES);
ALLOCSET_SMALL_INITSIZE,
ALLOCSET_SMALL_MAXSIZE);
MemoryContext oldctx = MemoryContextSwitchTo(local_ctx); MemoryContext oldctx = MemoryContextSwitchTo(local_ctx);
double *values; double *values;
Bitmapset **sets; Bitmapset **sets;
......
...@@ -925,9 +925,7 @@ ApplyLauncherMain(Datum main_arg) ...@@ -925,9 +925,7 @@ ApplyLauncherMain(Datum main_arg)
/* Use temporary context for the database list and worker info. */ /* Use temporary context for the database list and worker info. */
subctx = AllocSetContextCreate(TopMemoryContext, subctx = AllocSetContextCreate(TopMemoryContext,
"Logical Replication Launcher sublist", "Logical Replication Launcher sublist",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_SIZES);
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldctx = MemoryContextSwitchTo(subctx); oldctx = MemoryContextSwitchTo(subctx);
/* search for subscriptions to start or stop. */ /* search for subscriptions to start or stop. */
......
...@@ -237,16 +237,19 @@ ReorderBufferAllocate(void) ...@@ -237,16 +237,19 @@ ReorderBufferAllocate(void)
buffer->change_context = SlabContextCreate(new_ctx, buffer->change_context = SlabContextCreate(new_ctx,
"Change", "Change",
0,
SLAB_DEFAULT_BLOCK_SIZE, SLAB_DEFAULT_BLOCK_SIZE,
sizeof(ReorderBufferChange)); sizeof(ReorderBufferChange));
buffer->txn_context = SlabContextCreate(new_ctx, buffer->txn_context = SlabContextCreate(new_ctx,
"TXN", "TXN",
0,
SLAB_DEFAULT_BLOCK_SIZE, SLAB_DEFAULT_BLOCK_SIZE,
sizeof(ReorderBufferTXN)); sizeof(ReorderBufferTXN));
buffer->tup_context = GenerationContextCreate(new_ctx, buffer->tup_context = GenerationContextCreate(new_ctx,
"Tuples", "Tuples",
0,
SLAB_LARGE_BLOCK_SIZE); SLAB_LARGE_BLOCK_SIZE);
hash_ctl.keysize = sizeof(TransactionId); hash_ctl.keysize = sizeof(TransactionId);
......
...@@ -152,9 +152,7 @@ pgoutput_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt, ...@@ -152,9 +152,7 @@ pgoutput_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
/* Create our memory context for private allocations. */ /* Create our memory context for private allocations. */
data->context = AllocSetContextCreate(ctx->context, data->context = AllocSetContextCreate(ctx->context,
"logical replication output context", "logical replication output context",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_SIZES);
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
ctx->output_plugin_private = data; ctx->output_plugin_private = data;
......
...@@ -669,8 +669,9 @@ RelationBuildRuleLock(Relation relation) ...@@ -669,8 +669,9 @@ RelationBuildRuleLock(Relation relation)
/* /*
* Make the private context. Assume it'll not contain much data. * Make the private context. Assume it'll not contain much data.
*/ */
rulescxt = AllocSetContextCreate(CacheMemoryContext, rulescxt = AllocSetContextCreateExtended(CacheMemoryContext,
RelationGetRelationName(relation), RelationGetRelationName(relation),
MEMCONTEXT_COPY_NAME,
ALLOCSET_SMALL_SIZES); ALLOCSET_SMALL_SIZES);
relation->rd_rulescxt = rulescxt; relation->rd_rulescxt = rulescxt;
...@@ -984,8 +985,9 @@ RelationBuildPartitionKey(Relation relation) ...@@ -984,8 +985,9 @@ RelationBuildPartitionKey(Relation relation)
ReleaseSysCache(tuple); ReleaseSysCache(tuple);
/* Success --- now copy to the cache memory */ /* Success --- now copy to the cache memory */
partkeycxt = AllocSetContextCreate(CacheMemoryContext, partkeycxt = AllocSetContextCreateExtended(CacheMemoryContext,
RelationGetRelationName(relation), RelationGetRelationName(relation),
MEMCONTEXT_COPY_NAME,
ALLOCSET_SMALL_SIZES); ALLOCSET_SMALL_SIZES);
relation->rd_partkeycxt = partkeycxt; relation->rd_partkeycxt = partkeycxt;
oldcxt = MemoryContextSwitchTo(relation->rd_partkeycxt); oldcxt = MemoryContextSwitchTo(relation->rd_partkeycxt);
...@@ -1566,8 +1568,9 @@ RelationInitIndexAccessInfo(Relation relation) ...@@ -1566,8 +1568,9 @@ RelationInitIndexAccessInfo(Relation relation)
* a context, and not just a couple of pallocs, is so that we won't leak * a context, and not just a couple of pallocs, is so that we won't leak
* any subsidiary info attached to fmgr lookup records. * any subsidiary info attached to fmgr lookup records.
*/ */
indexcxt = AllocSetContextCreate(CacheMemoryContext, indexcxt = AllocSetContextCreateExtended(CacheMemoryContext,
RelationGetRelationName(relation), RelationGetRelationName(relation),
MEMCONTEXT_COPY_NAME,
ALLOCSET_SMALL_SIZES); ALLOCSET_SMALL_SIZES);
relation->rd_indexcxt = indexcxt; relation->rd_indexcxt = indexcxt;
...@@ -5537,8 +5540,10 @@ load_relcache_init_file(bool shared) ...@@ -5537,8 +5540,10 @@ load_relcache_init_file(bool shared)
* prepare index info context --- parameters should match * prepare index info context --- parameters should match
* RelationInitIndexAccessInfo * RelationInitIndexAccessInfo
*/ */
indexcxt = AllocSetContextCreate(CacheMemoryContext, indexcxt =
AllocSetContextCreateExtended(CacheMemoryContext,
RelationGetRelationName(rel), RelationGetRelationName(rel),
MEMCONTEXT_COPY_NAME,
ALLOCSET_SMALL_SIZES); ALLOCSET_SMALL_SIZES);
rel->rd_indexcxt = indexcxt; rel->rd_indexcxt = indexcxt;
......
...@@ -294,8 +294,9 @@ lookup_ts_dictionary_cache(Oid dictId) ...@@ -294,8 +294,9 @@ lookup_ts_dictionary_cache(Oid dictId)
Assert(!found); /* it wasn't there a moment ago */ Assert(!found); /* it wasn't there a moment ago */
/* Create private memory context the first time through */ /* Create private memory context the first time through */
saveCtx = AllocSetContextCreate(CacheMemoryContext, saveCtx = AllocSetContextCreateExtended(CacheMemoryContext,
NameStr(dict->dictname), NameStr(dict->dictname),
MEMCONTEXT_COPY_NAME,
ALLOCSET_SMALL_SIZES); ALLOCSET_SMALL_SIZES);
} }
else else
......
...@@ -340,8 +340,10 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags) ...@@ -340,8 +340,10 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
CurrentDynaHashCxt = info->hcxt; CurrentDynaHashCxt = info->hcxt;
else else
CurrentDynaHashCxt = TopMemoryContext; CurrentDynaHashCxt = TopMemoryContext;
CurrentDynaHashCxt = AllocSetContextCreate(CurrentDynaHashCxt, CurrentDynaHashCxt =
AllocSetContextCreateExtended(CurrentDynaHashCxt,
tabname, tabname,
MEMCONTEXT_COPY_NAME,
ALLOCSET_DEFAULT_SIZES); ALLOCSET_DEFAULT_SIZES);
} }
......
...@@ -177,8 +177,7 @@ every other context is a direct or indirect child of this one. Allocating ...@@ -177,8 +177,7 @@ every other context is a direct or indirect child of this one. Allocating
here is essentially the same as "malloc", because this context will never here is essentially the same as "malloc", because this context will never
be reset or deleted. This is for stuff that should live forever, or for be reset or deleted. This is for stuff that should live forever, or for
stuff that the controlling module will take care of deleting at the stuff that the controlling module will take care of deleting at the
appropriate time. An example is fd.c's tables of open files, as well as appropriate time. An example is fd.c's tables of open files. Avoid
the context management nodes for memory contexts themselves. Avoid
allocating stuff here unless really necessary, and especially avoid allocating stuff here unless really necessary, and especially avoid
running with CurrentMemoryContext pointing here. running with CurrentMemoryContext pointing here.
...@@ -420,11 +419,10 @@ a maximum block size. Selecting smaller values can prevent wastage of ...@@ -420,11 +419,10 @@ a maximum block size. Selecting smaller values can prevent wastage of
space in contexts that aren't expected to hold very much (an example space in contexts that aren't expected to hold very much (an example
is the relcache's per-relation contexts). is the relcache's per-relation contexts).
Also, it is possible to specify a minimum context size. If this Also, it is possible to specify a minimum context size, in case for some
value is greater than zero then a block of that size will be grabbed reason that should be different from the initial size for additional
immediately upon context creation, and cleared but not released during blocks. An aset.c context will always contain at least one block,
context resets. This feature is needed for ErrorContext (see above), of size minContextSize if that is specified, otherwise initBlockSize.
but will most likely not be used for other contexts.
We expect that per-tuple contexts will be reset frequently and typically We expect that per-tuple contexts will be reset frequently and typically
will not allocate very much space per tuple cycle. To make this usage will not allocate very much space per tuple cycle. To make this usage
......
This diff is collapsed.
...@@ -61,6 +61,7 @@ typedef struct GenerationContext ...@@ -61,6 +61,7 @@ typedef struct GenerationContext
/* Generational context parameters */ /* Generational context parameters */
Size blockSize; /* standard block size */ Size blockSize; /* standard block size */
Size headerSize; /* allocated size of context header */
GenerationBlock *block; /* current (most recently allocated) block */ GenerationBlock *block; /* current (most recently allocated) block */
dlist_head blocks; /* list of blocks */ dlist_head blocks; /* list of blocks */
...@@ -149,7 +150,6 @@ struct GenerationChunk ...@@ -149,7 +150,6 @@ struct GenerationChunk
static void *GenerationAlloc(MemoryContext context, Size size); static void *GenerationAlloc(MemoryContext context, Size size);
static void GenerationFree(MemoryContext context, void *pointer); static void GenerationFree(MemoryContext context, void *pointer);
static void *GenerationRealloc(MemoryContext context, void *pointer, Size size); static void *GenerationRealloc(MemoryContext context, void *pointer, Size size);
static void GenerationInit(MemoryContext context);
static void GenerationReset(MemoryContext context); static void GenerationReset(MemoryContext context);
static void GenerationDelete(MemoryContext context); static void GenerationDelete(MemoryContext context);
static Size GenerationGetChunkSpace(MemoryContext context, void *pointer); static Size GenerationGetChunkSpace(MemoryContext context, void *pointer);
...@@ -164,11 +164,10 @@ static void GenerationCheck(MemoryContext context); ...@@ -164,11 +164,10 @@ static void GenerationCheck(MemoryContext context);
/* /*
* This is the virtual function table for Generation contexts. * This is the virtual function table for Generation contexts.
*/ */
static MemoryContextMethods GenerationMethods = { static const MemoryContextMethods GenerationMethods = {
GenerationAlloc, GenerationAlloc,
GenerationFree, GenerationFree,
GenerationRealloc, GenerationRealloc,
GenerationInit,
GenerationReset, GenerationReset,
GenerationDelete, GenerationDelete,
GenerationGetChunkSpace, GenerationGetChunkSpace,
...@@ -208,8 +207,10 @@ static MemoryContextMethods GenerationMethods = { ...@@ -208,8 +207,10 @@ static MemoryContextMethods GenerationMethods = {
MemoryContext MemoryContext
GenerationContextCreate(MemoryContext parent, GenerationContextCreate(MemoryContext parent,
const char *name, const char *name,
int flags,
Size blockSize) Size blockSize)
{ {
Size headerSize;
GenerationContext *set; GenerationContext *set;
/* Assert we padded GenerationChunk properly */ /* Assert we padded GenerationChunk properly */
...@@ -231,29 +232,51 @@ GenerationContextCreate(MemoryContext parent, ...@@ -231,29 +232,51 @@ GenerationContextCreate(MemoryContext parent,
elog(ERROR, "invalid blockSize for memory context: %zu", elog(ERROR, "invalid blockSize for memory context: %zu",
blockSize); blockSize);
/* Do the type-independent part of context creation */ /*
set = (GenerationContext *) MemoryContextCreate(T_GenerationContext, * Allocate the context header. Unlike aset.c, we never try to combine
sizeof(GenerationContext), * this with the first regular block, since that would prevent us from
&GenerationMethods, * freeing the first generation of allocations.
parent, */
name);
set->blockSize = blockSize; /* Size of the memory context header, including name storage if needed */
if (flags & MEMCONTEXT_COPY_NAME)
headerSize = MAXALIGN(sizeof(GenerationContext) + strlen(name) + 1);
else
headerSize = MAXALIGN(sizeof(GenerationContext));
return (MemoryContext) set; set = (GenerationContext *) malloc(headerSize);
} if (set == NULL)
{
MemoryContextStats(TopMemoryContext);
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory"),
errdetail("Failed while creating memory context \"%s\".",
name)));
}
/* /*
* GenerationInit * Avoid writing code that can fail between here and MemoryContextCreate;
* Context-type-specific initialization routine. * we'd leak the header if we ereport in this stretch.
*/ */
static void
GenerationInit(MemoryContext context)
{
GenerationContext *set = (GenerationContext *) context;
/* Fill in GenerationContext-specific header fields */
set->blockSize = blockSize;
set->headerSize = headerSize;
set->block = NULL; set->block = NULL;
dlist_init(&set->blocks); dlist_init(&set->blocks);
/* Finally, do the type-independent part of context creation */
MemoryContextCreate((MemoryContext) set,
T_GenerationContext,
headerSize,
sizeof(GenerationContext),
&GenerationMethods,
parent,
name,
flags);
return (MemoryContext) set;
} }
/* /*
...@@ -296,16 +319,15 @@ GenerationReset(MemoryContext context) ...@@ -296,16 +319,15 @@ GenerationReset(MemoryContext context)
/* /*
* GenerationDelete * GenerationDelete
* Frees all memory which is allocated in the given set, in preparation * Free all memory which is allocated in the given context.
* for deletion of the set. We simply call GenerationReset() which does
* all the dirty work.
*/ */
static void static void
GenerationDelete(MemoryContext context) GenerationDelete(MemoryContext context)
{ {
/* just reset to release all the GenerationBlocks */ /* Reset to release all the GenerationBlocks */
GenerationReset(context); GenerationReset(context);
/* we are not responsible for deleting the context node itself */ /* And free the context header */
free(context);
} }
/* /*
...@@ -659,7 +681,7 @@ GenerationIsEmpty(MemoryContext context) ...@@ -659,7 +681,7 @@ GenerationIsEmpty(MemoryContext context)
/* /*
* GenerationStats * GenerationStats
* Compute stats about memory consumption of an Generation. * Compute stats about memory consumption of a Generation context.
* *
* level: recursion level (0 at top level); used for print indentation. * level: recursion level (0 at top level); used for print indentation.
* print: true to print stats to stderr. * print: true to print stats to stderr.
...@@ -676,10 +698,13 @@ GenerationStats(MemoryContext context, int level, bool print, ...@@ -676,10 +698,13 @@ GenerationStats(MemoryContext context, int level, bool print,
Size nblocks = 0; Size nblocks = 0;
Size nchunks = 0; Size nchunks = 0;
Size nfreechunks = 0; Size nfreechunks = 0;
Size totalspace = 0; Size totalspace;
Size freespace = 0; Size freespace = 0;
dlist_iter iter; dlist_iter iter;
/* Include context header in totalspace */
totalspace = set->headerSize;
dlist_foreach(iter, &set->blocks) dlist_foreach(iter, &set->blocks)
{ {
GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur); GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
...@@ -727,7 +752,7 @@ static void ...@@ -727,7 +752,7 @@ static void
GenerationCheck(MemoryContext context) GenerationCheck(MemoryContext context)
{ {
GenerationContext *gen = (GenerationContext *) context; GenerationContext *gen = (GenerationContext *) context;
char *name = context->name; const char *name = context->name;
dlist_iter iter; dlist_iter iter;
/* walk all blocks in this context */ /* walk all blocks in this context */
......
...@@ -91,9 +91,7 @@ MemoryContextInit(void) ...@@ -91,9 +91,7 @@ MemoryContextInit(void)
AssertState(TopMemoryContext == NULL); AssertState(TopMemoryContext == NULL);
/* /*
* First, initialize TopMemoryContext, which will hold the MemoryContext * First, initialize TopMemoryContext, which is the parent of all others.
* nodes for all other contexts. (There is special-case code in
* MemoryContextCreate() to handle this call.)
*/ */
TopMemoryContext = AllocSetContextCreate((MemoryContext) NULL, TopMemoryContext = AllocSetContextCreate((MemoryContext) NULL,
"TopMemoryContext", "TopMemoryContext",
...@@ -118,8 +116,9 @@ MemoryContextInit(void) ...@@ -118,8 +116,9 @@ MemoryContextInit(void)
* This should be the last step in this function, as elog.c assumes memory * This should be the last step in this function, as elog.c assumes memory
* management works once ErrorContext is non-null. * management works once ErrorContext is non-null.
*/ */
ErrorContext = AllocSetContextCreate(TopMemoryContext, ErrorContext = AllocSetContextCreateExtended(TopMemoryContext,
"ErrorContext", "ErrorContext",
0,
8 * 1024, 8 * 1024,
8 * 1024, 8 * 1024,
8 * 1024); 8 * 1024);
...@@ -191,10 +190,9 @@ MemoryContextResetChildren(MemoryContext context) ...@@ -191,10 +190,9 @@ MemoryContextResetChildren(MemoryContext context)
* Delete a context and its descendants, and release all space * Delete a context and its descendants, and release all space
* allocated therein. * allocated therein.
* *
* The type-specific delete routine removes all subsidiary storage * The type-specific delete routine removes all storage for the context,
* for the context, but we have to delete the context node itself, * but we have to recurse to handle the children.
* as well as recurse to get the children. We must also delink the * We must also delink the context from its parent, if it has one.
* node from its parent, if it has one.
*/ */
void void
MemoryContextDelete(MemoryContext context) MemoryContextDelete(MemoryContext context)
...@@ -205,6 +203,8 @@ MemoryContextDelete(MemoryContext context) ...@@ -205,6 +203,8 @@ MemoryContextDelete(MemoryContext context)
/* And not CurrentMemoryContext, either */ /* And not CurrentMemoryContext, either */
Assert(context != CurrentMemoryContext); Assert(context != CurrentMemoryContext);
/* save a function call in common case where there are no children */
if (context->firstchild != NULL)
MemoryContextDeleteChildren(context); MemoryContextDeleteChildren(context);
/* /*
...@@ -223,8 +223,8 @@ MemoryContextDelete(MemoryContext context) ...@@ -223,8 +223,8 @@ MemoryContextDelete(MemoryContext context)
MemoryContextSetParent(context, NULL); MemoryContextSetParent(context, NULL);
context->methods->delete_context(context); context->methods->delete_context(context);
VALGRIND_DESTROY_MEMPOOL(context); VALGRIND_DESTROY_MEMPOOL(context);
pfree(context);
} }
/* /*
...@@ -587,100 +587,85 @@ MemoryContextContains(MemoryContext context, void *pointer) ...@@ -587,100 +587,85 @@ MemoryContextContains(MemoryContext context, void *pointer)
return ptr_context == context; return ptr_context == context;
} }
/*-------------------- /*
* MemoryContextCreate * MemoryContextCreate
* Context-type-independent part of context creation. * Context-type-independent part of context creation.
* *
* This is only intended to be called by context-type-specific * This is only intended to be called by context-type-specific
* context creation routines, not by the unwashed masses. * context creation routines, not by the unwashed masses.
* *
* The context creation procedure is a little bit tricky because * The memory context creation procedure goes like this:
* we want to be sure that we don't leave the context tree invalid * 1. Context-type-specific routine makes some initial space allocation,
* in case of failure (such as insufficient memory to allocate the * including enough space for the context header. If it fails,
* context node itself). The procedure goes like this: * it can ereport() with no damage done.
* 1. Context-type-specific routine first calls MemoryContextCreate(), * 2. Context-type-specific routine sets up all type-specific fields of
* passing the appropriate tag/size/methods values (the methods * the header (those beyond MemoryContextData proper), as well as any
* pointer will ordinarily point to statically allocated data). * other management fields it needs to have a fully valid context.
* The parent and name parameters usually come from the caller. * Usually, failure in this step is impossible, but if it's possible
* 2. MemoryContextCreate() attempts to allocate the context node, * the initial space allocation should be freed before ereport'ing.
* plus space for the name. If this fails we can ereport() with no * 3. Context-type-specific routine calls MemoryContextCreate() to fill in
* damage done. * the generic header fields and link the context into the context tree.
* 3. We fill in all of the type-independent MemoryContext fields. * 4. We return to the context-type-specific routine, which finishes
* 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
* 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
* up type-specific initialization. This routine can now do things * up type-specific initialization. This routine can now do things
* that might fail (like allocate more memory), so long as it's * that might fail (like allocate more memory), so long as it's
* sure the node is left in a state that delete will handle. * sure the node is left in a state that delete will handle.
* *
* This protocol doesn't prevent us from leaking memory if step 6 fails * node: the as-yet-uninitialized common part of the context header node.
* during creation of a top-level context, since there's no parent link * tag: NodeTag code identifying the memory context type.
* in that case. However, if you run out of memory while you're building * size: total size of context header including context-type-specific fields,
* a top-level context, you might as well go home anyway... * as well as space for the context name if MEMCONTEXT_COPY_NAME is set.
* nameoffset: where within the "size" space to insert the context name.
* methods: context-type-specific methods (usually statically allocated).
* parent: parent context, or NULL if this will be a top-level context.
* name: name of context (for debugging only, need not be unique).
* flags: bitmask of MEMCONTEXT_XXX option flags.
* *
* Normally, the context node and the name are allocated from * Context routines generally assume that MemoryContextCreate can't fail,
* TopMemoryContext (NOT from the parent context, since the node must * so this can contain Assert but not elog/ereport.
* 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.
*
* Note that the name field of a MemoryContext does not point to
* separately-allocated storage, so it should not be freed at context
* deletion.
*--------------------
*/ */
MemoryContext void
MemoryContextCreate(NodeTag tag, Size size, MemoryContextCreate(MemoryContext node,
MemoryContextMethods *methods, NodeTag tag, Size size, Size nameoffset,
const MemoryContextMethods *methods,
MemoryContext parent, MemoryContext parent,
const char *name) const char *name,
int flags)
{ {
MemoryContext node; /* Creating new memory contexts is not allowed in a critical section */
Size needed = size + strlen(name) + 1;
/* creating new memory contexts is not allowed in a critical section */
Assert(CritSectionCount == 0); Assert(CritSectionCount == 0);
/* Get space for node and name */ /* Check size is sane */
if (TopMemoryContext != NULL) Assert(nameoffset >= sizeof(MemoryContextData));
{ Assert((flags & MEMCONTEXT_COPY_NAME) ?
/* Normal case: allocate the node in TopMemoryContext */ size >= nameoffset + strlen(name) + 1 :
node = (MemoryContext) MemoryContextAlloc(TopMemoryContext, size >= nameoffset);
needed);
}
else
{
/* Special case for startup: use good ol' malloc */
node = (MemoryContext) malloc(needed);
Assert(node != NULL);
}
/* Initialize the node as best we can */ /* Initialize all standard fields of memory context header */
MemSet(node, 0, size);
node->type = tag; node->type = tag;
node->isReset = true;
node->methods = methods; node->methods = methods;
node->parent = NULL; /* for the moment */ node->parent = parent;
node->firstchild = NULL; node->firstchild = NULL;
node->prevchild = NULL; node->prevchild = NULL;
node->nextchild = NULL; node->reset_cbs = NULL;
node->isReset = true;
node->name = ((char *) node) + size; if (flags & MEMCONTEXT_COPY_NAME)
strcpy(node->name, name); {
/* Insert context name into space reserved for it */
char *namecopy = ((char *) node) + nameoffset;
/* Type-specific routine finishes any other essential initialization */ node->name = namecopy;
node->methods->init(node); strcpy(namecopy, name);
}
else
{
/* Assume the passed-in name is statically allocated */
node->name = name;
}
/* OK to link node to parent (if any) */ /* OK to link node into context tree */
/* Could use MemoryContextSetParent here, but doesn't seem worthwhile */
if (parent) if (parent)
{ {
node->parent = parent;
node->nextchild = parent->firstchild; node->nextchild = parent->firstchild;
if (parent->firstchild != NULL) if (parent->firstchild != NULL)
parent->firstchild->prevchild = node; parent->firstchild->prevchild = node;
...@@ -688,11 +673,13 @@ MemoryContextCreate(NodeTag tag, Size size, ...@@ -688,11 +673,13 @@ MemoryContextCreate(NodeTag tag, Size size,
/* inherit allowInCritSection flag from parent */ /* inherit allowInCritSection flag from parent */
node->allowInCritSection = parent->allowInCritSection; node->allowInCritSection = parent->allowInCritSection;
} }
else
{
node->nextchild = NULL;
node->allowInCritSection = false;
}
VALGRIND_CREATE_MEMPOOL(node, 0, false); VALGRIND_CREATE_MEMPOOL(node, 0, false);
/* Return to type-specific creation routine to finish up */
return node;
} }
/* /*
......
...@@ -67,6 +67,7 @@ typedef struct SlabContext ...@@ -67,6 +67,7 @@ typedef struct SlabContext
Size chunkSize; /* chunk size */ Size chunkSize; /* chunk size */
Size fullChunkSize; /* chunk size including header and alignment */ Size fullChunkSize; /* chunk size including header and alignment */
Size blockSize; /* block size */ Size blockSize; /* block size */
Size headerSize; /* allocated size of context header */
int chunksPerBlock; /* number of chunks per block */ int chunksPerBlock; /* number of chunks per block */
int minFreeChunks; /* min number of free chunks in any block */ int minFreeChunks; /* min number of free chunks in any block */
int nblocks; /* number of blocks allocated */ int nblocks; /* number of blocks allocated */
...@@ -126,7 +127,6 @@ typedef struct SlabChunk ...@@ -126,7 +127,6 @@ typedef struct SlabChunk
static void *SlabAlloc(MemoryContext context, Size size); static void *SlabAlloc(MemoryContext context, Size size);
static void SlabFree(MemoryContext context, void *pointer); static void SlabFree(MemoryContext context, void *pointer);
static void *SlabRealloc(MemoryContext context, void *pointer, Size size); static void *SlabRealloc(MemoryContext context, void *pointer, Size size);
static void SlabInit(MemoryContext context);
static void SlabReset(MemoryContext context); static void SlabReset(MemoryContext context);
static void SlabDelete(MemoryContext context); static void SlabDelete(MemoryContext context);
static Size SlabGetChunkSpace(MemoryContext context, void *pointer); static Size SlabGetChunkSpace(MemoryContext context, void *pointer);
...@@ -140,11 +140,10 @@ static void SlabCheck(MemoryContext context); ...@@ -140,11 +140,10 @@ static void SlabCheck(MemoryContext context);
/* /*
* This is the virtual function table for Slab contexts. * This is the virtual function table for Slab contexts.
*/ */
static MemoryContextMethods SlabMethods = { static const MemoryContextMethods SlabMethods = {
SlabAlloc, SlabAlloc,
SlabFree, SlabFree,
SlabRealloc, SlabRealloc,
SlabInit,
SlabReset, SlabReset,
SlabDelete, SlabDelete,
SlabGetChunkSpace, SlabGetChunkSpace,
...@@ -177,24 +176,30 @@ static MemoryContextMethods SlabMethods = { ...@@ -177,24 +176,30 @@ static MemoryContextMethods SlabMethods = {
* Create a new Slab context. * Create a new Slab context.
* *
* parent: parent context, or NULL if top-level 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)
* flags: bitmask of MEMCONTEXT_XXX option flags
* blockSize: allocation block size * blockSize: allocation block size
* chunkSize: allocation chunk size * chunkSize: allocation chunk size
* *
* Notes: if flags & MEMCONTEXT_COPY_NAME, the name string will be copied into
* context-lifespan storage; otherwise, it had better be statically allocated.
* The chunkSize may not exceed: * The chunkSize may not exceed:
* MAXALIGN_DOWN(SIZE_MAX) - MAXALIGN(sizeof(SlabBlock)) - SLAB_CHUNKHDRSZ * MAXALIGN_DOWN(SIZE_MAX) - MAXALIGN(sizeof(SlabBlock)) - SLAB_CHUNKHDRSZ
*
*/ */
MemoryContext MemoryContext
SlabContextCreate(MemoryContext parent, SlabContextCreate(MemoryContext parent,
const char *name, const char *name,
int flags,
Size blockSize, Size blockSize,
Size chunkSize) Size chunkSize)
{ {
int chunksPerBlock; int chunksPerBlock;
Size fullChunkSize; Size fullChunkSize;
Size freelistSize; Size freelistSize;
Size nameOffset;
Size headerSize;
SlabContext *slab; SlabContext *slab;
int i;
/* Assert we padded SlabChunk properly */ /* Assert we padded SlabChunk properly */
StaticAssertStmt(sizeof(SlabChunk) == MAXALIGN(sizeof(SlabChunk)), StaticAssertStmt(sizeof(SlabChunk) == MAXALIGN(sizeof(SlabChunk)),
...@@ -211,7 +216,7 @@ SlabContextCreate(MemoryContext parent, ...@@ -211,7 +216,7 @@ SlabContextCreate(MemoryContext parent,
fullChunkSize = sizeof(SlabChunk) + MAXALIGN(chunkSize); fullChunkSize = sizeof(SlabChunk) + MAXALIGN(chunkSize);
/* Make sure the block can store at least one chunk. */ /* Make sure the block can store at least one chunk. */
if (blockSize - sizeof(SlabBlock) < fullChunkSize) if (blockSize < fullChunkSize + sizeof(SlabBlock))
elog(ERROR, "block size %zu for slab is too small for %zu chunks", elog(ERROR, "block size %zu for slab is too small for %zu chunks",
blockSize, chunkSize); blockSize, chunkSize);
...@@ -221,45 +226,58 @@ SlabContextCreate(MemoryContext parent, ...@@ -221,45 +226,58 @@ SlabContextCreate(MemoryContext parent,
/* The freelist starts with 0, ends with chunksPerBlock. */ /* The freelist starts with 0, ends with chunksPerBlock. */
freelistSize = sizeof(dlist_head) * (chunksPerBlock + 1); freelistSize = sizeof(dlist_head) * (chunksPerBlock + 1);
/* if we can't fit at least one chunk into the block, we're hosed */ /*
Assert(chunksPerBlock > 0); * Allocate the context header. Unlike aset.c, we never try to combine
* this with the first regular block; not worth the extra complication.
*/
/* make sure the chunks actually fit on the block */ /* Size of the memory context header, including name storage if needed */
Assert((fullChunkSize * chunksPerBlock) + sizeof(SlabBlock) <= blockSize); nameOffset = offsetof(SlabContext, freelist) + freelistSize;
if (flags & MEMCONTEXT_COPY_NAME)
headerSize = nameOffset + strlen(name) + 1;
else
headerSize = nameOffset;
/* Do the type-independent part of context creation */ slab = (SlabContext *) malloc(headerSize);
slab = (SlabContext *) if (slab == NULL)
MemoryContextCreate(T_SlabContext, {
(offsetof(SlabContext, freelist) + freelistSize), MemoryContextStats(TopMemoryContext);
&SlabMethods, ereport(ERROR,
parent, (errcode(ERRCODE_OUT_OF_MEMORY),
name); errmsg("out of memory"),
errdetail("Failed while creating memory context \"%s\".",
name)));
}
slab->blockSize = blockSize; /*
* Avoid writing code that can fail between here and MemoryContextCreate;
* we'd leak the header if we ereport in this stretch.
*/
/* Fill in SlabContext-specific header fields */
slab->chunkSize = chunkSize; slab->chunkSize = chunkSize;
slab->fullChunkSize = fullChunkSize; slab->fullChunkSize = fullChunkSize;
slab->blockSize = blockSize;
slab->headerSize = headerSize;
slab->chunksPerBlock = chunksPerBlock; slab->chunksPerBlock = chunksPerBlock;
slab->nblocks = 0;
slab->minFreeChunks = 0; slab->minFreeChunks = 0;
slab->nblocks = 0;
return (MemoryContext) slab;
}
/*
* SlabInit
* Context-type-specific initialization routine.
*/
static void
SlabInit(MemoryContext context)
{
int i;
SlabContext *slab = castNode(SlabContext, context);
Assert(slab);
/* initialize the freelist slots */ /* initialize the freelist slots */
for (i = 0; i < (slab->chunksPerBlock + 1); i++) for (i = 0; i < (slab->chunksPerBlock + 1); i++)
dlist_init(&slab->freelist[i]); dlist_init(&slab->freelist[i]);
/* Finally, do the type-independent part of context creation */
MemoryContextCreate((MemoryContext) slab,
T_SlabContext,
headerSize,
nameOffset,
&SlabMethods,
parent,
name,
flags);
return (MemoryContext) slab;
} }
/* /*
...@@ -308,14 +326,15 @@ SlabReset(MemoryContext context) ...@@ -308,14 +326,15 @@ SlabReset(MemoryContext context)
/* /*
* SlabDelete * SlabDelete
* Frees all memory which is allocated in the given slab, in preparation * Free all memory which is allocated in the given context.
* for deletion of the slab. We simply call SlabReset().
*/ */
static void static void
SlabDelete(MemoryContext context) SlabDelete(MemoryContext context)
{ {
/* just reset the context */ /* Reset to release all the SlabBlocks */
SlabReset(context); SlabReset(context);
/* And free the context header */
free(context);
} }
/* /*
...@@ -613,7 +632,7 @@ SlabIsEmpty(MemoryContext context) ...@@ -613,7 +632,7 @@ SlabIsEmpty(MemoryContext context)
/* /*
* SlabStats * SlabStats
* Compute stats about memory consumption of an Slab. * Compute stats about memory consumption of a Slab context.
* *
* level: recursion level (0 at top level); used for print indentation. * level: recursion level (0 at top level); used for print indentation.
* print: true to print stats to stderr. * print: true to print stats to stderr.
...@@ -626,11 +645,12 @@ SlabStats(MemoryContext context, int level, bool print, ...@@ -626,11 +645,12 @@ SlabStats(MemoryContext context, int level, bool print,
SlabContext *slab = castNode(SlabContext, context); SlabContext *slab = castNode(SlabContext, context);
Size nblocks = 0; Size nblocks = 0;
Size freechunks = 0; Size freechunks = 0;
Size totalspace = 0; Size totalspace;
Size freespace = 0; Size freespace = 0;
int i; int i;
Assert(slab); /* Include context header in totalspace */
totalspace = slab->headerSize;
for (i = 0; i <= slab->chunksPerBlock; i++) for (i = 0; i <= slab->chunksPerBlock; i++)
{ {
...@@ -682,7 +702,7 @@ SlabCheck(MemoryContext context) ...@@ -682,7 +702,7 @@ SlabCheck(MemoryContext context)
{ {
int i; int i;
SlabContext *slab = castNode(SlabContext, context); SlabContext *slab = castNode(SlabContext, context);
char *name = slab->header.name; const char *name = slab->header.name;
char *freechunks; char *freechunks;
Assert(slab); Assert(slab);
......
...@@ -57,7 +57,6 @@ typedef struct MemoryContextMethods ...@@ -57,7 +57,6 @@ typedef struct MemoryContextMethods
/* call this free_p in case someone #define's free() */ /* call this free_p in case someone #define's free() */
void (*free_p) (MemoryContext context, void *pointer); void (*free_p) (MemoryContext context, void *pointer);
void *(*realloc) (MemoryContext context, void *pointer, Size size); void *(*realloc) (MemoryContext context, void *pointer, Size size);
void (*init) (MemoryContext context);
void (*reset) (MemoryContext context); void (*reset) (MemoryContext context);
void (*delete_context) (MemoryContext context); void (*delete_context) (MemoryContext context);
Size (*get_chunk_space) (MemoryContext context, void *pointer); Size (*get_chunk_space) (MemoryContext context, void *pointer);
...@@ -76,12 +75,12 @@ typedef struct MemoryContextData ...@@ -76,12 +75,12 @@ typedef struct MemoryContextData
/* these two fields are placed here to minimize alignment wastage: */ /* these two fields are placed here to minimize alignment wastage: */
bool isReset; /* T = no space alloced since last reset */ bool isReset; /* T = no space alloced since last reset */
bool allowInCritSection; /* allow palloc in critical section */ bool allowInCritSection; /* allow palloc in critical section */
MemoryContextMethods *methods; /* virtual function table */ const MemoryContextMethods *methods; /* virtual function table */
MemoryContext parent; /* NULL if no parent (toplevel context) */ MemoryContext parent; /* NULL if no parent (toplevel context) */
MemoryContext firstchild; /* head of linked list of children */ MemoryContext firstchild; /* head of linked list of children */
MemoryContext prevchild; /* previous child of same parent */ MemoryContext prevchild; /* previous child of same parent */
MemoryContext nextchild; /* next child of same parent */ MemoryContext nextchild; /* next child of same parent */
char *name; /* context name (just for debugging) */ const char *name; /* context name (just for debugging) */
MemoryContextCallback *reset_cbs; /* list of reset/delete callbacks */ MemoryContextCallback *reset_cbs; /* list of reset/delete callbacks */
} MemoryContextData; } MemoryContextData;
......
...@@ -132,10 +132,12 @@ GetMemoryChunkContext(void *pointer) ...@@ -132,10 +132,12 @@ GetMemoryChunkContext(void *pointer)
* context creation. It's intended to be called from context-type- * context creation. It's intended to be called from context-type-
* specific creation routines, and noplace else. * specific creation routines, and noplace else.
*/ */
extern MemoryContext MemoryContextCreate(NodeTag tag, Size size, extern void MemoryContextCreate(MemoryContext node,
MemoryContextMethods *methods, NodeTag tag, Size size, Size nameoffset,
const MemoryContextMethods *methods,
MemoryContext parent, MemoryContext parent,
const char *name); const char *name,
int flags);
/* /*
...@@ -143,23 +145,48 @@ extern MemoryContext MemoryContextCreate(NodeTag tag, Size size, ...@@ -143,23 +145,48 @@ extern MemoryContext MemoryContextCreate(NodeTag tag, Size size,
*/ */
/* aset.c */ /* aset.c */
extern MemoryContext AllocSetContextCreate(MemoryContext parent, extern MemoryContext AllocSetContextCreateExtended(MemoryContext parent,
const char *name, const char *name,
int flags,
Size minContextSize, Size minContextSize,
Size initBlockSize, Size initBlockSize,
Size maxBlockSize); Size maxBlockSize);
/*
* This backwards compatibility macro only works for constant context names,
* and you must specify block sizes with one of the abstraction macros below.
*/
#ifdef HAVE__BUILTIN_CONSTANT_P
#define AllocSetContextCreate(parent, name, allocparams) \
(StaticAssertExpr(__builtin_constant_p(name), \
"Use AllocSetContextCreateExtended with MEMCONTEXT_COPY_NAME for non-constant context names"), \
AllocSetContextCreateExtended(parent, name, 0, allocparams))
#else
#define AllocSetContextCreate(parent, name, allocparams) \
AllocSetContextCreateExtended(parent, name, 0, allocparams)
#endif
/* slab.c */ /* slab.c */
extern MemoryContext SlabContextCreate(MemoryContext parent, extern MemoryContext SlabContextCreate(MemoryContext parent,
const char *name, const char *name,
int flags,
Size blockSize, Size blockSize,
Size chunkSize); Size chunkSize);
/* generation.c */ /* generation.c */
extern MemoryContext GenerationContextCreate(MemoryContext parent, extern MemoryContext GenerationContextCreate(MemoryContext parent,
const char *name, const char *name,
int flags,
Size blockSize); Size blockSize);
/*
* Flag option bits for FooContextCreate functions.
* In future, some of these might be relevant to only some context types.
*
* COPY_NAME: FooContextCreate's name argument is not a constant string
*/
#define MEMCONTEXT_COPY_NAME 0x0001 /* is passed name transient? */
/* /*
* Recommended default alloc parameters, suitable for "ordinary" contexts * Recommended default alloc parameters, suitable for "ordinary" contexts
* that might hold quite a lot of data. * that might hold quite a lot of data.
......
...@@ -2777,8 +2777,9 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger) ...@@ -2777,8 +2777,9 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
/************************************************************ /************************************************************
* Allocate a context that will hold all PG data for the procedure. * Allocate a context that will hold all PG data for the procedure.
************************************************************/ ************************************************************/
proc_cxt = AllocSetContextCreate(TopMemoryContext, proc_cxt = AllocSetContextCreateExtended(TopMemoryContext,
NameStr(procStruct->proname), NameStr(procStruct->proname),
MEMCONTEXT_COPY_NAME,
ALLOCSET_SMALL_SIZES); ALLOCSET_SMALL_SIZES);
/************************************************************ /************************************************************
......
...@@ -166,8 +166,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) ...@@ -166,8 +166,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
} }
/* Create long-lived context that all procedure info will live in */ /* Create long-lived context that all procedure info will live in */
cxt = AllocSetContextCreate(TopMemoryContext, cxt = AllocSetContextCreateExtended(TopMemoryContext,
procName, procName,
MEMCONTEXT_COPY_NAME,
ALLOCSET_DEFAULT_SIZES); ALLOCSET_DEFAULT_SIZES);
oldcxt = MemoryContextSwitchTo(cxt); oldcxt = MemoryContextSwitchTo(cxt);
......
...@@ -146,7 +146,7 @@ typedef struct pltcl_proc_desc ...@@ -146,7 +146,7 @@ typedef struct pltcl_proc_desc
Oid result_typid; /* OID of fn's result type */ Oid result_typid; /* OID of fn's result type */
FmgrInfo result_in_func; /* input function for fn's result type */ FmgrInfo result_in_func; /* input function for fn's result type */
Oid result_typioparam; /* param to pass to same */ Oid result_typioparam; /* param to pass to same */
bool fn_is_procedure;/* true if this is a procedure */ bool fn_is_procedure; /* true if this is a procedure */
bool fn_retisset; /* true if function returns a set */ bool fn_retisset; /* true if function returns a set */
bool fn_retistuple; /* true if function returns composite */ bool fn_retistuple; /* true if function returns composite */
bool fn_retisdomain; /* true if function returns domain */ bool fn_retisdomain; /* true if function returns domain */
...@@ -1471,8 +1471,9 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, ...@@ -1471,8 +1471,9 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
* Allocate a context that will hold all PG data for the procedure. * Allocate a context that will hold all PG data for the procedure.
* We use the internal proc name as the context name. * We use the internal proc name as the context name.
************************************************************/ ************************************************************/
proc_cxt = AllocSetContextCreate(TopMemoryContext, proc_cxt = AllocSetContextCreateExtended(TopMemoryContext,
internal_proname, internal_proname,
MEMCONTEXT_COPY_NAME,
ALLOCSET_SMALL_SIZES); ALLOCSET_SMALL_SIZES);
/************************************************************ /************************************************************
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment