[Python-checkins] python/dist/src/Objects typeobject.c,2.149,2.150

gvanrossum@users.sourceforge.net gvanrossum@users.sourceforge.net
Mon, 10 Jun 2002 08:24:46 -0700


Update of /cvsroot/python/python/dist/src/Objects
In directory usw-pr-cvs1:/tmp/cvs-serv26343

Modified Files:
	typeobject.c 
Log Message:
In the recent python-dev thread "Bizarre new test failure", we
discovered that subtype_traverse must traverse the type if it is a
heap type, because otherwise some cycles involving a type and its
instance would not be collected.  Simplest example:
    while 1:
        class C(object): pass
        C.ref = C()
This program grows without bounds before this fix.  (It grows ever
slower since it spends ever more time in the collector.)

Simply adding the right visit() call to subtype_traverse() revealed
other problems.  With MvL's help we re-learned that type_clear()
doesn't have to clear *all* references, only the ones that may not be
cleared by other means.  Careful analysis (see comments in the code)
revealed that only tp_mro needs to be cleared.  (The previous checkin
to this file adds a test for tp_mro==NULL to _PyType_Lookup() that's
essential to prevent crashes due to tp_mro being NULL when
subtype_dealloc() tries to look for a __del__ method.)  The same kind
of analysis also revealed that subtype_clear() doesn't need to clear
the instance dict.

With this fix, a useful property of the collector is once again
guaranteed: a single gc.collect() call will clear out all garbage.
(It didn't always before, which put us on the track of this bug.)

Will backport to 2.2.


Index: typeobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/typeobject.c,v
retrieving revision 2.149
retrieving revision 2.150
diff -C2 -d -r2.149 -r2.150
*** typeobject.c	10 Jun 2002 14:30:43 -0000	2.149
--- typeobject.c	10 Jun 2002 15:24:42 -0000	2.150
***************
*** 291,294 ****
--- 291,303 ----
  	}
  
+ 	if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
+ 		/* For a heaptype, the instances count as references
+ 		   to the type.  Traverse the type so the collector
+ 		   can find cycles involving this link. */
+ 		int err = visit((PyObject *)type, arg);
+ 		if (err)
+ 			return err;
+ 	}
+ 
  	if (basetraverse)
  		return basetraverse(self, visit, arg);
***************
*** 333,342 ****
  	}
  
! 	if (type->tp_dictoffset != base->tp_dictoffset) {
! 		PyObject **dictptr = _PyObject_GetDictPtr(self);
! 		if (dictptr && *dictptr) {
! 			PyDict_Clear(*dictptr);
! 		}
! 	}
  
  	if (baseclear)
--- 342,347 ----
  	}
  
! 	/* There's no need to clear the instance dict (if any);
! 	   the collector will call its tp_clear handler. */
  
  	if (baseclear)
***************
*** 1484,1494 ****
  type_traverse(PyTypeObject *type, visitproc visit, void *arg)
  {
- 	etype *et;
  	int err;
  
! 	if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
! 		return 0;
! 
! 	et = (etype *)type;
  
  #define VISIT(SLOT) \
--- 1489,1497 ----
  type_traverse(PyTypeObject *type, visitproc visit, void *arg)
  {
  	int err;
  
! 	/* Because of type_is_gc(), the collector only calls this
! 	   for heaptypes. */
! 	assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
  
  #define VISIT(SLOT) \
***************
*** 1504,1509 ****
  	VISIT(type->tp_bases);
  	VISIT(type->tp_base);
! 	VISIT(type->tp_subclasses);
! 	VISIT(et->slots);
  
  #undef VISIT
--- 1507,1515 ----
  	VISIT(type->tp_bases);
  	VISIT(type->tp_base);
! 
! 	/* There's no need to visit type->tp_subclasses or
! 	   ((etype *)type)->slots, because they can't be involved
! 	   in cycles; tp_subclasses is a list of weak references,
! 	   and slots is a tuple of strings. */
  
  #undef VISIT
***************
*** 1515,1525 ****
  type_clear(PyTypeObject *type)
  {
- 	etype *et;
  	PyObject *tmp;
  
! 	if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
! 		return 0;
! 
! 	et = (etype *)type;
  
  #define CLEAR(SLOT) \
--- 1521,1529 ----
  type_clear(PyTypeObject *type)
  {
  	PyObject *tmp;
  
! 	/* Because of type_is_gc(), the collector only calls this
! 	   for heaptypes. */
! 	assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
  
  #define CLEAR(SLOT) \
***************
*** 1530,1545 ****
  	}
  
! 	CLEAR(type->tp_dict);
! 	CLEAR(type->tp_cache);
! 	CLEAR(type->tp_mro);
! 	CLEAR(type->tp_bases);
! 	CLEAR(type->tp_base);
! 	CLEAR(type->tp_subclasses);
! 	CLEAR(et->slots);
  
! 	if (type->tp_doc != NULL) {
! 		PyObject_FREE(type->tp_doc);
! 		type->tp_doc = NULL;
! 	}
  
  #undef CLEAR
--- 1534,1563 ----
  	}
  
! 	/* The only field we need to clear is tp_mro, which is part of a
! 	   hard cycle (its first element is the class itself) that won't
! 	   be broken otherwise (it's a tuple and tuples don't have a
! 	   tp_clear handler).  None of the other fields need to be
! 	   cleared, and here's why:
  
! 	   tp_dict:
! 	       It is a dict, so the collector will call its tp_clear.
! 
! 	   tp_cache:
! 	       Not used; if it were, it would be a dict.
! 
! 	   tp_bases, tp_base:
! 	       If these are involved in a cycle, there must be at least
! 	       one other, mutable object in the cycle, e.g. a base
! 	       class's dict; the cycle will be broken that way.
! 
! 	   tp_subclasses:
! 	       A list of weak references can't be part of a cycle; and
! 	       lists have their own tp_clear.
! 
! 	   slots (in etype):
! 	       A tuple of strings can't be part of a cycle.
! 	*/
! 
! 	CLEAR(type->tp_mro);
  
  #undef CLEAR
***************
*** 2158,2165 ****
  	int i, n;
  
! 	if (type->tp_flags & Py_TPFLAGS_READY) {
! 		assert(type->tp_dict != NULL);
  		return 0;
- 	}
  	assert((type->tp_flags & Py_TPFLAGS_READYING) == 0);
  
--- 2176,2181 ----
  	int i, n;
  
! 	if (type->tp_flags & Py_TPFLAGS_READY)
  		return 0;
  	assert((type->tp_flags & Py_TPFLAGS_READYING) == 0);