mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
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.
This commit is contained in:
@ -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)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
/* 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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
/* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
_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)
|
|||||||
|
|
||||||
/* 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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
* 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);
|
|||||||
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 = {
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
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
|
|||||||
pgport_pfree(void *pointer)
|
pgport_pfree(void *pointer)
|
||||||
{
|
{
|
||||||
pfree(pointer);
|
pfree(pointer);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif /* WIN32 */
|
||||||
|
@ -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
|
|||||||
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 @@
|
|||||||
* 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);
|
|||||||
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
|
||||||
|
Reference in New Issue
Block a user