Commit 257cccbe authored by Tom Lane's avatar Tom Lane

Add some marginal tweaks to eliminate memory leakages associated with

subtransactions.  Trivial subxacts (such as a plpgsql exception block
containing no database access) now demonstrably leak zero bytes.
parent 86fff990
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.189 2004/09/16 16:58:26 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.190 2004/09/16 20:17:16 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -861,9 +861,6 @@ AtCommit_Memory(void) ...@@ -861,9 +861,6 @@ AtCommit_Memory(void)
/* /*
* AtSubCommit_Memory * AtSubCommit_Memory
*
* We do not throw away the child's CurTransactionContext, since the data
* it contains will be needed at upper commit.
*/ */
static void static void
AtSubCommit_Memory(void) AtSubCommit_Memory(void)
...@@ -875,6 +872,18 @@ AtSubCommit_Memory(void) ...@@ -875,6 +872,18 @@ AtSubCommit_Memory(void)
/* Return to parent transaction level's memory context. */ /* Return to parent transaction level's memory context. */
CurTransactionContext = s->parent->curTransactionContext; CurTransactionContext = s->parent->curTransactionContext;
MemoryContextSwitchTo(CurTransactionContext); MemoryContextSwitchTo(CurTransactionContext);
/*
* Ordinarily we cannot throw away the child's CurTransactionContext,
* since the data it contains will be needed at upper commit. However,
* if there isn't actually anything in it, we can throw it away. This
* avoids a small memory leak in the common case of "trivial" subxacts.
*/
if (MemoryContextIsEmpty(s->curTransactionContext))
{
MemoryContextDelete(s->curTransactionContext);
s->curTransactionContext = NULL;
}
} }
/* /*
...@@ -890,13 +899,27 @@ AtSubCommit_childXids(void) ...@@ -890,13 +899,27 @@ AtSubCommit_childXids(void)
Assert(s->parent != NULL); Assert(s->parent != NULL);
old_cxt = MemoryContextSwitchTo(s->parent->curTransactionContext); /*
* We keep the child-XID lists in TopTransactionContext; this avoids
* setting up child-transaction contexts for what might be just a few
* bytes of grandchild XIDs.
*/
old_cxt = MemoryContextSwitchTo(TopTransactionContext);
s->parent->childXids = lappend_xid(s->parent->childXids, s->parent->childXids = lappend_xid(s->parent->childXids,
s->transactionId); s->transactionId);
s->parent->childXids = list_concat(s->parent->childXids, s->childXids); if (s->childXids != NIL)
s->childXids = NIL; /* ensure list not doubly referenced */ {
s->parent->childXids = list_concat(s->parent->childXids,
s->childXids);
/*
* list_concat doesn't free the list header for the second list;
* do so here to avoid memory leakage (kluge)
*/
pfree(s->childXids);
s->childXids = NIL;
}
MemoryContextSwitchTo(old_cxt); MemoryContextSwitchTo(old_cxt);
} }
...@@ -1092,6 +1115,23 @@ AtSubAbort_Memory(void) ...@@ -1092,6 +1115,23 @@ AtSubAbort_Memory(void)
MemoryContextSwitchTo(TopTransactionContext); MemoryContextSwitchTo(TopTransactionContext);
} }
/*
* AtSubAbort_childXids
*/
static void
AtSubAbort_childXids(void)
{
TransactionState s = CurrentTransactionState;
/*
* We keep the child-XID lists in TopTransactionContext (see
* AtSubCommit_childXids). This means we'd better free the list
* explicitly at abort to avoid leakage.
*/
list_free(s->childXids);
s->childXids = NIL;
}
/* /*
* RecordSubTransactionAbort * RecordSubTransactionAbort
*/ */
...@@ -3317,7 +3357,10 @@ AbortSubTransaction(void) ...@@ -3317,7 +3357,10 @@ AbortSubTransaction(void)
/* Advertise the fact that we aborted in pg_clog. */ /* Advertise the fact that we aborted in pg_clog. */
if (TransactionIdIsValid(s->transactionId)) if (TransactionIdIsValid(s->transactionId))
{
RecordSubTransactionAbort(); RecordSubTransactionAbort();
AtSubAbort_childXids();
}
/* Post-abort cleanup */ /* Post-abort cleanup */
CallSubXactCallbacks(SUBXACT_EVENT_ABORT_SUB, s->subTransactionId, CallSubXactCallbacks(SUBXACT_EVENT_ABORT_SUB, s->subTransactionId,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.128 2004/09/16 16:58:29 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.129 2004/09/16 20:17:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -104,6 +104,8 @@ SPI_connect(void) ...@@ -104,6 +104,8 @@ SPI_connect(void)
_SPI_current = &(_SPI_stack[_SPI_connected]); _SPI_current = &(_SPI_stack[_SPI_connected]);
_SPI_current->processed = 0; _SPI_current->processed = 0;
_SPI_current->tuptable = NULL; _SPI_current->tuptable = NULL;
_SPI_current->procCxt = NULL; /* in case we fail to create 'em */
_SPI_current->execCxt = NULL;
_SPI_current->connectSubid = GetCurrentSubTransactionId(); _SPI_current->connectSubid = GetCurrentSubTransactionId();
/* /*
...@@ -144,7 +146,9 @@ SPI_finish(void) ...@@ -144,7 +146,9 @@ SPI_finish(void)
/* Release memory used in procedure call */ /* Release memory used in procedure call */
MemoryContextDelete(_SPI_current->execCxt); MemoryContextDelete(_SPI_current->execCxt);
_SPI_current->execCxt = NULL;
MemoryContextDelete(_SPI_current->procCxt); MemoryContextDelete(_SPI_current->procCxt);
_SPI_current->procCxt = NULL;
/* /*
* Reset result variables, especially SPI_tuptable which is probably * Reset result variables, especially SPI_tuptable which is probably
...@@ -214,11 +218,24 @@ AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid) ...@@ -214,11 +218,24 @@ AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
found = true; found = true;
/*
* Release procedure memory explicitly (see note in SPI_connect)
*/
if (connection->execCxt)
{
MemoryContextDelete(connection->execCxt);
connection->execCxt = NULL;
}
if (connection->procCxt)
{
MemoryContextDelete(connection->procCxt);
connection->procCxt = NULL;
}
/* /*
* Pop the stack entry and reset global variables. Unlike * Pop the stack entry and reset global variables. Unlike
* SPI_finish(), we don't risk switching to memory contexts that * SPI_finish(), we don't risk switching to memory contexts that
* might be already gone, or deleting memory contexts that have * might be already gone.
* been or will be thrown away anyway.
*/ */
_SPI_connected--; _SPI_connected--;
_SPI_curid = _SPI_connected; _SPI_curid = _SPI_connected;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.57 2004/08/29 05:06:51 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.58 2004/09/16 20:17:33 tgl Exp $
* *
* NOTE: * NOTE:
* This is a new (Feb. 05, 1999) implementation of the allocation set * This is a new (Feb. 05, 1999) implementation of the allocation set
...@@ -205,6 +205,7 @@ static void AllocSetInit(MemoryContext context); ...@@ -205,6 +205,7 @@ static void AllocSetInit(MemoryContext context);
static void AllocSetReset(MemoryContext context); static void AllocSetReset(MemoryContext context);
static void AllocSetDelete(MemoryContext context); static void AllocSetDelete(MemoryContext context);
static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer); static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer);
static bool AllocSetIsEmpty(MemoryContext context);
static void AllocSetStats(MemoryContext context); static void AllocSetStats(MemoryContext context);
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
...@@ -222,6 +223,7 @@ static MemoryContextMethods AllocSetMethods = { ...@@ -222,6 +223,7 @@ static MemoryContextMethods AllocSetMethods = {
AllocSetReset, AllocSetReset,
AllocSetDelete, AllocSetDelete,
AllocSetGetChunkSpace, AllocSetGetChunkSpace,
AllocSetIsEmpty,
AllocSetStats AllocSetStats
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
,AllocSetCheck ,AllocSetCheck
...@@ -991,6 +993,26 @@ AllocSetGetChunkSpace(MemoryContext context, void *pointer) ...@@ -991,6 +993,26 @@ AllocSetGetChunkSpace(MemoryContext context, void *pointer)
return chunk->size + ALLOC_CHUNKHDRSZ; return chunk->size + ALLOC_CHUNKHDRSZ;
} }
/*
* AllocSetIsEmpty
* Is an allocset empty of any allocated space?
*/
static bool
AllocSetIsEmpty(MemoryContext context)
{
AllocSet set = (AllocSet) context;
/*
* For now, we say "empty" only if the context never contained any
* space at all. We could examine the freelists to determine if all
* space has been freed, but it's not really worth the trouble for
* present uses of this functionality.
*/
if (set->blocks == NULL)
return true;
return false;
}
/* /*
* AllocSetStats * AllocSetStats
* Displays stats about memory consumption of an allocset. * Displays stats about memory consumption of an allocset.
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/mmgr/mcxt.c,v 1.50 2004/08/29 05:06:51 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/mmgr/mcxt.c,v 1.51 2004/09/16 20:17:33 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -291,6 +291,25 @@ GetMemoryChunkContext(void *pointer) ...@@ -291,6 +291,25 @@ GetMemoryChunkContext(void *pointer)
return header->context; return header->context;
} }
/*
* MemoryContextIsEmpty
* Is a memory context empty of any allocated space?
*/
bool
MemoryContextIsEmpty(MemoryContext context)
{
AssertArg(MemoryContextIsValid(context));
/*
* For now, we consider a memory context nonempty if it has any children;
* perhaps this should be changed later.
*/
if (context->firstchild != NULL)
return false;
/* Otherwise use the type-specific inquiry */
return (*context->methods->is_empty) (context);
}
/* /*
* MemoryContextStats * MemoryContextStats
* Print statistics about the named context and all its descendants. * Print statistics about the named context and all its descendants.
...@@ -662,7 +681,6 @@ void ...@@ -662,7 +681,6 @@ void
pgport_pfree(void *pointer) pgport_pfree(void *pointer)
{ {
pfree(pointer); pfree(pointer);
return;
} }
#endif #endif /* WIN32 */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/memnodes.h,v 1.28 2004/08/29 04:13:07 momjian Exp $ * $PostgreSQL: pgsql/src/include/nodes/memnodes.h,v 1.29 2004/09/16 20:17:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -43,6 +43,7 @@ typedef struct MemoryContextMethods ...@@ -43,6 +43,7 @@ typedef struct MemoryContextMethods
void (*reset) (MemoryContext context); void (*reset) (MemoryContext context);
void (*delete) (MemoryContext context); void (*delete) (MemoryContext context);
Size (*get_chunk_space) (MemoryContext context, void *pointer); Size (*get_chunk_space) (MemoryContext context, void *pointer);
bool (*is_empty) (MemoryContext context);
void (*stats) (MemoryContext context); void (*stats) (MemoryContext context);
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
void (*check) (MemoryContext context); void (*check) (MemoryContext context);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/memutils.h,v 1.57 2004/08/29 04:13:11 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/memutils.h,v 1.58 2004/09/16 20:17:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -91,6 +91,7 @@ extern void MemoryContextDeleteChildren(MemoryContext context); ...@@ -91,6 +91,7 @@ extern void MemoryContextDeleteChildren(MemoryContext context);
extern void MemoryContextResetAndDeleteChildren(MemoryContext context); extern void MemoryContextResetAndDeleteChildren(MemoryContext context);
extern Size GetMemoryChunkSpace(void *pointer); extern Size GetMemoryChunkSpace(void *pointer);
extern MemoryContext GetMemoryChunkContext(void *pointer); extern MemoryContext GetMemoryChunkContext(void *pointer);
extern bool MemoryContextIsEmpty(MemoryContext context);
extern void MemoryContextStats(MemoryContext context); extern void MemoryContextStats(MemoryContext context);
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
......
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