[Patches] Sightly clean-up cycle-gc patch

Neil Schemenauer nascheme@enme.ucalgary.ca
Wed, 26 Apr 2000 09:51:37 -0600


Thanks for the comments Greg.  I had already made most of the
changes you suggested.  As per your suggestions, I have renamed
the macros from PY_* to Py_* and lowercased the list function
names.  The latest version is attached.


    Neil


Index: 0.3/Makefile.in
*** 0.3/Makefile.in Tue, 25 Apr 2000 17:33:19 -0600 nas (python/0_Makefile.i 1.1 644)
--- gc.6(w)/Makefile.in Tue, 25 Apr 2000 17:59:00 -0600 nas (python/0_Makefile.i 1.1.1.1 644)
***************
*** 401,407 ****
  		$(INSTALL_DATA) Modules/Makefile $(LIBPL)/Makefile
  		$(INSTALL_DATA) Modules/Setup $(LIBPL)/Setup
  		$(INSTALL_DATA) Modules/Setup.local $(LIBPL)/Setup.local
! 		$(INSTALL_DATA) Modules/Setup.thread $(LIBPL)/Setup.thread
  		$(INSTALL_PROGRAM) $(srcdir)/Modules/makesetup $(LIBPL)/makesetup
  		$(INSTALL_PROGRAM) $(srcdir)/install-sh $(LIBPL)/install-sh
  		$(INSTALL_DATA) $(srcdir)/Misc/Makefile.pre.in $(LIBPL)/Makefile.pre.in
--- 401,407 ----
  		$(INSTALL_DATA) Modules/Makefile $(LIBPL)/Makefile
  		$(INSTALL_DATA) Modules/Setup $(LIBPL)/Setup
  		$(INSTALL_DATA) Modules/Setup.local $(LIBPL)/Setup.local
! 		$(INSTALL_DATA) Modules/Setup.auto $(LIBPL)/Setup.auto
  		$(INSTALL_PROGRAM) $(srcdir)/Modules/makesetup $(LIBPL)/makesetup
  		$(INSTALL_PROGRAM) $(srcdir)/install-sh $(LIBPL)/install-sh
  		$(INSTALL_DATA) $(srcdir)/Misc/Makefile.pre.in $(LIBPL)/Makefile.pre.in
Index: 0.3/config.h.in
*** 0.3/config.h.in Tue, 25 Apr 2000 17:33:19 -0600 nas (python/3_config.h.i 1.1 644)
--- gc.6(w)/config.h.in Tue, 25 Apr 2000 17:59:00 -0600 nas (python/3_config.h.i 1.1.1.1 644)
***************
*** 207,212 ****
--- 207,215 ----
     (shared library plus accessory files). */
  #undef WITH_NEXT_FRAMEWORK
  
+ /* Define if you want cycle garbage collection */
+ #undef WITH_CYCLE_GC
+ 
  /* The number of bytes in an off_t. */
  #undef SIZEOF_OFF_T
  
Index: 0.3/configure.in
*** 0.3/configure.in Tue, 25 Apr 2000 17:33:19 -0600 nas (python/5_configure. 1.1 644)
--- gc.6(w)/configure.in Tue, 25 Apr 2000 17:59:00 -0600 nas (python/5_configure. 1.1.1.1 644)
***************
*** 1070,1079 ****
  fi],
  [AC_MSG_RESULT(no)])
  
  # generate output files
  AC_OUTPUT(Makefile \
   Objects/Makefile \
   Parser/Makefile \
   Python/Makefile \
   Modules/Makefile.pre \
!  Modules/Setup.thread)
--- 1070,1090 ----
  fi],
  [AC_MSG_RESULT(no)])
  
+ AC_SUBST(USE_GC_MODULE)
+ USE_GC_MODULE="#"
+ 
+ AC_MSG_CHECKING(for --with-cycle-gc)
+ AC_ARG_WITH(cycle-gc, [--with-cycle-gc           enable garbage collection], [
+ AC_MSG_RESULT($withval)
+ AC_DEFINE(WITH_CYCLE_GC)
+ USE_GC_MODULE=
+ ],
+ AC_MSG_RESULT(no))
+ 
  # generate output files
  AC_OUTPUT(Makefile \
   Objects/Makefile \
   Parser/Makefile \
   Python/Makefile \
   Modules/Makefile.pre \
!  Modules/Setup.auto)
Index: 0.3/Include/object.h
*** 0.3/Include/object.h Tue, 25 Apr 2000 17:33:19 -0600 nas (python/o/18_object.h 1.1 644)
--- gc.6(w)/Include/object.h Tue, 25 Apr 2000 17:59:00 -0600 nas (python/o/18_object.h 1.1.1.1 644)
***************
*** 145,150 ****
--- 145,152 ----
  typedef int (*getsegcountproc) Py_PROTO((PyObject *, int *));
  typedef int (*getcharbufferproc) Py_PROTO((PyObject *, int, const char **));
  typedef int (*objobjproc) Py_PROTO((PyObject *, PyObject *));
+ typedef int (*visitproc) Py_PROTO((PyObject *, void *));
+ typedef int (*recurseproc) Py_PROTO((PyObject *, visitproc, void *));
  
  typedef struct {
  	binaryfunc nb_add;
***************
*** 243,251 ****
  
  	char *tp_doc; /* Documentation string */
  
  	/* More spares */
- 	long tp_xxx5;
- 	long tp_xxx6;
  	long tp_xxx7;
  	long tp_xxx8;
  
--- 245,257 ----
  
  	char *tp_doc; /* Documentation string */
  
+ 	/* call function for all accessible objects */
+ 	recurseproc tp_recurse;
+ 	
+ 	/* delete references to contained objects */
+ 	inquiry tp_clear;
+ 
  	/* More spares */
  	long tp_xxx7;
  	long tp_xxx8;
  
***************
*** 318,323 ****
--- 324,332 ----
  
  /* PySequenceMethods contains sq_contains */
  #define Py_TPFLAGS_HAVE_SEQUENCE_IN (1L<<1)
+ 
+ /* Objects with a GC info prefix (allocated *before* the object itself!) */
+ #define Py_TPFLAGS_HAVE_GCINFO (1L<<2)
  
  #define Py_TPFLAGS_DEFAULT  (Py_TPFLAGS_HAVE_GETCHARBUFFER | \
                               Py_TPFLAGS_HAVE_SEQUENCE_IN)
Index: 0.3/Include/objimpl.h
*** 0.3/Include/objimpl.h Tue, 25 Apr 2000 17:33:19 -0600 nas (python/o/19_objimpl.h 1.1 644)
--- gc.6(w)/Include/objimpl.h Tue, 25 Apr 2000 20:38:07 -0600 nas (python/o/19_objimpl.h 1.1.1.2 644)
***************
*** 72,77 ****
--- 72,94 ----
  
  #endif /* MS_COREDLL */
  
+ /*** Garbage Collection Support ***/
+ 
+ /* Add the object into the container set */
+ extern DL_IMPORT(void) PyGC_Insert Py_PROTO((PyObject *));
+ 
+ /* Remove the object from the container set */
+ extern DL_IMPORT(void) PyGC_Remove Py_PROTO((PyObject *));
+ 
+ /* GC objects are prefixed with PyGCInfo */
+ extern DL_IMPORT(PyObject *) _PyGC_New Py_PROTO((PyTypeObject *));
+ extern DL_IMPORT(PyVarObject *) _PyGC_NewVar Py_PROTO((PyTypeObject *, int));
+ extern DL_IMPORT(PyObject *) PyGC_Realloc Py_PROTO((PyObject *, int));
+ extern DL_IMPORT(void) PyGC_Del Py_PROTO((PyObject *));
+ 
+ #define PyGC_NEW(type, typeobj) ((type *) _PyGC_New(typeobj))
+ #define PyGC_NEW_VAR(type, typeobj, n) ((type *) _PyGC_NewVar(typeobj, n))
+ 
  #ifdef __cplusplus
  }
  #endif
Index: 0.3/Misc/Makefile.pre.in
*** 0.3/Misc/Makefile.pre.in Tue, 25 Apr 2000 17:33:19 -0600 nas (python/B/45_Makefile.p 1.1 644)
--- gc.6(w)/Misc/Makefile.pre.in Tue, 25 Apr 2000 17:59:00 -0600 nas (python/B/45_Makefile.p 1.2 644)
***************
*** 168,174 ****
  MAKEFILE=	$(LIBPL)/Makefile
  CONFIGC=	$(LIBPL)/config.c
  CONFIGCIN=	$(LIBPL)/config.c.in
! SETUP=		$(LIBPL)/Setup.thread $(LIBPL)/Setup.local $(LIBPL)/Setup
  
  SYSLIBS=	$(LIBM) $(LIBC)
  
--- 168,174 ----
  MAKEFILE=	$(LIBPL)/Makefile
  CONFIGC=	$(LIBPL)/config.c
  CONFIGCIN=	$(LIBPL)/config.c.in
! SETUP=		$(LIBPL)/Setup.auto $(LIBPL)/Setup.local $(LIBPL)/Setup
  
  SYSLIBS=	$(LIBM) $(LIBC)
  
Index: 0.3/Modules/Makefile.pre.in
*** 0.3/Modules/Makefile.pre.in Tue, 25 Apr 2000 17:33:19 -0600 nas (python/C/17_Makefile.p 1.1 644)
--- gc.6(w)/Modules/Makefile.pre.in Tue, 25 Apr 2000 17:59:00 -0600 nas (python/C/17_Makefile.p 1.1.1.1 644)
***************
*** 147,156 ****
  # gets remade from scratch; this ensures to remove modules that are no
  # longer pertinent (but that were in a previous configuration).
  config.c Makefile: Makefile.pre config.c.in $(MAKESETUP)
! config.c Makefile: Setup.thread Setup Setup.local
  config.c Makefile:
  		-rm -f $(LIBRARY)
! 		$(SHELL) $(MAKESETUP) Setup.thread Setup.local Setup
  
  hassignal:
  		-rm -f hassignal
--- 147,156 ----
  # gets remade from scratch; this ensures to remove modules that are no
  # longer pertinent (but that were in a previous configuration).
  config.c Makefile: Makefile.pre config.c.in $(MAKESETUP)
! config.c Makefile: Setup.auto Setup Setup.local
  config.c Makefile:
  		-rm -f $(LIBRARY)
! 		$(SHELL) $(MAKESETUP) Setup.auto Setup.local Setup
  
  hassignal:
  		-rm -f hassignal
Index: 0.3/Modules/Setup.in
*** 0.3/Modules/Setup.in Tue, 25 Apr 2000 17:33:19 -0600 nas (python/C/18_Setup.in 1.1 644)
--- gc.6(w)/Modules/Setup.in Tue, 25 Apr 2000 17:59:00 -0600 nas (python/C/18_Setup.in 1.1.1.1 644)
***************
*** 100,106 ****
  GLHACK=-Dclear=__GLclear
  #gl glmodule.c cgensupport.c -I$(srcdir) $(GLHACK) -lgl -lX11
  
! # The thread module is now automatically enabled, see Setup.thread.
  
  # Pure module.  Cannot be linked dynamically.
  # -DWITH_QUANTIFY, -DWITH_PURIFY, or -DWITH_ALL_PURE
--- 100,106 ----
  GLHACK=-Dclear=__GLclear
  #gl glmodule.c cgensupport.c -I$(srcdir) $(GLHACK) -lgl -lX11
  
! # The thread module is now automatically enabled, see Setup.auto.
  
  # Pure module.  Cannot be linked dynamically.
  # -DWITH_QUANTIFY, -DWITH_PURIFY, or -DWITH_ALL_PURE
Index: 0.3/Modules/cPickle.c
*** 0.3/Modules/cPickle.c Tue, 25 Apr 2000 17:33:19 -0600 nas (python/C/27_cPickle.c 1.1 644)
--- gc.6(w)/Modules/cPickle.c Tue, 25 Apr 2000 17:59:00 -0600 nas (python/C/27_cPickle.c 1.1.1.1 644)
***************
*** 2885,2891 ****
                PyInstanceObject *inst;
  
                PyErr_Clear();
!               UNLESS (inst=PyObject_NEW(PyInstanceObject, &PyInstance_Type))
                  goto err;
                inst->in_class=(PyClassObject*)cls;
                Py_INCREF(cls);
--- 2885,2891 ----
                PyInstanceObject *inst;
  
                PyErr_Clear();
!               UNLESS (inst=PyGC_NEW(PyInstanceObject, &PyInstance_Type))
                  goto err;
                inst->in_class=(PyClassObject*)cls;
                Py_INCREF(cls);
***************
*** 2893,2899 ****
                  Py_DECREF(inst);
                  goto err;
                }
! 
                return (PyObject *)inst;
              }
            Py_DECREF(__getinitargs__);
--- 2893,2899 ----
                  Py_DECREF(inst);
                  goto err;
                }
!               PyGC_Insert((PyObject *)inst);
                return (PyObject *)inst;
              }
            Py_DECREF(__getinitargs__);
Index: 0.3/Modules/newmodule.c
*** 0.3/Modules/newmodule.c Tue, 25 Apr 2000 17:33:19 -0600 nas (python/D/13_newmodule. 1.1 644)
--- gc.6(w)/Modules/newmodule.c Tue, 25 Apr 2000 17:59:00 -0600 nas (python/D/13_newmodule. 1.1.1.1 644)
***************
*** 49,61 ****
  			      &PyClass_Type, &klass,
  			      &PyDict_Type, &dict))
  		return NULL;
! 	inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type);
  	if (inst == NULL)
  		return NULL;
  	Py_INCREF(klass);
  	Py_INCREF(dict);
  	inst->in_class = (PyClassObject *)klass;
  	inst->in_dict = dict;
  	return (PyObject *)inst;
  }
  
--- 49,62 ----
  			      &PyClass_Type, &klass,
  			      &PyDict_Type, &dict))
  		return NULL;
! 	inst = PyGC_NEW(PyInstanceObject, &PyInstance_Type);
  	if (inst == NULL)
  		return NULL;
  	Py_INCREF(klass);
  	Py_INCREF(dict);
  	inst->in_class = (PyClassObject *)klass;
  	inst->in_dict = dict;
+ 	PyGC_Insert((PyObject *)inst);
  	return (PyObject *)inst;
  }
  
Index: 0.3/Objects/classobject.c
*** 0.3/Objects/classobject.c Tue, 25 Apr 2000 17:33:19 -0600 nas (python/E/16_classobjec 1.1 644)
--- gc.6(w)/Objects/classobject.c Tue, 25 Apr 2000 20:38:07 -0600 nas (python/E/16_classobjec 1.1.1.2 644)
***************
*** 110,116 ****
  		}
  		Py_INCREF(bases);
  	}
! 	op = PyObject_NEW(PyClassObject, &PyClass_Type);
  	if (op == NULL) {
  		Py_DECREF(bases);
  		return NULL;
--- 110,116 ----
  		}
  		Py_INCREF(bases);
  	}
! 	op = PyGC_NEW(PyClassObject, &PyClass_Type);
  	if (op == NULL) {
  		Py_DECREF(bases);
  		return NULL;
***************
*** 131,136 ****
--- 131,137 ----
  	Py_XINCREF(op->cl_getattr);
  	Py_XINCREF(op->cl_setattr);
  	Py_XINCREF(op->cl_delattr);
+ 	PyGC_Insert((PyObject *)op);
  	return (PyObject *) op;
  }
  
***************
*** 140,152 ****
  class_dealloc(op)
  	PyClassObject *op;
  {
  	Py_DECREF(op->cl_bases);
  	Py_DECREF(op->cl_dict);
  	Py_XDECREF(op->cl_name);
  	Py_XDECREF(op->cl_getattr);
  	Py_XDECREF(op->cl_setattr);
  	Py_XDECREF(op->cl_delattr);
! 	free((ANY *)op);
  }
  
  static PyObject *
--- 141,154 ----
  class_dealloc(op)
  	PyClassObject *op;
  {
+ 	PyGC_Remove((PyObject *)op);
  	Py_DECREF(op->cl_bases);
  	Py_DECREF(op->cl_dict);
  	Py_XDECREF(op->cl_name);
  	Py_XDECREF(op->cl_getattr);
  	Py_XDECREF(op->cl_setattr);
  	Py_XDECREF(op->cl_delattr);
! 	PyGC_Del((PyObject *)op);
  }
  
  static PyObject *
***************
*** 386,391 ****
--- 388,408 ----
  	return res;
  }
  
+ #ifdef WITH_CYCLE_GC
+ static int
+ class_recurse(PyClassObject *o, visitproc visit, void *closure)
+ {
+ 	if (o->cl_bases) visit(o->cl_bases, closure);
+ 	if (o->cl_dict) visit(o->cl_dict, closure);
+ 	if (o->cl_name) visit(o->cl_name, closure);
+ 	if (o->cl_getattr) visit(o->cl_getattr, closure);
+ 	if (o->cl_setattr) visit(o->cl_setattr, closure);
+ 	if (o->cl_delattr) visit(o->cl_delattr, closure);
+ 	return 1;
+ }
+ #endif /* WITH_CYCLE_GC */
+ 
+ 
  PyTypeObject PyClass_Type = {
  	PyObject_HEAD_INIT(&PyType_Type)
  	0,
***************
*** 406,411 ****
--- 423,434 ----
  	(reprfunc)class_str, /*tp_str*/
  	(getattrofunc)class_getattr, /*tp_getattro*/
  	(setattrofunc)class_setattr, /*tp_setattro*/
+ #ifdef WITH_CYCLE_GC
+ 	0,		/* tp_as_buffer */
+ 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GCINFO, /*tp_flags*/
+ 	0,		/* tp_doc */
+ 	(recurseproc)class_recurse,	/* tp_recurse */
+ #endif
  };
  
  int
***************
*** 444,455 ****
  		PyErr_BadInternalCall();
  		return NULL;
  	}
! 	inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type);
  	if (inst == NULL)
  		return NULL;
  	Py_INCREF(class);
  	inst->in_class = (PyClassObject *)class;
  	inst->in_dict = PyDict_New();
  	if (inst->in_dict == NULL) {
  		Py_DECREF(inst);
  		return NULL;
--- 467,479 ----
  		PyErr_BadInternalCall();
  		return NULL;
  	}
! 	inst = PyGC_NEW(PyInstanceObject, &PyInstance_Type);
  	if (inst == NULL)
  		return NULL;
  	Py_INCREF(class);
  	inst->in_class = (PyClassObject *)class;
  	inst->in_dict = PyDict_New();
+ 	PyGC_Insert((PyObject *)inst);
  	if (inst->in_dict == NULL) {
  		Py_DECREF(inst);
  		return NULL;
***************
*** 498,508 ****
  	PyObject *error_type, *error_value, *error_traceback;
  	PyObject *del;
  	static PyObject *delstr;
  	/* Call the __del__ method if it exists.  First temporarily
  	   revive the object and save the current exception, if any. */
  #ifdef Py_TRACE_REFS
  	/* much too complicated if Py_TRACE_REFS defined */
- 	extern long _Py_RefTotal;
  	inst->ob_type = &PyInstance_Type;
  	_Py_NewReference((PyObject *)inst);
  	_Py_RefTotal--;		/* compensate for increment in NEWREF */
--- 522,533 ----
  	PyObject *error_type, *error_value, *error_traceback;
  	PyObject *del;
  	static PyObject *delstr;
+ 	extern long _Py_RefTotal;
+ 	PyGC_Remove((PyObject *)inst);
  	/* Call the __del__ method if it exists.  First temporarily
  	   revive the object and save the current exception, if any. */
  #ifdef Py_TRACE_REFS
  	/* much too complicated if Py_TRACE_REFS defined */
  	inst->ob_type = &PyInstance_Type;
  	_Py_NewReference((PyObject *)inst);
  	_Py_RefTotal--;		/* compensate for increment in NEWREF */
***************
*** 550,555 ****
--- 575,581 ----
  #ifdef COUNT_ALLOCS
  		inst->ob_type->tp_free--;
  #endif
+ 		PyGC_Insert((PyObject *)inst);
  		return; /* __del__ added a reference; don't delete now */
  	}
  #ifdef Py_TRACE_REFS
***************
*** 561,567 ****
  #endif /* Py_TRACE_REFS */
  	Py_DECREF(inst->in_class);
  	Py_XDECREF(inst->in_dict);
! 	free((ANY *)inst);
  }
  
  static PyObject *
--- 587,593 ----
  #endif /* Py_TRACE_REFS */
  	Py_DECREF(inst->in_class);
  	Py_XDECREF(inst->in_dict);
! 	PyGC_Del((PyObject *)inst);
  }
  
  static PyObject *
***************
*** 840,845 ****
--- 866,881 ----
  	return outcome;
  }
  
+ #ifdef WITH_CYCLE_GC
+ static int
+ instance_recurse(PyInstanceObject *o, visitproc visit, void *closure)
+ {
+ 	if (o->in_class) visit((PyObject *)(o->in_class), closure);
+ 	if (o->in_dict) visit(o->in_dict, closure);
+ 	return 1;
+ }
+ #endif /* WITH_CYCLE_GC */
+ 
  static PyObject *getitemstr, *setitemstr, *delitemstr, *lenstr;
  
  static int
***************
*** 1463,1469 ****
  	(getattrofunc)instance_getattr, /*tp_getattro*/
  	(setattrofunc)instance_setattr, /*tp_setattro*/
          0, /* tp_as_buffer */
! 	Py_TPFLAGS_DEFAULT, /*tp_flags */
  };
  
  
--- 1499,1511 ----
  	(getattrofunc)instance_getattr, /*tp_getattro*/
  	(setattrofunc)instance_setattr, /*tp_setattro*/
          0, /* tp_as_buffer */
! #ifndef WITH_CYCLE_GC
! 	Py_TPFLAGS_DEFAULT, /*tp_flags*/
! #else
! 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GCINFO, /*tp_flags*/
! 	0,		/* tp_doc */
! 	(recurseproc)instance_recurse,	/* tp_recurse */
! #endif
  };
  
  
Index: 0.3/Objects/dictobject.c
*** 0.3/Objects/dictobject.c Tue, 25 Apr 2000 17:33:19 -0600 nas (python/E/19_dictobject 1.1 644)
--- gc.6(w)/Objects/dictobject.c Tue, 25 Apr 2000 17:59:00 -0600 nas (python/E/19_dictobject 1.1.1.1 644)
***************
*** 121,127 ****
  		if (dummy == NULL)
  			return NULL;
  	}
! 	mp = PyObject_NEW(dictobject, &PyDict_Type);
  	if (mp == NULL)
  		return NULL;
  	mp->ma_size = 0;
--- 121,127 ----
  		if (dummy == NULL)
  			return NULL;
  	}
! 	mp = PyGC_NEW(dictobject, &PyDict_Type);
  	if (mp == NULL)
  		return NULL;
  	mp->ma_size = 0;
***************
*** 129,134 ****
--- 129,135 ----
  	mp->ma_table = NULL;
  	mp->ma_fill = 0;
  	mp->ma_used = 0;
+ 	PyGC_Insert((PyObject *)mp);
  	return (PyObject *)mp;
  }
  
***************
*** 479,484 ****
--- 480,486 ----
  {
  	register int i;
  	register dictentry *ep;
+ 	PyGC_Remove((PyObject *)mp);
  	Py_TRASHCAN_SAFE_BEGIN(mp)
  	for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
  		if (ep->me_key != NULL) {
***************
*** 489,495 ****
  		}
  	}
  	PyMem_XDEL(mp->ma_table);
! 	PyMem_DEL(mp);
  	Py_TRASHCAN_SAFE_END(mp)
  }
  
--- 491,497 ----
  		}
  	}
  	PyMem_XDEL(mp->ma_table);
! 	PyGC_Del((PyObject *)mp);
  	Py_TRASHCAN_SAFE_END(mp)
  }
  
***************
*** 1036,1041 ****
--- 1038,1070 ----
  	return Py_None;
  }
  
+ #ifdef WITH_CYCLE_GC
+ static int
+ dict_recurse(PyObject *op, visitproc visit, void *closure)
+ {
+ 	int i = 0;
+ 	PyObject *pk;
+ 	PyObject *pv;
+ 
+ 	while (PyDict_Next(op, &i, &pk, &pv)) {
+ 		if (!visit(pk, closure)) {
+ 			return 0;
+ 		}
+ 		if (!visit(pv, closure)) {
+ 			return 0;
+ 		}
+ 	}
+ 	return 1;
+ }
+ 
+ static int
+ dict_gc_clear(PyObject *op)
+ {
+ 	PyDict_Clear(op);
+ 	return 0;
+ }
+ #endif /* WITH_CYCLE_GC */
+ 
  static PyMethodDef mapp_methods[] = {
  	{"has_key",	(PyCFunction)dict_has_key,      METH_VARARGS},
  	{"keys",	(PyCFunction)dict_keys},
***************
*** 1071,1076 ****
--- 1100,1117 ----
  	0,			/*tp_as_number*/
  	0,			/*tp_as_sequence*/
  	&dict_as_mapping,	/*tp_as_mapping*/
+ #ifdef WITH_CYCLE_GC
+ 	0,		/* tp_hash */
+ 	0,		/* tp_call */
+ 	0,		/* tp_str */
+ 	0,		/* tp_getattro */
+ 	0,		/* tp_setattro */
+ 	0,		/* tp_as_buffer */
+ 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GCINFO, /*tp_flags*/
+ 	0,		/* tp_doc */
+ 	(recurseproc)dict_recurse,	/* tp_recurse */
+ 	(inquiry)dict_gc_clear,		/* tp_clear */
+ #endif
  };
  
  /* For backward compatibility with old dictionary interface */
Index: 0.3/Objects/funcobject.c
*** 0.3/Objects/funcobject.c Tue, 25 Apr 2000 17:33:19 -0600 nas (python/E/23_funcobject 1.1 644)
--- gc.6(w)/Objects/funcobject.c Tue, 25 Apr 2000 17:59:00 -0600 nas (python/E/23_funcobject 1.2 644)
***************
*** 40,47 ****
  	PyObject *code;
  	PyObject *globals;
  {
! 	PyFunctionObject *op = PyObject_NEW(PyFunctionObject,
! 					    &PyFunction_Type);
  	if (op != NULL) {
  		PyObject *doc;
  		PyObject *consts;
--- 40,46 ----
  	PyObject *code;
  	PyObject *globals;
  {
! 	PyFunctionObject *op = PyGC_NEW(PyFunctionObject, &PyFunction_Type);
  	if (op != NULL) {
  		PyObject *doc;
  		PyObject *consts;
***************
*** 63,68 ****
--- 62,68 ----
  		Py_INCREF(doc);
  		op->func_doc = doc;
  	}
+ 	PyGC_Insert((PyObject *)op);
  	return (PyObject *)op;
  }
  
***************
*** 186,197 ****
  func_dealloc(op)
  	PyFunctionObject *op;
  {
  	Py_DECREF(op->func_code);
  	Py_DECREF(op->func_globals);
  	Py_DECREF(op->func_name);
  	Py_XDECREF(op->func_defaults);
  	Py_XDECREF(op->func_doc);
! 	PyMem_DEL(op);
  }
  
  static PyObject*
--- 186,198 ----
  func_dealloc(op)
  	PyFunctionObject *op;
  {
+ 	PyGC_Remove((PyObject *)op);
  	Py_DECREF(op->func_code);
  	Py_DECREF(op->func_globals);
  	Py_DECREF(op->func_name);
  	Py_XDECREF(op->func_defaults);
  	Py_XDECREF(op->func_doc);
! 	PyGC_Del((PyObject *)op);
  }
  
  static PyObject*
***************
*** 239,244 ****
--- 240,258 ----
  	return h;
  }
  
+ #ifdef WITH_CYCLE_GC
+ static int
+ func_recurse(PyFunctionObject *f, visitproc visit, void *closure)
+ {
+ 	if (f->func_code) visit(f->func_code, closure);
+ 	if (f->func_globals) visit(f->func_globals, closure);
+ 	if (f->func_defaults) visit(f->func_defaults, closure);
+ 	if (f->func_doc) visit(f->func_doc, closure);
+ 	if (f->func_name) visit(f->func_name, closure);
+ 	return 1;
+ }
+ #endif /* WITH_CYCLE_GC */
+ 
  PyTypeObject PyFunction_Type = {
  	PyObject_HEAD_INIT(&PyType_Type)
  	0,
***************
*** 255,258 ****
--- 269,282 ----
  	0,		/*tp_as_sequence*/
  	0,		/*tp_as_mapping*/
  	(hashfunc)func_hash, /*tp_hash*/
+ #ifdef WITH_CYCLE_GC
+ 	0,		/*tp_call*/
+ 	0,		/*tp_str*/
+ 	0,		/*tp_getattro*/
+ 	0,		/*tp_setattro*/
+ 	0,		/* tp_as_buffer */
+ 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GCINFO, /*tp_flags*/
+ 	0,		/* tp_doc */
+ 	(recurseproc)func_recurse,	/* tp_recurse */
+ #endif
  };
Index: 0.3/Objects/listobject.c
*** 0.3/Objects/listobject.c Tue, 25 Apr 2000 17:33:19 -0600 nas (python/E/25_listobject 1.1 644)
--- gc.6(w)/Objects/listobject.c Tue, 25 Apr 2000 17:59:00 -0600 nas (python/E/25_listobject 1.1.1.1 644)
***************
*** 61,94 ****
  	int i;
  	PyListObject *op;
  	size_t nbytes;
  	if (size < 0) {
  		PyErr_BadInternalCall();
  		return NULL;
  	}
! 	nbytes = size * sizeof(PyObject *);
! 	/* Check for overflow */
! 	if (nbytes / sizeof(PyObject *) != (size_t)size) {
! 		return PyErr_NoMemory();
! 	}
! 	op = (PyListObject *) malloc(sizeof(PyListObject));
! 	if (op == NULL) {
! 		return PyErr_NoMemory();
! 	}
! 	if (size <= 0) {
! 		op->ob_item = NULL;
! 	}
  	else {
! 		op->ob_item = (PyObject **) malloc(nbytes);
! 		if (op->ob_item == NULL) {
! 			free((ANY *)op);
  			return PyErr_NoMemory();
  		}
  	}
- 	op->ob_type = &PyList_Type;
  	op->ob_size = size;
  	for (i = 0; i < size; i++)
  		op->ob_item[i] = NULL;
! 	_Py_NewReference((PyObject *)op);
  	return (PyObject *) op;
  }
  
--- 61,95 ----
  	int i;
  	PyListObject *op;
  	size_t nbytes;
+ 	PyObject **item;
+ 
  	if (size < 0) {
  		PyErr_BadInternalCall();
  		return NULL;
  	}
! 	if (size == 0)
! 		item = NULL;
  	else {
! 		nbytes = size * sizeof(PyObject *);
! 		/* Check for overflow */
! 		if (nbytes / sizeof(PyObject *) != (size_t)size) {
  			return PyErr_NoMemory();
  		}
+ 		item = (PyObject **) malloc(nbytes);
+ 		if (item == NULL)
+ 			return PyErr_NoMemory();
+ 	}
+ 	op = PyGC_NEW(PyListObject, &PyList_Type);
+ 	if (op == NULL) {
+ 		if (item != NULL)
+ 			free((ANY *)item);
+ 		return PyErr_NoMemory();
  	}
  	op->ob_size = size;
+ 	op->ob_item = item;
  	for (i = 0; i < size; i++)
  		op->ob_item[i] = NULL;
! 	PyGC_Insert((PyObject *)op);
  	return (PyObject *) op;
  }
  
***************
*** 215,220 ****
--- 216,222 ----
  	PyListObject *op;
  {
  	int i;
+ 	PyGC_Remove((PyObject *)op);
  	Py_TRASHCAN_SAFE_BEGIN(op)
  	if (op->ob_item != NULL) {
  		/* Do it backwards, for Christian Tismer.
***************
*** 227,233 ****
  		}
  		free((ANY *)op->ob_item);
  	}
! 	free((ANY *)op);
  	Py_TRASHCAN_SAFE_END(op)
  }
  
--- 229,235 ----
  		}
  		free((ANY *)op->ob_item);
  	}
! 	PyGC_Del((PyObject *)op);
  	Py_TRASHCAN_SAFE_END(op)
  }
  
***************
*** 1399,1404 ****
--- 1401,1429 ----
  	return NULL;
  }
  
+ #ifdef WITH_CYCLE_GC
+ static int
+ list_recurse(PyListObject *o, visitproc visit, void *closure)
+ {
+ 	int i;
+ 	PyObject *x;
+ 
+ 	for (i = o->ob_size; --i >= 0; ) {
+ 		x = o->ob_item[i];
+ 		if (x != NULL && !visit(x, closure))
+ 			return 0;
+ 	}
+ 	return 1;
+ }
+ 
+ static int
+ list_clear(PyListObject *lp)
+ {
+ 	(void) PyList_SetSlice((PyObject *)lp, 0, lp->ob_size, 0);
+ 	return 0;
+ }
+ #endif /* WITH_CYCLE_GC */
+ 
  static char append_doc[] =
  "L.append(object) -- append object to end";
  static char extend_doc[] =
***************
*** 1464,1469 ****
--- 1489,1506 ----
  	0,		/*tp_as_number*/
  	&list_as_sequence,	/*tp_as_sequence*/
  	0,		/*tp_as_mapping*/
+ #ifdef WITH_CYCLE_GC
+ 	0,		/* tp_hash */
+ 	0,		/* tp_call */
+ 	0,		/* tp_str */
+ 	0,		/* tp_getattro */
+ 	0,		/* tp_setattro */
+ 	0,		/* tp_as_buffer */
+ 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GCINFO,	/* tp_flags */
+ 	0,		/* tp_doc */
+ 	(recurseproc)list_recurse,	/* tp_recurse */
+ 	(inquiry)list_clear,	/* tp_clear */
+ #endif
  };
  
  
***************
*** 1532,1536 ****
--- 1569,1584 ----
  	0,		/*tp_as_number*/
  	&immutable_list_as_sequence,	/*tp_as_sequence*/
  	0,		/*tp_as_mapping*/
+ #ifdef WITH_CYCLE_GC
+ 	0,		/* tp_hash */
+ 	0,		/* tp_call */
+ 	0,		/* tp_str */
+ 	0,		/* tp_getattro */
+ 	0,		/* tp_setattro */
+ 	0,		/* tp_as_buffer */
+ 	Py_TPFLAGS_DEFAULT,	/* tp_flags */
+ 	0,		/* tp_doc */
+ 	(recurseproc)list_recurse,	/* tp_recurse */
+ #endif
  };
  
Index: 0.3/Objects/tupleobject.c
*** 0.3/Objects/tupleobject.c Tue, 25 Apr 2000 17:33:19 -0600 nas (python/E/33_tupleobjec 1.1 644)
--- gc.6(w)/Objects/tupleobject.c Tue, 25 Apr 2000 17:59:00 -0600 nas (python/E/33_tupleobjec 1.1.1.1 644)
***************
*** 84,89 ****
--- 84,90 ----
  		op->ob_type = &PyTuple_Type;
  		op->ob_size = size;
  #endif
+ 		_Py_NewReference((PyObject *)op);
  	}
  	else
  #endif
***************
*** 97,112 ****
  			return PyErr_NoMemory();
  		}
  		;
! 		op = (PyTupleObject *) malloc(nbytes);
  		if (op == NULL)
! 			return PyErr_NoMemory();
! 
! 		op->ob_type = &PyTuple_Type;
! 		op->ob_size = size;
  	}
  	for (i = 0; i < size; i++)
  		op->ob_item[i] = NULL;
! 	_Py_NewReference((PyObject *)op);
  #if MAXSAVESIZE > 0
  	if (size == 0) {
  		free_tuples[0] = op;
--- 98,110 ----
  			return PyErr_NoMemory();
  		}
  		;
! 		op = PyGC_NEW_VAR(PyTupleObject, &PyTuple_Type, size);
  		if (op == NULL)
! 			return NULL;
  	}
  	for (i = 0; i < size; i++)
  		op->ob_item[i] = NULL;
! 
  #if MAXSAVESIZE > 0
  	if (size == 0) {
  		free_tuples[0] = op;
***************
*** 114,119 ****
--- 112,118 ----
  		Py_INCREF(op);	/* extra INCREF so that this is never freed */
  	}
  #endif
+ 	PyGC_Insert((PyObject *)op);
  	return (PyObject *) op;
  }
  
***************
*** 179,184 ****
--- 178,184 ----
  {
  	register int i;
  	register int len =  op->ob_size;
+ 	PyGC_Remove((PyObject *)op);
  	Py_TRASHCAN_SAFE_BEGIN(op)
  	if (len > 0) {
  		i = len;
***************
*** 193,199 ****
  		}
  #endif
  	}
! 	free((ANY *)op);
  done:
  	Py_TRASHCAN_SAFE_END(op)
  }
--- 193,199 ----
  		}
  #endif
  	}
! 	PyGC_Del((PyObject *)op);
  done:
  	Py_TRASHCAN_SAFE_END(op)
  }
***************
*** 401,406 ****
--- 401,422 ----
  	return (PyObject *) np;
  }
  
+ #ifdef WITH_CYCLE_GC
+ static int
+ tuplerecurse(PyTupleObject *o, visitproc visit, void *closure)
+ {
+ 	int i;
+ 	PyObject *x;
+ 
+ 	for (i = o->ob_size; --i >= 0; ) {
+ 		x = o->ob_item[i];
+ 		if (x != NULL && !visit(x, closure))
+ 			return 0;
+ 	}
+ 	return 1;
+ }
+ #endif /* WITH_CYCLE_GC */
+ 
  static PySequenceMethods tuple_as_sequence = {
  	(inquiry)tuplelength, /*sq_length*/
  	(binaryfunc)tupleconcat, /*sq_concat*/
***************
*** 427,432 ****
--- 443,458 ----
  	&tuple_as_sequence,	/*tp_as_sequence*/
  	0,		/*tp_as_mapping*/
  	(hashfunc)tuplehash, /*tp_hash*/
+ #ifdef WITH_CYCLE_GC
+ 	0,		/* tp_call */
+ 	0,		/* tp_str */
+ 	0,		/* tp_getattro */
+ 	0,		/* tp_setattro */
+ 	0,		/* tp_as_buffer */
+ 	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GCINFO,	/* tp_flags */
+ 	0,		/* tp_doc */
+ 	(recurseproc)tuplerecurse,	/* tp_recurse */
+ #endif
  };
  
  /* The following function breaks the notion that tuples are immutable:
***************
*** 511,519 ****
  	} else 
  #endif		
  	{
! 		sv = (PyTupleObject *)
! 			realloc((char *)v,
! 				sizeof(PyTupleObject) + newsize * sizeof(PyObject *));
  		*pv = (PyObject *) sv;
  		if (sv == NULL) {
  			PyMem_DEL(v);
--- 537,545 ----
  	} else 
  #endif		
  	{
! 		PyGC_Remove((PyObject *)v);
! 		sv = (PyTupleObject *)PyGC_Realloc((PyObject *)v,
! 			sizeof(PyTupleObject) + newsize * sizeof(PyObject *));
  		*pv = (PyObject *) sv;
  		if (sv == NULL) {
  			PyMem_DEL(v);
***************
*** 522,527 ****
--- 548,554 ----
  		}
  	}
  	_Py_NewReference((PyObject *)sv);
+ 	PyGC_Insert((PyObject *)sv);
  	for (i = sv->ob_size; i < newsize; i++)
  		sv->ob_item[i] = NULL;
  	if (last_is_sticky && sizediff > 0) {
***************
*** 551,557 ****
  		while (p) {
  			q = p;
  			p = (PyTupleObject *)(p->ob_item[0]);
! 			PyMem_DEL(q);
  		}
  	}
  #endif
--- 578,584 ----
  		while (p) {
  			q = p;
  			p = (PyTupleObject *)(p->ob_item[0]);
! 			PyGC_Del((PyObject *)q);
  		}
  	}
  #endif
Index: 0.3/PC/config.c
*** 0.3/PC/config.c Tue, 25 Apr 2000 17:33:19 -0600 nas (python/E/39_config.c 1.1 644)
--- gc.6(w)/PC/config.c Tue, 25 Apr 2000 17:59:00 -0600 nas (python/E/39_config.c 1.1.1.1 644)
***************
*** 41,46 ****
--- 41,49 ----
  extern void initbinascii();
  extern void initcmath();
  extern void initerrno();
+ #ifdef WITH_CYCLE_GC
+ extern void initgc();
+ #endif
  extern void initimageop();
  extern void initmath();
  extern void initmd5();
***************
*** 79,84 ****
--- 82,90 ----
          {"binascii", initbinascii},
          {"cmath", initcmath},
          {"errno", initerrno},
+ #ifdef WITH_CYCLE_GC
+         {"gc", initgc},
+ #endif
          {"imageop", initimageop},
          {"math", initmath},
          {"md5", initmd5},
Index: 0.3/PC/config.h
*** 0.3/PC/config.h Tue, 25 Apr 2000 17:33:19 -0600 nas (python/E/40_config.h 1.1 644)
--- gc.6(w)/PC/config.h Tue, 25 Apr 2000 17:59:00 -0600 nas (python/E/40_config.h 1.1.1.1 644)
***************
*** 384,389 ****
--- 384,392 ----
  /* Define if you want to use the GNU readline library */
  /* #define WITH_READLINE 1 */
  
+ /* Define if you want cycle garbage collection */
+ #define WITH_CYCLE_GC 1
+ 
  /* Define if you have clock.  */
  /* #define HAVE_CLOCK */
  
Index: 0.3/PCbuild/python16.dsp
*** 0.3/PCbuild/python16.dsp Tue, 25 Apr 2000 17:33:19 -0600 nas (python/G/1_python16.d 1.1 644)
--- gc.6(w)/PCbuild/python16.dsp Tue, 25 Apr 2000 17:59:00 -0600 nas (python/G/1_python16.d 1.2 644)
***************
*** 645,650 ****
--- 645,665 ----
  # End Source File
  # Begin Source File
  
+ SOURCE=..\Modules\gcmodule.c
+ 
+ !IF  "$(CFG)" == "python16 - Win32 Release"
+ 
+ !ELSEIF  "$(CFG)" == "python16 - Win32 Debug"
+ 
+ !ELSEIF  "$(CFG)" == "python16 - Win32 Alpha Debug"
+ 
+ !ELSEIF  "$(CFG)" == "python16 - Win32 Alpha Release"
+ 
+ !ENDIF 
+ 
+ # End Source File
+ # Begin Source File
+ 
  SOURCE=..\Python\getargs.c
  
  !IF  "$(CFG)" == "python16 - Win32 Release"
Index: 0.3/Modules/gcmodule.c
*** 0.3/Modules/gcmodule.c Wed, 26 Apr 2000 09:48:07 -0600 nas ()
--- gc.6(w)/Modules/gcmodule.c Wed, 26 Apr 2000 09:39:29 -0600 nas (python/M/10_gcmodule.c 1.3 644)
***************
*** 0 ****
--- 1,771 ----
+ /*
+  
+   Reference Cycle Garbage Collection
+   ==================================
+ 
+   Neil Schemenauer <nascheme@enme.ucalgary.ca>
+ 
+   Based on a post on the python-dev list.  Ideas from Guido van Rossum,
+   Eric Tiedemann, and various others.
+ 
+   http://www.enme.calgary.ca/~nascheme/python/gc.html
+   http://www.python.org/pipermail/python-dev/2000-March/003869.html
+   http://www.python.org/pipermail/python-dev/2000-March/004010.html
+   http://www.python.org/pipermail/python-dev/2000-March/004022.html
+ 
+   For a highlevel view of the collection process, read the collect
+   function.
+ 
+   TODO:
+ 	use a different interface for set_debug() (keywords)?
+ 	inline PyGC_Insert and PyGC_Remove calls?
+ 	tune parameters
+ 
+ */
+ 
+ #include "Python.h"
+ 
+ #define DEBUG
+ 
+ #ifndef WITH_CYCLE_GC
+ #error "You must define WITH_CYCLE_GC to include this module"
+ #endif
+ 
+ /* Structure *prefixed* to container objects participating in GC */ 
+ typedef struct _gcinfo {
+ 	struct _gcinfo *gc_next;
+ 	struct _gcinfo *gc_prev;
+ 	int gc_refs;
+ } PyGCInfo;
+ 
+ /* Test if a type has GC info */
+ #define Py_TYPEISGC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GCINFO)
+ 
+ /* Test if an object has GC info */
+ #define Py_ISGC(o) Py_TYPEISGC((o)->ob_type)
+ 
+ /* Get an object's GC info -- NULL if the object has none */
+ #define Py_GCINFO(o) (Py_ISGC(o) ? ((PyGCInfo *)(o)-1) : NULL)
+ 
+ /* Unsafe version of Py_GCINFO() -- only call if Py_ISGC(p) is true */
+ #define Py_GCINFO_UNSAFE(o) ((PyGCInfo *)(o)-1)
+ 
+ /* Get the object given the PyGCInfo */
+ #define Py_GCOBJ(g) ((PyObject *)((g)+1))
+ 
+ /* magic gc_refs value */
+ #define GC_MOVED -1
+ 
+ /*** Global GC state ***/
+ 
+ /* linked lists of container objects */
+ static PyGCInfo generation0 = {&generation0, &generation0, 0};
+ static PyGCInfo generation1 = {&generation1, &generation1, 0};
+ static PyGCInfo generation2 = {&generation2, &generation2, 0};
+ static int generation = 0; /* current generation being collected */
+ 
+ /* collection frequencies, XXX tune these */
+ static int threshold0 = 100; /* net new containers before collection */
+ static int threshold1 = 10;  /* generation0 collections before collecting 1 */
+ static int threshold2 = 10;  /* generation1 collections before collecting 2 */
+ 
+ /* net new objects allocated since last collection */
+ static int allocated = 0;
+ 
+ /* set for debugging information */
+ #define DEBUG_STATS		(1<<0) /* print collection statistics */
+ #define DEBUG_COLLECTABLE	(1<<1) /* print collectable objects */
+ #define DEBUG_UNCOLLECTABLE	(1<<2) /* print uncollectable objects */
+ #define DEBUG_INSTANCES		(1<<3) /* print instances */
+ #define DEBUG_OBJECTS		(1<<4) /* print other objects */
+ #define DEBUG_LEAK		DEBUG_COLLECTABLE | \
+ 				DEBUG_UNCOLLECTABLE | \
+ 				DEBUG_INSTANCES | \
+ 				DEBUG_OBJECTS
+ static int debug = DEBUG_LEAK;
+ 
+ /* list of uncollectable objects */
+ static PyObject *garbage = NULL;
+ 
+ 
+ /*** list functions ***/
+ 
+ static void
+ list_init(PyGCInfo *list)
+ {
+ 	list->gc_prev = list;
+ 	list->gc_next = list;
+ }
+ 
+ static void
+ list_append(PyGCInfo *node, PyGCInfo *list)
+ {
+ 	node->gc_next = list;
+ 	node->gc_prev = list->gc_prev;
+ 	node->gc_prev->gc_next = node;
+ 	list->gc_prev = node;
+ }
+ 
+ static void
+ list_remove(PyGCInfo *node)
+ {
+ 	node->gc_prev->gc_next = node->gc_next;
+ 	node->gc_next->gc_prev = node->gc_prev;
+ #ifdef DEBUG
+ 	node->gc_next = NULL;
+ 	node->gc_prev = NULL;
+ #endif
+ }
+ 
+ static void 
+ list_move(PyGCInfo *from, PyGCInfo *to)
+ {
+ 	if (from->gc_next == from) {
+ 		/* empty from list */
+ 		list_init(to);
+ 	} else {
+ 		to->gc_next = from->gc_next;
+ 		to->gc_next->gc_prev = to;
+ 		to->gc_prev = from->gc_prev;
+ 		to->gc_prev->gc_next = to;
+ 	}
+ 	list_init(from);
+ }
+ 
+ /* append a list onto another list */
+ static void
+ list_lappend(PyGCInfo *from, PyGCInfo *to)
+ {
+ 	PyGCInfo *tail;
+ 	if (from->gc_next != from) {
+ 		tail = to->gc_prev;
+ 		tail->gc_next = from->gc_next;
+ 		tail->gc_next->gc_prev = tail;
+ 		to->gc_prev = from->gc_prev;
+ 		to->gc_prev->gc_next = to;
+ 	}
+ 	list_init(from);
+ }
+ 
+ static long
+ list_size(PyGCInfo *list)
+ {
+ 	PyGCInfo *gc;
+ 	long n = 0;
+ 	for (gc = list->gc_next; gc != list; gc = gc->gc_next) {
+ 		n++;
+ 	}
+ 	return n;
+ }
+ 
+ 
+ #ifdef DEBUG
+ static void
+ list_print(PyGCInfo *list) {
+ 	PyGCInfo *op = list;
+ 	do {
+ 		printf("%x %x %x %d\n", (long)op, (long)op->gc_prev, 
+ 				(long)op->gc_next, (long)op->gc_refs);
+ 
+ 		op = op->gc_next;
+ 	} while (op != list);
+ }
+ #endif
+ 
+ /*** end of list stuff ***/
+ 
+ 
+ /* Set all gc_refs = ob_refcnt */
+ static void
+ update_refs(PyGCInfo *containers)
+ {
+ 	PyGCInfo *gc = containers->gc_next;
+ 	for (; gc != containers; gc=gc->gc_next) {
+ 		gc->gc_refs = Py_GCOBJ(gc)->ob_refcnt;
+ 	}
+ }
+ 
+ static int
+ visit_decref(PyObject *op, void *data)
+ {
+ 	if (op && Py_ISGC(op)) {
+ 		Py_GCINFO_UNSAFE(op)->gc_refs--;
+ 	}
+ 	return 1;
+ }
+ 
+ /* Subtract internal references from gc_refs */
+ static void
+ subtract_refs(PyGCInfo *containers)
+ {
+ 	recurseproc recurse;
+ 	PyGCInfo *gc = containers->gc_next;
+ 	for (; gc != containers; gc=gc->gc_next) {
+ 		recurse = Py_GCOBJ(gc)->ob_type->tp_recurse;
+ 		(void) recurse(Py_GCOBJ(gc),
+ 			       (visitproc)visit_decref,
+ 			       NULL);
+ 	}
+ }
+ 
+ /* Append objects with gc_refs > 0 to roots list */
+ static void
+ move_roots(PyGCInfo *containers, PyGCInfo *roots)
+ {
+ 	PyGCInfo *next;
+ 	PyGCInfo *gc = containers->gc_next;
+ 	while (gc != containers) {
+ 		next = gc->gc_next;
+ 		if (gc->gc_refs > 0) {
+ 			list_remove(gc);
+ 			list_append(gc, roots);
+ 			gc->gc_refs = GC_MOVED;
+ 		}
+ 		gc = next;
+ 	}
+ }
+ 
+ static int
+ visit_reachable(PyObject *op, PyGCInfo *roots)
+ {
+ 	PyGCInfo *gc = Py_GCINFO(op);
+ 	if (gc && gc->gc_refs != GC_MOVED) {
+ 		list_remove(gc);
+ 		list_append(gc, roots);
+ 		gc->gc_refs = GC_MOVED;
+ 	}
+ 	return 1;
+ }
+ 
+ /* Move objects referenced from reachable to reachable set. */
+ static void
+ move_root_reachable(PyGCInfo *reachable)
+ {
+ 	recurseproc recurse;
+ 	PyGCInfo *gc = reachable->gc_next;
+ 	for (; gc != reachable; gc=gc->gc_next) {
+ 		/* careful, reachable list is growing here */
+ 		PyObject *op = Py_GCOBJ(gc);
+ 		recurse = op->ob_type->tp_recurse;
+ 		(void) recurse(op,
+ 			       (visitproc)visit_reachable,
+ 			       (void *)reachable);
+ 	}
+ }
+ 
+ /* move all objects with finalizers (instances with __del__) */
+ static void
+ move_finalizers(PyGCInfo *unreachable, PyGCInfo *finalizers)
+ {
+ 	PyGCInfo *next;
+ 	PyGCInfo *gc = unreachable->gc_next;
+ 	static PyObject *delstr;
+ 	if (delstr == NULL) {
+ 		delstr = PyString_InternFromString("__del__");
+ 	}
+ 	for (; gc != unreachable; gc=next) {
+ 		PyObject *op = Py_GCOBJ(gc);
+ 		next = gc->gc_next;
+ 		if (PyInstance_Check(op) && PyObject_HasAttr(op, delstr)) {
+ 			list_remove(gc);
+ 			list_append(gc, finalizers);
+ 		}
+ 	}
+ }
+ 
+ 
+ /* called by tp_recurse */
+ static int
+ visit_finalizer_reachable(PyObject *op, PyGCInfo *finalizers)
+ {
+ 	PyGCInfo *gc = Py_GCINFO(op);
+ 	if (gc && gc->gc_refs != GC_MOVED) {
+ 		list_remove(gc);
+ 		list_append(gc, finalizers);
+ 		gc->gc_refs = GC_MOVED;
+ 	}
+ 	return 1;
+ }
+ 
+ /* Move objects referenced from roots to roots */
+ static void
+ move_finalizer_reachable(PyGCInfo *finalizers)
+ {
+ 	recurseproc recurse;
+ 	PyGCInfo *gc = finalizers->gc_next;
+ 	for (; gc != finalizers; gc=gc->gc_next) {
+ 		/* careful, finalizers list is growing here */
+ 		recurse = Py_GCOBJ(gc)->ob_type->tp_recurse;
+ 		(void) recurse(Py_GCOBJ(gc), 
+ 			       (visitproc)visit_finalizer_reachable,
+ 			       (void *)finalizers);
+ 	}
+ }
+ 
+ static void
+ debug_instance(PyObject *output, char *msg, PyInstanceObject *inst)
+ {
+ 	char buf[200];
+ 	char *cname;
+ 	/* be careful not to create new dictionaries */
+ 	PyObject *classname = inst->in_class->cl_name;
+ 	if (classname != NULL && PyString_Check(classname))
+ 		cname = PyString_AsString(classname);
+ 	else
+ 		cname = "?";
+ 	sprintf(buf, "gc: %s<%.100s instance at %lx>\n", 
+ 			msg, cname, (long)inst);
+ 	PyFile_WriteString(buf, output);
+ }
+ 
+ static void
+ debug_cycle(PyObject *output, char *msg, PyObject *op)
+ {
+ 	if ((debug & DEBUG_INSTANCES) && PyInstance_Check(op)) {
+ 		debug_instance(output, msg, (PyInstanceObject *)op);
+ 	} else if (debug & DEBUG_OBJECTS) {
+ 		char buf[200];
+ 		sprintf(buf, "gc: %s<%s 0x%x>\n",
+ 				msg,
+ 				op->ob_type->tp_name,
+ 				(long)op);
+ 		PyFile_WriteString(buf, output);
+ 	}
+ }
+ 
+ /* Handle uncollectable garbage (cycles with finalizers). */
+ static void
+ handle_finalizers(PyGCInfo *finalizers, PyGCInfo *old)
+ {
+ 	PyGCInfo *gc;
+ 	if (garbage == NULL) {
+ 		garbage = PyList_New(0);
+ 	}
+ 	for (gc = finalizers->gc_next; gc != finalizers;
+ 			gc = finalizers->gc_next) {
+ 		PyObject *op = Py_GCOBJ(gc);
+ 		/* Add all instances to a Python accessible list of garbage */
+ 		if (PyInstance_Check(op)) {
+ 			PyList_Append(garbage, op);
+ 		}
+ 		/* We assume that all objects in finalizers are reachable from
+ 		 * instances.  Once we add the instances to the garbage list
+ 		 * everything is reachable from Python again. */
+ 		list_remove(gc);
+ 		list_append(gc, old);
+ 	}
+ }
+ 
+ /* Break reference cycles by clearing the containers involved.  This is
+  * tricky business as the lists can be changing and we don't know which
+  * objects may be freed.  It is possible I screwed something up here. */
+ static void
+ delete_garbage(PyGCInfo *unreachable, PyGCInfo *old)
+ {
+ 	inquiry clear;
+ 
+ 	while (unreachable->gc_next != unreachable) {
+ 		PyGCInfo *gc = unreachable->gc_next;
+ 		PyObject *op = Py_GCOBJ(gc);
+ 		/*
+ 		PyList_Append(garbage, op);
+ 		*/
+ 		if ((clear = op->ob_type->tp_clear) != NULL) {
+ 			Py_INCREF(op);
+ 			clear((PyObject *)op);
+ 			Py_DECREF(op);
+ 		}
+ 		/* only try to call tp_clear once for each object */
+ 		if (unreachable->gc_next == gc) {
+ 			/* still alive, move it, it may die later */
+ 			list_remove(gc);
+ 			list_append(gc, old);
+ 		}
+ 	}
+ }
+ 
+ /* This is the main function.  Read this to understand how the
+  * collection process works. */
+ static long
+ collect(PyGCInfo *young, PyGCInfo *old)
+ {
+ 	long n = 0;
+ 	long m = 0;
+ 	PyGCInfo reachable;
+ 	PyGCInfo unreachable;
+ 	PyGCInfo finalizers;
+ 	PyGCInfo *gc;
+ 	PyObject *output = NULL;
+ 
+ 	if (debug) {
+ 		output = PySys_GetObject("stderr");
+ 	}
+ 	if (debug & DEBUG_STATS) {
+ 		char buf[100];
+ 		sprintf(buf, "gc: collecting generation %d...\n", generation);
+ 		PyFile_WriteString(buf,output);
+ 		sprintf(buf, "gc: objects in each generation: %d %d %d\n",
+ 			list_size(&generation0),
+ 			list_size(&generation1),
+ 			list_size(&generation2));
+ 		PyFile_WriteString(buf,output);
+ 	}
+ 
+ 	/* Using ob_refcnt and gc_refs, calculate which objects in the
+ 	 * container set are reachable from outside the set (ie. have a
+ 	 * refcount greater than 0 when all the references within the
+ 	 * set are taken into account */
+ 	update_refs(young);
+ 	subtract_refs(young);
+ 
+ 	/* Move everything reachable from outside the set into the
+ 	 * reachable set (ie. gc_refs > 0).  Next, move everything
+ 	 * reachable from objects in the reachable set. */
+ 	list_init(&reachable);
+ 	move_roots(young, &reachable);
+ 	move_root_reachable(&reachable);
+ 
+ 	/* move unreachable objects to a temporary list, new objects can be
+ 	 * allocated after this point */
+ 	list_init(&unreachable);
+ 	list_move(young, &unreachable);
+ 
+ 	/* move reachable objects to next generation */
+ 	list_lappend(&reachable, old);
+ 
+ 	/* Move objects reachable from finalizers, we can't safely delete
+ 	 * them.  Python programmers should take care not to create such
+ 	 * things.  For Python finalizers means instance objects with
+ 	 * __del__ methods. */
+ 	list_init(&finalizers);
+ 	move_finalizers(&unreachable, &finalizers);
+ 	move_finalizer_reachable(&finalizers);
+ 
+ 	/* Collect statistics on collectable objects found and print
+ 	 * debugging information. */
+ 	for (gc = unreachable.gc_next; gc != &unreachable;
+ 			gc = gc->gc_next) {
+ 		m++;
+ 		if (output != NULL && (debug & DEBUG_COLLECTABLE)) {
+ 			debug_cycle(output, "collectable ", Py_GCOBJ(gc));
+ 		}
+ 	}
+ 	/* 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(&unreachable, old);
+ 
+ 	/* Collect statistics on uncollectable objects found and print
+ 	 * debugging information. */
+ 	for (gc = finalizers.gc_next; gc != &finalizers;
+ 			gc = gc->gc_next) {
+ 		n++;
+ 		if (output != NULL && (debug & DEBUG_UNCOLLECTABLE)) {
+ 			debug_cycle(output, "uncollectable ", Py_GCOBJ(gc));
+ 		}
+ 	}
+ 	if (output != NULL && (debug & DEBUG_STATS)) {
+ 		if (m == 0 && n == 0) {
+ 			PyFile_WriteString("gc: done.\n", output);
+ 		} else {
+ 			char buf[200];
+ 			sprintf(buf,
+ 				"gc: done, %d unreachable, %d uncollectable.\n",
+ 				n+m, n);
+ 			PyFile_WriteString(buf, output);
+ 		}
+ 	}
+ 
+ 	/* 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);
+ 
+ 	allocated = 0;
+ 	PyErr_Clear(); /* in case writing to sys.stderr failed */
+ 	return n+m;
+ }
+ 
+ static long
+ collect_generations(void)
+ {
+ 	static long collections0 = 0;
+ 	static long collections1 = 0;
+ 	long n;
+ 
+ 
+ 	if (collections1 > threshold2) {
+ 		generation = 2;
+ 		list_lappend(&generation0, &generation2);
+ 		list_lappend(&generation1, &generation2);
+ 		if (generation2.gc_next != &generation2) {
+ 			n = collect(&generation2, &generation2);
+ 		}
+ 		collections1 = 0;
+ 	} else if (collections0 > threshold1) {
+ 		generation = 1;
+ 		collections1++;
+ 		list_lappend(&generation0, &generation1);
+ 		if (generation1.gc_next != &generation1) {
+ 			n = collect(&generation1, &generation2);
+ 		}
+ 		collections0 = 0;
+ 	} else {
+ 		generation = 0;
+ 		collections0++;
+ 		if (generation0.gc_next != &generation0) {
+ 			n = collect(&generation0, &generation1);
+ 		}
+ 	}
+ 	return n;
+ }
+ 
+ void
+ PyGC_Insert(PyObject *op)
+ {
+ 	/* collection lock since collecting may cause allocations */
+ 	static int collecting = 0;
+ 
+ #ifdef DEBUG
+ 	/*fprintf(stderr, "Insert 0x%x\n", op);*/
+ 	if (!Py_ISGC(op)) {
+ 		abort();
+ 	}
+ #endif
+ 	if (threshold0 && allocated > threshold0 && !collecting) {
+ 		collecting++;
+ 		collect_generations();
+ 		collecting--;
+ 	}
+ 	allocated++;
+ 	list_append(Py_GCINFO(op), &generation0);
+ }
+ 
+ void
+ PyGC_Remove(PyObject *op)
+ {
+ 	PyGCInfo *g = Py_GCINFO_UNSAFE(op);
+ #ifdef DEBUG
+ 	/*fprintf(stderr, "Remove 0x%x 0x%x\n", g, op);*/
+ #endif
+ 	if (allocated > 0) {
+ 		allocated--;
+ 	}
+ 	list_remove(g);
+ }
+ 
+ PyObject *
+ _PyGC_New(PyTypeObject *tp)
+ {
+ 	PyObject *op;
+ 	PyGCInfo *g = malloc(sizeof(*g) + tp->tp_basicsize);
+ 	if (g == NULL) {
+ 		return PyErr_NoMemory();
+ 	}
+ #ifdef DEBUG
+ 	g->gc_next = NULL;
+ 	g->gc_prev = NULL;
+ #endif
+ 	op = Py_GCOBJ(g);
+ 	op->ob_type = tp;
+ 	_Py_NewReference(op);
+ 	return op;
+ }
+ 
+ PyVarObject *
+ _PyGC_NewVar(PyTypeObject *tp, int size)
+ {
+ 	PyVarObject *op;
+ 	PyGCInfo *g = 
+ 	g = (PyGCInfo *) malloc(sizeof(*g) + 
+ 				tp->tp_basicsize +
+ 				size * tp->tp_itemsize);
+ 	if (g == NULL) {
+ 		return (PyVarObject *)PyErr_NoMemory();
+ 	}
+ #ifdef DEBUG
+ 	g->gc_next = NULL;
+ 	g->gc_prev = NULL;
+ #endif
+ 	op = (PyVarObject *)Py_GCOBJ(g);
+ 	op->ob_type = tp;
+ 	op->ob_size = size;
+ 	_Py_NewReference((PyObject *)op);
+ 	return op;
+ }
+ 
+ PyObject *
+ PyGC_Realloc(PyObject *op, int size)
+ {
+ 	PyGCInfo *g = Py_GCINFO(op);
+ 	g = realloc((char *)g, sizeof(*g) + size);
+ 	if (g == NULL) {
+ 		return NULL;
+ 	} else {
+ 		return Py_GCOBJ(g);
+ 	}
+ }
+ 
+ void
+ PyGC_Del(PyObject *op)
+ {
+ 	PyGCInfo *g = Py_GCINFO_UNSAFE(op);
+ 	free(g);
+ }
+ 
+ static char collect__doc__[] =
+ "collect() -> n\n"
+ "\n"
+ "Run a full collection.  The number of unreachable objects is returned.\n"
+ ;
+ 
+ static PyObject *
+ Py_collect(self, args)
+ 	PyObject *self;
+ 	PyObject *args;
+ {
+ 	long n;
+ 
+ 	if(!PyArg_ParseTuple(args, ""))	/* check no args */
+ 		return NULL;
+ 
+ 	generation = 2;
+ 	list_lappend(&generation0, &generation2);
+ 	list_lappend(&generation1, &generation2);
+ 	n = collect(&generation2, &generation2);
+ 
+ 	return Py_BuildValue("i", n);
+ }
+ 
+ static char set_debug__doc__[] = 
+ "set_debug(flags) -> None\n"
+ "\n"
+ "Set the garbage collection debugging flags. Debugging information is\n"
+ "written to sys.stderr.\n"
+ "\n"
+ "flags is an integer and can have the following bits turned on:\n"
+ "\n"
+ "  DEBUG_STATS - Print statistics during collection.\n"
+ "  DEBUG_COLLECTABLE - Print collectable objects found.\n"
+ "  DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects found.\n"
+ "  DEBUG_INSTANCES - Print instance objects.\n"
+ "  DEBUG_OBJECTS - Print objects other than instances.\n"
+ "  DEBUG_LEAK - Debug leaking programs (everything but STATS).\n"
+ ;
+ 
+ static PyObject *
+ Py_set_debug(self, args)
+ 	PyObject *self;
+ 	PyObject *args;
+ {
+ 	if (!PyArg_ParseTuple(args, "l", &debug))
+ 		return NULL;
+ 
+ 	Py_INCREF(Py_None);
+ 	return Py_None;
+ }
+ 
+ static char get_debug__doc__[] = 
+ "get_debug() -> flags\n"
+ "\n"
+ "Get the garbage collection debugging flags.\n"
+ ;
+ 
+ static PyObject *
+ Py_get_debug(self, args)
+ 	PyObject *self;
+ 	PyObject *args;
+ {
+ 	if(!PyArg_ParseTuple(args, ""))	/* no args */
+ 		return NULL;
+ 
+ 	return Py_BuildValue("i", debug);
+ }
+ 
+ static char set_thresh__doc__[] =
+ "set_threshold(threshold0, [threhold1, threshold2]) -> None\n"
+ "\n"
+ "Sets the collection thresholds.  Setting threshold0 to zero disables\n"
+ "collection.\n"
+ ;
+ 
+ static PyObject *
+ Py_set_thresh(self, args)
+ 	PyObject *self;
+ 	PyObject *args;
+ {
+ 	if (!PyArg_ParseTuple(args, "i|ii", &threshold0, 
+ 				&threshold1, &threshold2))
+ 		return NULL;
+ 
+ 	Py_INCREF(Py_None);
+ 	return Py_None;
+ }
+ 
+ static char get_thresh__doc__[] =
+ "get_threshold() -> (threshold0, threshold1, threshold2)\n"
+ "\n"
+ "Return the current collection thresholds\n"
+ ;
+ 
+ static PyObject *
+ Py_get_thresh(self, args)
+ 	PyObject *self;
+ 	PyObject *args;
+ {
+ 	if(!PyArg_ParseTuple(args, ""))	/* no args */
+ 		return NULL;
+ 
+ 	return Py_BuildValue("(iii)", threshold0, threshold1, threshold2);
+ }
+ 
+ 
+ static char gc__doc__ [] =
+ "This module provides access to the garbage collector for reference cycles.\n"
+ "\n"
+ "collect() -- Do a full collection right now.\n"
+ "set_debug() -- Set debugging flags.\n"
+ "get_debug() -- Get debugging flags.\n"
+ "set_threshold() -- Set the collection thresholds.\n"
+ "get_threshold() -- Return the current the collection thresholds.\n"
+ ;
+ 
+ static PyMethodDef GcMethods[] = {
+ 	{"set_debug",		Py_set_debug,  METH_VARARGS, set_debug__doc__},
+ 	{"get_debug",		Py_get_debug,  METH_VARARGS, get_debug__doc__},
+ 	{"set_threshold",	Py_set_thresh, METH_VARARGS, set_thresh__doc__},
+ 	{"get_threshold",	Py_get_thresh, METH_VARARGS, get_thresh__doc__},
+ 	{"collect",		Py_collect,    METH_VARARGS, collect__doc__},
+ 	{NULL,	NULL}		/* Sentinel */
+ };
+ 
+ void
+ initgc(void)
+ {
+ 	PyObject *m;
+ 	PyObject *d;
+ 
+ 	m = Py_InitModule4("gc",
+ 			      GcMethods,
+ 			      gc__doc__,
+ 			      NULL,
+ 			      PYTHON_API_VERSION);
+ 	d = PyModule_GetDict(m);
+ 	if (garbage == NULL) {
+ 		garbage = PyList_New(0);
+ 	}
+ 	PyDict_SetItemString(d, "garbage", garbage);
+ 	PyDict_SetItemString(d, "DEBUG_STATS",
+ 			PyInt_FromLong(DEBUG_STATS));
+ 	PyDict_SetItemString(d, "DEBUG_COLLECTABLE",
+ 			PyInt_FromLong(DEBUG_COLLECTABLE));
+ 	PyDict_SetItemString(d, "DEBUG_UNCOLLECTABLE",
+ 			PyInt_FromLong(DEBUG_UNCOLLECTABLE));
+ 	PyDict_SetItemString(d, "DEBUG_INSTANCES",
+ 			PyInt_FromLong(DEBUG_INSTANCES));
+ 	PyDict_SetItemString(d, "DEBUG_OBJECTS",
+ 			PyInt_FromLong(DEBUG_OBJECTS));
+ 	PyDict_SetItemString(d, "DEBUG_LEAK",
+ 			PyInt_FromLong(DEBUG_LEAK));
+ }
+ 
Index: 0.3/Modules/Setup.auto.in
*** 0.3/Modules/Setup.auto.in Wed, 26 Apr 2000 09:48:07 -0600 nas ()
--- gc.6(w)/Modules/Setup.auto.in Tue, 25 Apr 2000 17:59:00 -0600 nas (python/M/11_Setup.auto 1.1 644)
***************
*** 0 ****
--- 1,16 ----
+ # This file is transmogrified into Setup.auto by config.status.  Its
+ # purpose is to automatically enable modules given when certain
+ # arguments are given to the configure script.  It replaces the old
+ # Setup.thread.in file.
+ 
+ # Include the thread module when the --with-thread argument is given to
+ # the configure script.
+ #
+ # *NOTE*: if the configure script decides it can't support threads, the
+ # thread module will still be enabled and cause compile errors.  The
+ # solution is not to use --with-thread on platforms that don't support
+ # threads.
+ @USE_THREAD_MODULE@thread threadmodule.c
+ 
+ # Garbage collection enabled with --with-cycle-gc
+ @USE_GC_MODULE@gc gcmodule.c
Index: 0.3/Lib/test/test_gc.py
*** 0.3/Lib/test/test_gc.py Wed, 26 Apr 2000 09:48:07 -0600 nas ()
--- gc.6(w)/Lib/test/test_gc.py Tue, 25 Apr 2000 17:59:00 -0600 nas (python/M/12_test_gc.py 1.1 644)
***************
*** 0 ****
--- 1,90 ----
+ import gc
+ 
+ def test_list():
+     l = []
+     l.append(l)
+     print 'list 0x%x' % id(l)
+     gc.collect()
+     del l
+     assert gc.collect() == 1
+ 
+ def test_dict():
+     d = {}
+     d[1] = d
+     print 'dict 0x%x' % id(d)
+     gc.collect()
+     del d
+     assert gc.collect() == 1
+ 
+ def test_tuple():
+     l = []
+     t = (l,)
+     l.append(t)
+     print 'list 0x%x' % id(l)
+     print 'tuple 0x%x' % id(t)
+     gc.collect()
+     del t
+     del l
+     assert gc.collect() == 2
+ 
+ def test_class():
+     class A:
+         pass
+     A.a = A
+     print 'class 0x%x' % id(A)
+     gc.collect()
+     del A
+     assert gc.collect() > 0
+ 
+ def test_instance():
+     class A:
+         pass
+     a = A()
+     a.a = a
+     print repr(a)
+     gc.collect()
+     del a
+     assert gc.collect() > 0
+ 
+ def test_finalizer():
+     class A:
+         def __del__(self): pass
+     class B:
+         pass
+     a = A()
+     a.a = a
+     id_a = id(a)
+     b = B()
+     b.b = b
+     print 'a', repr(a)
+     print 'b', repr(b)
+     gc.collect()
+     gc.garbage[:] = []
+     del a
+     del b
+     assert gc.collect() > 0
+     assert id(gc.garbage[0]) == id_a
+ 
+ def test_function():
+     d = {}
+     exec("def f(): pass\n") in d
+     print 'dict 0x%x' % id(d)
+     print 'func 0x%x' % id(d['f'])
+     gc.collect()
+     del d
+     assert gc.collect() == 2
+ 
+ 
+ def test_all():
+     debug = gc.get_debug()
+     gc.set_debug(gc.DEBUG_LEAK | gc.DEBUG_STATS)
+     test_list()
+     test_dict()
+     test_tuple()
+     test_class()
+     test_instance()
+     test_finalizer()
+     test_function()
+     gc.set_debug(debug)
+ 
+ test_all()