Tom Lane escribió: > Alvaro Herrera <[EMAIL PROTECTED]> writes: > > After the patch we don't have any way to detect whether resowner.c has > > any snapshot still linked to. I assume there's no objection to adding a > > new entry point in resowner.c for this. > > Hmm, that's a bit problematic because resowner.c doesn't have any global > notion of what resource owners exist. I think you still need to have > snapmgr.c maintain a list of all known snapshots. resowner.c can only > help you with tracking reference counts for particular snapshots.
A counter seems to suffice. Patch attached. -- Alvaro Herrera http://www.CommandPrompt.com/ The PostgreSQL Company - Command Prompt, Inc.
Index: src/backend/utils/resowner/resowner.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/utils/resowner/resowner.c,v retrieving revision 1.29 diff -c -p -r1.29 resowner.c *** src/backend/utils/resowner/resowner.c 19 Jun 2008 00:46:05 -0000 1.29 --- src/backend/utils/resowner/resowner.c 25 Nov 2008 17:10:22 -0000 *************** *** 26,31 **** --- 26,32 ---- #include "utils/memutils.h" #include "utils/rel.h" #include "utils/resowner.h" + #include "utils/snapmgr.h" /* *************** typedef struct ResourceOwnerData *** 66,71 **** --- 67,77 ---- int ntupdescs; /* number of owned tupdesc references */ TupleDesc *tupdescs; /* dynamically allocated array */ int maxtupdescs; /* currently allocated array size */ + + /* We have built-in support for remembering snapshot references */ + int nsnapshots; /* number of owned snapshot references */ + Snapshot *snapshots; /* dynamically allocated array */ + int maxsnapshots; /* currently allocated array size */ } ResourceOwnerData; *************** static void ResourceOwnerReleaseInternal *** 98,103 **** --- 104,110 ---- static void PrintRelCacheLeakWarning(Relation rel); static void PrintPlanCacheLeakWarning(CachedPlan *plan); static void PrintTupleDescLeakWarning(TupleDesc tupdesc); + static void PrintSnapshotLeakWarning(Snapshot snapshot); /***************************************************************************** *************** ResourceOwnerReleaseInternal(ResourceOwn *** 301,306 **** --- 308,320 ---- PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]); DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]); } + /* Ditto for snapshot references */ + while (owner->nsnapshots > 0) + { + if (isCommit) + PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots -1]); + UnregisterSnapshot(owner->snapshots[owner->nsnapshots -1]); + } /* Clean up index scans too */ ReleaseResources_hash(); *************** ResourceOwnerDelete(ResourceOwner owner) *** 332,337 **** --- 346,352 ---- Assert(owner->nrelrefs == 0); Assert(owner->nplanrefs == 0); Assert(owner->ntupdescs == 0); + Assert(owner->nsnapshots == 0); /* * Delete children. The recursive call will delink the child from me, so *************** ResourceOwnerDelete(ResourceOwner owner) *** 360,365 **** --- 375,382 ---- pfree(owner->planrefs); if (owner->tupdescs) pfree(owner->tupdescs); + if (owner->snapshots) + pfree(owner->snapshots); pfree(owner); } *************** PrintTupleDescLeakWarning(TupleDesc tupd *** 936,938 **** --- 953,1037 ---- "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced", tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod); } + + /* + * Make sure there is room for at least one more entry in a ResourceOwner's + * snapshot reference array. + * + * This is separate from actually inserting an entry because if we run out + * of memory, it's critical to do so *before* acquiring the resource. + */ + void + ResourceOwnerEnlargeSnapshots(ResourceOwner owner) + { + int newmax; + + if (owner->nsnapshots < owner->maxsnapshots) + return; /* nothing to do */ + + if (owner->snapshots == NULL) + { + newmax = 16; + owner->snapshots = (Snapshot *) + MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot)); + owner->maxsnapshots = newmax; + } + else + { + newmax = owner->maxsnapshots * 2; + owner->snapshots = (Snapshot *) + repalloc(owner->snapshots, newmax * sizeof(Snapshot)); + owner->maxsnapshots = newmax; + } + } + + /* + * Remember that a snapshot reference is owned by a ResourceOwner + * + * Caller must have previously done ResourceOwnerEnlargeSnapshots() + */ + void + ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot) + { + Assert(owner->nsnapshots < owner->maxsnapshots); + owner->snapshots[owner->nsnapshots] = snapshot; + owner->nsnapshots++; + } + + /* + * Forget that a snapshot reference is owned by a ResourceOwner + */ + void + ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot) + { + Snapshot *snapshots = owner->snapshots; + int ns1 = owner->nsnapshots -1; + int i; + + for (i = ns1; i >= 0; i--) + { + if (snapshots[i] == snapshot) + { + while (i < ns1) + { + snapshots[i] = snapshots[i + 1]; + i++; + } + owner->nsnapshots = ns1; + return; + } + } + elog(ERROR, "snapshot reference %p is not owned by resource owner %s", + snapshot, owner->name); + } + + /* + * Debugging subroutine + */ + static void + PrintSnapshotLeakWarning(Snapshot snapshot) + { + elog(WARNING, + "Snapshot reference leak: Snapshot %p still referenced", + snapshot); + } Index: src/backend/utils/time/snapmgr.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/utils/time/snapmgr.c,v retrieving revision 1.6 diff -c -p -r1.6 snapmgr.c *** src/backend/utils/time/snapmgr.c 27 Oct 2008 22:15:05 -0000 1.6 --- src/backend/utils/time/snapmgr.c 25 Nov 2008 19:26:47 -0000 *************** *** 2,8 **** * snapmgr.c * PostgreSQL snapshot manager * ! * We keep track of snapshots in two ways: the "registered snapshots" list, * and the "active snapshot" stack. All snapshots in either of them live in * persistent memory. When a snapshot is no longer in any of these lists * (tracked by separate refcounts on each snapshot), its memory can be freed. --- 2,8 ---- * snapmgr.c * PostgreSQL snapshot manager * ! * We keep track of snapshots in two ways: those "registered" by resowner.c, * and the "active snapshot" stack. All snapshots in either of them live in * persistent memory. When a snapshot is no longer in any of these lists * (tracked by separate refcounts on each snapshot), its memory can be freed. *************** *** 14,22 **** * anyway it should be rather uncommon to keep snapshots referenced for too * long.) * - * Note: parts of this code could probably be replaced by appropriate use - * of resowner.c. - * * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California --- 14,19 ---- *************** *** 34,39 **** --- 31,37 ---- #include "storage/procarray.h" #include "utils/memutils.h" #include "utils/memutils.h" + #include "utils/resowner.h" #include "utils/snapmgr.h" #include "utils/tqual.h" *************** TransactionId RecentXmin = FirstNormalTr *** 69,101 **** TransactionId RecentGlobalXmin = InvalidTransactionId; /* - * Elements of the list of registered snapshots. - * - * Note that we keep refcounts both here and in SnapshotData. This is because - * the same snapshot may be registered more than once in a subtransaction, and - * if a subxact aborts we want to be able to subtract the correct amount of - * counts from SnapshotData. (Another approach would be keeping one - * RegdSnapshotElt each time a snapshot is registered, but that seems - * unnecessary wastage.) - * - * NB: the code assumes that elements in this list are in non-increasing - * order of s_level; also, the list must be NULL-terminated. - */ - typedef struct RegdSnapshotElt - { - Snapshot s_snap; - uint32 s_count; - int s_level; - struct RegdSnapshotElt *s_next; - } RegdSnapshotElt; - - /* * Elements of the active snapshot stack. * ! * It's not necessary to keep a refcount like we do for the registered list; ! * each element here accounts for exactly one active_count on SnapshotData. ! * We cannot condense them like we do for RegdSnapshotElt because it would mess ! * up the order of entries in the stack. * * NB: the code assumes that elements in this list are in non-increasing * order of as_level; also, the list must be NULL-terminated. --- 67,75 ---- TransactionId RecentGlobalXmin = InvalidTransactionId; /* * Elements of the active snapshot stack. * ! * Each element here accounts for exactly one active_count on SnapshotData. * * NB: the code assumes that elements in this list are in non-increasing * order of as_level; also, the list must be NULL-terminated. *************** typedef struct ActiveSnapshotElt *** 107,118 **** struct ActiveSnapshotElt *as_next; } ActiveSnapshotElt; - /* Head of the list of registered snapshots */ - static RegdSnapshotElt *RegisteredSnapshotList = NULL; - /* Top of the stack of active snapshots */ static ActiveSnapshotElt *ActiveSnapshot = NULL; /* first GetTransactionSnapshot call in a transaction? */ bool FirstSnapshotSet = false; --- 81,92 ---- struct ActiveSnapshotElt *as_next; } ActiveSnapshotElt; /* Top of the stack of active snapshots */ static ActiveSnapshotElt *ActiveSnapshot = NULL; + /* How many snapshots is resowner.c tracking for us? */ + static int RegisteredSnapshots = 0; + /* first GetTransactionSnapshot call in a transaction? */ bool FirstSnapshotSet = false; *************** static void SnapshotResetXmin(void); *** 133,139 **** * GetTransactionSnapshot * Get the appropriate snapshot for a new query in a transaction. * - * * Note that the return value may point at static storage that will be modified * by future calls and by CommandCounterIncrement(). Callers should call * RegisterSnapshot or PushActiveSnapshot on the returned snap if it is to be --- 107,112 ---- *************** GetTransactionSnapshot(void) *** 145,150 **** --- 118,125 ---- /* First call in transaction? */ if (!FirstSnapshotSet) { + Assert(RegisteredSnapshots == 0); + CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData); FirstSnapshotSet = true; *************** ActiveSnapshotSet(void) *** 371,478 **** Snapshot RegisterSnapshot(Snapshot snapshot) { ! RegdSnapshotElt *elt; ! RegdSnapshotElt *newhead; ! int level; if (snapshot == InvalidSnapshot) return InvalidSnapshot; - level = GetCurrentTransactionNestLevel(); - - /* - * If there's already an item in the list for the same snapshot and the - * same subxact nest level, increment its refcounts. Otherwise create a - * new one. - */ - for (elt = RegisteredSnapshotList; elt != NULL; elt = elt->s_next) - { - if (elt->s_level < level) - break; - - if (elt->s_snap == snapshot && elt->s_level == level) - { - elt->s_snap->regd_count++; - elt->s_count++; - - return elt->s_snap; - } - } - - /* - * Create the new list element. If it's not been copied into persistent - * memory already, we must do so; otherwise we can just increment the - * reference count. - */ - newhead = MemoryContextAlloc(TopTransactionContext, sizeof(RegdSnapshotElt)); - newhead->s_next = RegisteredSnapshotList; /* Static snapshot? Create a persistent copy */ ! newhead->s_snap = snapshot->copied ? snapshot : CopySnapshot(snapshot); ! newhead->s_level = level; ! newhead->s_count = 1; ! newhead->s_snap->regd_count++; ! RegisteredSnapshotList = newhead; ! return RegisteredSnapshotList->s_snap; } /* * UnregisterSnapshot - * Signals that a snapshot is no longer necessary * ! * If both reference counts fall to zero, the snapshot memory is released. ! * If only the registered list refcount falls to zero, just the list element is ! * freed. */ void UnregisterSnapshot(Snapshot snapshot) { ! RegdSnapshotElt *prev = NULL; ! RegdSnapshotElt *elt; ! bool found = false; ! ! if (snapshot == InvalidSnapshot) return; ! for (elt = RegisteredSnapshotList; elt != NULL; elt = elt->s_next) ! { ! if (elt->s_snap == snapshot) ! { ! Assert(elt->s_snap->regd_count > 0); ! Assert(elt->s_count > 0); ! elt->s_snap->regd_count--; ! elt->s_count--; ! found = true; ! ! if (elt->s_count == 0) ! { ! /* delink it from the registered snapshot list */ ! if (prev) ! prev->s_next = elt->s_next; ! else ! RegisteredSnapshotList = elt->s_next; ! ! /* free the snapshot itself if it's no longer relevant */ ! if (elt->s_snap->regd_count == 0 && elt->s_snap->active_count == 0) ! FreeSnapshot(elt->s_snap); ! ! /* and free the list element */ ! pfree(elt); ! } ! ! break; ! } ! ! prev = elt; } - - if (!found) - elog(WARNING, "unregistering failed for snapshot %p", snapshot); - - SnapshotResetXmin(); } /* --- 346,392 ---- Snapshot RegisterSnapshot(Snapshot snapshot) { ! Snapshot snap; if (snapshot == InvalidSnapshot) return InvalidSnapshot; /* Static snapshot? Create a persistent copy */ ! snap = snapshot->copied ? snapshot : CopySnapshot(snapshot); ! /* and tell resowner.c about it */ ! ResourceOwnerEnlargeSnapshots(CurrentResourceOwner); ! snap->regd_count++; ! ResourceOwnerRememberSnapshot(CurrentResourceOwner, snap); ! RegisteredSnapshots++; ! return snap; } /* * UnregisterSnapshot * ! * Decrement the reference count of a snapshot, remove the corresponding ! * reference from CurrentResourceOwner, and free the snapshot if no more ! * references remain. */ void UnregisterSnapshot(Snapshot snapshot) { ! if (snapshot == NULL) return; ! Assert(snapshot->regd_count > 0); ! Assert(RegisteredSnapshots > 0); ! ResourceOwnerForgetSnapshot(CurrentResourceOwner, snapshot); ! RegisteredSnapshots--; ! if (--snapshot->regd_count == 0 && snapshot->active_count == 0) ! { ! FreeSnapshot(snapshot); ! SnapshotResetXmin(); } } /* *************** UnregisterSnapshot(Snapshot snapshot) *** 485,491 **** static void SnapshotResetXmin(void) { ! if (RegisteredSnapshotList == NULL && ActiveSnapshot == NULL) MyProc->xmin = InvalidTransactionId; } --- 399,405 ---- static void SnapshotResetXmin(void) { ! if (RegisteredSnapshots == 0 && ActiveSnapshot == NULL) MyProc->xmin = InvalidTransactionId; } *************** void *** 496,502 **** AtSubCommit_Snapshot(int level) { ActiveSnapshotElt *active; - RegdSnapshotElt *regd; /* * Relabel the active snapshots set in this subtransaction as though they --- 410,415 ---- *************** AtSubCommit_Snapshot(int level) *** 508,527 **** break; active->as_level = level - 1; } - - /* - * Reassign all registered snapshots to the parent subxact. - * - * Note: this code is somewhat bogus in that we could end up with multiple - * entries for the same snapshot and the same subxact level (my parent's - * level). Cleaning that up is more trouble than it's currently worth, - * however. - */ - for (regd = RegisteredSnapshotList; regd != NULL; regd = regd->s_next) - { - if (regd->s_level == level) - regd->s_level--; - } } /* --- 421,426 ---- *************** AtSubCommit_Snapshot(int level) *** 531,539 **** void AtSubAbort_Snapshot(int level) { - RegdSnapshotElt *prev; - RegdSnapshotElt *regd; - /* Forget the active snapshots set by this subtransaction */ while (ActiveSnapshot && ActiveSnapshot->as_level >= level) { --- 430,435 ---- *************** AtSubAbort_Snapshot(int level) *** 558,596 **** ActiveSnapshot = next; } - /* Unregister all snapshots registered during this subtransaction */ - prev = NULL; - for (regd = RegisteredSnapshotList; regd != NULL; ) - { - if (regd->s_level >= level) - { - RegdSnapshotElt *tofree; - - if (prev) - prev->s_next = regd->s_next; - else - RegisteredSnapshotList = regd->s_next; - - tofree = regd; - regd = regd->s_next; - - tofree->s_snap->regd_count -= tofree->s_count; - - /* free the snapshot if possible */ - if (tofree->s_snap->regd_count == 0 && - tofree->s_snap->active_count == 0) - FreeSnapshot(tofree->s_snap); - - /* and free the list element */ - pfree(tofree); - } - else - { - prev = regd; - regd = regd->s_next; - } - } - SnapshotResetXmin(); } --- 454,459 ---- *************** AtEOXact_Snapshot(bool isCommit) *** 605,611 **** if (isCommit) { ActiveSnapshotElt *active; - RegdSnapshotElt *regd; /* * On a serializable snapshot we must first unregister our private --- 468,473 ---- *************** AtEOXact_Snapshot(bool isCommit) *** 614,629 **** if (registered_serializable) UnregisterSnapshot(CurrentSnapshot); /* complain about unpopped active snapshots */ for (active = ActiveSnapshot; active != NULL; active = active->as_next) elog(WARNING, "snapshot %p still active", active); - - /* complain about any unregistered snapshot */ - for (regd = RegisteredSnapshotList; regd != NULL; regd = regd->s_next) - elog(WARNING, - "snapshot %p not destroyed at commit (%d regd refs, %d active refs)", - regd->s_snap, regd->s_snap->regd_count, - regd->s_snap->active_count); } /* --- 476,488 ---- if (registered_serializable) UnregisterSnapshot(CurrentSnapshot); + if (RegisteredSnapshots != 0) + elog(WARNING, "%d registered snapshots seem to remain after cleanup", + RegisteredSnapshots); + /* complain about unpopped active snapshots */ for (active = ActiveSnapshot; active != NULL; active = active->as_next) elog(WARNING, "snapshot %p still active", active); } /* *************** AtEOXact_Snapshot(bool isCommit) *** 631,637 **** * it'll go away with TopTransactionContext. */ ActiveSnapshot = NULL; ! RegisteredSnapshotList = NULL; CurrentSnapshot = NULL; SecondarySnapshot = NULL; --- 490,496 ---- * it'll go away with TopTransactionContext. */ ActiveSnapshot = NULL; ! RegisteredSnapshots = 0; CurrentSnapshot = NULL; SecondarySnapshot = NULL; Index: src/include/utils/resowner.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/utils/resowner.h,v retrieving revision 1.15 diff -c -p -r1.15 resowner.h *** src/include/utils/resowner.h 1 Jan 2008 19:45:59 -0000 1.15 --- src/include/utils/resowner.h 25 Nov 2008 16:30:16 -0000 *************** *** 22,27 **** --- 22,28 ---- #include "storage/buf.h" #include "utils/catcache.h" #include "utils/plancache.h" + #include "utils/snapshot.h" /* *************** extern void ResourceOwnerRememberTupleDe *** 121,124 **** --- 122,132 ---- extern void ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc); + /* support for snapshot refcount management */ + extern void ResourceOwnerEnlargeSnapshots(ResourceOwner owner); + extern void ResourceOwnerRememberSnapshot(ResourceOwner owner, + Snapshot snapshot); + extern void ResourceOwnerForgetSnapshot(ResourceOwner owner, + Snapshot snapshot); + #endif /* RESOWNER_H */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers