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

Reply via email to