[Python-checkins] python/dist/src/Modules gcmodule.c,2.62,2.63

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Sat, 05 Apr 2003 16:11:41 -0800


Update of /cvsroot/python/python/dist/src/Modules
In directory sc8-pr-cvs1:/tmp/cvs-serv11222/python/Modules

Modified Files:
	gcmodule.c 
Log Message:
Reworked move_finalizer_reachable() to create two distinct lists:
externally unreachable objects with finalizers, and externally unreachable
objects without finalizers reachable from such objects.  This allows us
to call has_finalizer() at most once per object, and so limit the pain of
nasty getattr hooks.  This fixes the failing "boom 2" example Jeremy
posted (a non-printing variant of which is now part of test_gc), via never
triggering the nasty part of its __getattr__ method.


Index: gcmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/gcmodule.c,v
retrieving revision 2.62
retrieving revision 2.63
diff -C2 -d -r2.62 -r2.63
*** gcmodule.c	5 Apr 2003 18:40:50 -0000	2.62
--- gcmodule.c	6 Apr 2003 00:11:39 -0000	2.63
***************
*** 52,62 ****
  
  /* true if we are currently running the collector */
! static int collecting;
  
  /* list of uncollectable objects */
! static PyObject *garbage;
  
  /* Python string to use if unhandled exception occurs */
! static PyObject *gc_str;
  
  /* Python string used to look for __del__ attribute. */
--- 52,62 ----
  
  /* true if we are currently running the collector */
! static int collecting = 0;
  
  /* list of uncollectable objects */
! static PyObject *garbage = NULL;
  
  /* Python string to use if unhandled exception occurs */
! static PyObject *gc_str = NULL;
  
  /* Python string used to look for __del__ attribute. */
***************
*** 413,429 ****
  
  /* Move objects that are reachable from finalizers, from the unreachable set
!  * into the finalizers set.
   */
  static void
! move_finalizer_reachable(PyGC_Head *finalizers)
  {
  	traverseproc traverse;
  	PyGC_Head *gc = finalizers->gc.gc_next;
! 	for (; gc != finalizers; gc=gc->gc.gc_next) {
! 		/* careful, finalizers list is growing here */
  		traverse = FROM_GC(gc)->ob_type->tp_traverse;
  		(void) traverse(FROM_GC(gc),
! 			       (visitproc)visit_move,
! 			       (void *)finalizers);
  	}
  }
--- 413,430 ----
  
  /* Move objects that are reachable from finalizers, from the unreachable set
!  * into the reachable_from_finalizers set.
   */
  static void
! move_finalizer_reachable(PyGC_Head *finalizers,
! 			 PyGC_Head *reachable_from_finalizers)
  {
  	traverseproc traverse;
  	PyGC_Head *gc = finalizers->gc.gc_next;
! 	for (; gc != finalizers; gc = gc->gc.gc_next) {
! 		/* Note that the finalizers list may grow during this. */
  		traverse = FROM_GC(gc)->ob_type->tp_traverse;
  		(void) traverse(FROM_GC(gc),
! 				(visitproc)visit_move,
! 				(void *)reachable_from_finalizers);
  	}
  }
***************
*** 455,471 ****
  }
  
! /* Handle uncollectable garbage (cycles with finalizers). */
  static void
! handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
  {
  	PyGC_Head *gc;
  	if (garbage == NULL) {
  		garbage = PyList_New(0);
  	}
! 	for (gc = finalizers->gc.gc_next; gc != finalizers;
! 			gc = finalizers->gc.gc_next) {
  		PyObject *op = FROM_GC(gc);
! 		/* XXX has_finalizer() is not safe here. */
! 		if ((debug & DEBUG_SAVEALL) || has_finalizer(op)) {
  			/* If SAVEALL is not set then just append objects with
  			 * finalizers to the list of garbage.  All objects in
--- 456,478 ----
  }
  
! /* Handle uncollectable garbage (cycles with finalizers, and stuff reachable
!  * only from such cycles).
!  */
  static void
! handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old, int hasfinalizer)
  {
  	PyGC_Head *gc;
  	if (garbage == NULL) {
  		garbage = PyList_New(0);
+ 		if (garbage == NULL)
+ 			Py_FatalError("gc couldn't create gc.garbage list");
  	}
! 	for (gc = finalizers->gc.gc_next;
! 	     gc != finalizers;
! 	     gc = finalizers->gc.gc_next) {
  		PyObject *op = FROM_GC(gc);
! 
! 		assert(IS_REACHABLE(op));
! 		if ((debug & DEBUG_SAVEALL) || hasfinalizer) {
  			/* If SAVEALL is not set then just append objects with
  			 * finalizers to the list of garbage.  All objects in
***************
*** 475,480 ****
  			PyList_Append(garbage, op);
  		}
- 		/* object is now reachable again */
- 		assert(IS_REACHABLE(op));
  		gc_list_remove(gc);
  		gc_list_append(gc, old);
--- 482,485 ----
***************
*** 528,531 ****
--- 533,537 ----
  	PyGC_Head collectable;
  	PyGC_Head finalizers;
+ 	PyGC_Head reachable_from_finalizers;
  	PyGC_Head *gc;
  
***************
*** 590,603 ****
  	 * instance objects with __del__ methods.
  	 *
! 	 * Move each object into the collectable set or the finalizers set.
! 	 * Because we need to check for __del__ methods on instances of
! 	 * classic classes, arbitrary Python code may get executed by
! 	 * getattr hooks:  that may resurrect or deallocate (via refcount
! 	 * falling to 0) unreachable objects, so this is very delicate.
  	 */
  	gc_list_init(&collectable);
  	gc_list_init(&finalizers);
  	move_finalizers(&unreachable, &collectable, &finalizers);
! 	move_finalizer_reachable(&finalizers);
  
  	/* Collect statistics on collectable objects found and print
--- 596,620 ----
  	 * instance objects with __del__ methods.
  	 *
! 	 * Move each unreachable object into the collectable set or the
! 	 * finalizers set.  Because we need to check for __del__ methods on
! 	 * instances of classic classes, arbitrary Python code may get
! 	 * executed by getattr hooks:  that may resurrect or deallocate (via
! 	 * refcount falling to 0) unreachable objects, so this is very
! 	 * delicate.
  	 */
  	gc_list_init(&collectable);
  	gc_list_init(&finalizers);
  	move_finalizers(&unreachable, &collectable, &finalizers);
! 	/* finalizers contains the unreachable objects with a finalizer;
! 	 * unreachable objects reachable only *from* those are also
! 	 * uncollectable; we move those into a separate list
! 	 * (reachable_from_finalizers) so we don't have to do the dangerous
! 	 * has_finalizer() test again later.
! 	 */
! 	gc_list_init(&reachable_from_finalizers);
! 	move_finalizer_reachable(&finalizers, &reachable_from_finalizers);
! 	/* And move everything only reachable from the reachable stuff. */
! 	move_finalizer_reachable(&reachable_from_finalizers,
! 				 &reachable_from_finalizers);
  
  	/* Collect statistics on collectable objects found and print
***************
*** 611,626 ****
  	}
  	/* Call tp_clear on objects in the collectable set.  This will cause
! 	 * the reference cycles to be broken. It may also cause some objects in
! 	 * finalizers to be freed */
  	delete_garbage(&collectable, old);
  
  	/* Collect statistics on uncollectable objects found and print
  	 * debugging information. */
! 	for (gc = finalizers.gc.gc_next; gc != &finalizers;
! 			gc = gc->gc.gc_next) {
  		n++;
! 		if (debug & DEBUG_UNCOLLECTABLE) {
  			debug_cycle("uncollectable", FROM_GC(gc));
- 		}
  	}
  	if (debug & DEBUG_STATS) {
--- 628,650 ----
  	}
  	/* Call tp_clear on objects in the collectable set.  This will cause
! 	 * the reference cycles to be broken. It may also cause some objects
! 	 * in finalizers and/or reachable_from_finalizers to be freed */
  	delete_garbage(&collectable, old);
  
  	/* Collect statistics on uncollectable objects found and print
  	 * debugging information. */
! 	for (gc = finalizers.gc.gc_next; 
! 	     gc != &finalizers;
! 	     gc = gc->gc.gc_next) {
  		n++;
! 		if (debug & DEBUG_UNCOLLECTABLE)
! 			debug_cycle("uncollectable", FROM_GC(gc));
! 	}
! 	for (gc = reachable_from_finalizers.gc.gc_next;
! 	     gc != &reachable_from_finalizers;
! 	     gc = gc->gc.gc_next) {
! 		n++;
! 		if (debug & DEBUG_UNCOLLECTABLE)
  			debug_cycle("uncollectable", FROM_GC(gc));
  	}
  	if (debug & DEBUG_STATS) {
***************
*** 637,642 ****
  	/* Append instances in the uncollectable set to a Python
  	 * reachable list of garbage.  The programmer has to deal with
! 	 * this if they insist on creating this type of structure. */
! 	handle_finalizers(&finalizers, old);
  
  	if (PyErr_Occurred()) {
--- 661,668 ----
  	/* Append instances in the uncollectable set to a Python
  	 * reachable list of garbage.  The programmer has to deal with
! 	 * this if they insist on creating this type of structure.
! 	 */
! 	handle_finalizers(&finalizers, old, 1);
! 	handle_finalizers(&reachable_from_finalizers, old, 0);
  
  	if (PyErr_Occurred()) {