Invent a new memory context primitive, MemoryContextSetParent.

This function will be useful for altering the lifespan of a context after
creation (for example, by creating it under a transient context and later
reparenting it to belong to a long-lived context).  It costs almost no new
code, since we can refactor what was there.  Per my proposal of yesterday.
This commit is contained in:
Tom Lane 2011-09-11 16:29:42 -04:00
parent 5f42e5945b
commit b0025bd957
2 changed files with 65 additions and 19 deletions

View File

@ -178,26 +178,8 @@ MemoryContextDelete(MemoryContext context)
* there's an error we won't have deleted/busted contexts still attached
* to the context tree. Better a leak than a crash.
*/
if (context->parent)
{
MemoryContext parent = context->parent;
MemoryContextSetParent(context, NULL);
if (context == parent->firstchild)
parent->firstchild = context->nextchild;
else
{
MemoryContext child;
for (child = parent->firstchild; child; child = child->nextchild)
{
if (context == child->nextchild)
{
child->nextchild = context->nextchild;
break;
}
}
}
}
(*context->methods->delete_context) (context);
pfree(context);
}
@ -237,6 +219,67 @@ MemoryContextResetAndDeleteChildren(MemoryContext context)
MemoryContextReset(context);
}
/*
* MemoryContextSetParent
* Change a context to belong to a new parent (or no parent).
*
* We provide this as an API function because it is sometimes useful to
* change a context's lifespan after creation. For example, a context
* might be created underneath a transient context, filled with data,
* and then reparented underneath CacheMemoryContext to make it long-lived.
* In this way no special effort is needed to get rid of the context in case
* a failure occurs before its contents are completely set up.
*
* Callers often assume that this function cannot fail, so don't put any
* elog(ERROR) calls in it.
*
* A possible caller error is to reparent a context under itself, creating
* a loop in the context graph. We assert here that context != new_parent,
* but checking for multi-level loops seems more trouble than it's worth.
*/
void
MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
{
AssertArg(MemoryContextIsValid(context));
AssertArg(context != new_parent);
/* Delink from existing parent, if any */
if (context->parent)
{
MemoryContext parent = context->parent;
if (context == parent->firstchild)
parent->firstchild = context->nextchild;
else
{
MemoryContext child;
for (child = parent->firstchild; child; child = child->nextchild)
{
if (context == child->nextchild)
{
child->nextchild = context->nextchild;
break;
}
}
}
}
/* And relink */
if (new_parent)
{
AssertArg(MemoryContextIsValid(new_parent));
context->parent = new_parent;
context->nextchild = new_parent->firstchild;
new_parent->firstchild = context;
}
else
{
context->parent = NULL;
context->nextchild = NULL;
}
}
/*
* GetMemoryChunkSpace
* Given a currently-allocated chunk, determine the total space
@ -489,6 +532,7 @@ MemoryContextCreate(NodeTag tag, Size size,
(*node->methods->init) (node);
/* OK to link node to parent (if any) */
/* Could use MemoryContextSetParent here, but doesn't seem worthwhile */
if (parent)
{
node->parent = parent;

View File

@ -90,6 +90,8 @@ extern void MemoryContextDelete(MemoryContext context);
extern void MemoryContextResetChildren(MemoryContext context);
extern void MemoryContextDeleteChildren(MemoryContext context);
extern void MemoryContextResetAndDeleteChildren(MemoryContext context);
extern void MemoryContextSetParent(MemoryContext context,
MemoryContext new_parent);
extern Size GetMemoryChunkSpace(void *pointer);
extern MemoryContext GetMemoryChunkContext(void *pointer);
extern bool MemoryContextIsEmpty(MemoryContext context);