Index: Objects/moduleobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/moduleobject.c,v retrieving revision 2.47 diff -c -r2.47 moduleobject.c *** Objects/moduleobject.c 16 Jul 2003 22:04:11 -0000 2.47 --- Objects/moduleobject.c 25 Sep 2003 10:18:55 -0000 *************** *** 7,12 **** --- 7,13 ---- typedef struct { PyObject_HEAD PyObject *md_dict; + PyObject *md_weakreflist; } PyModuleObject; static PyMemberDef module_members[] = { *************** *** 24,29 **** --- 25,31 ---- return NULL; nameobj = PyString_FromString(name); m->md_dict = PyDict_New(); + m->md_weakreflist = NULL; if (m->md_dict == NULL || nameobj == NULL) goto fail; if (PyDict_SetItemString(m->md_dict, "__name__", nameobj) != 0) *************** *** 172,177 **** --- 174,181 ---- module_dealloc(PyModuleObject *m) { PyObject_GC_UnTrack(m); + if (m->md_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) m); if (m->md_dict != NULL) { _PyModule_Clear((PyObject *)m); Py_DECREF(m->md_dict); *************** *** 242,248 **** (traverseproc)module_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ ! 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ --- 246,252 ---- (traverseproc)module_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ ! offsetof(PyModuleObject, md_weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ Index: Modules/_hotshot.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/_hotshot.c,v retrieving revision 1.34 diff -c -r1.34 _hotshot.c *** Modules/_hotshot.c 17 Mar 2003 19:46:07 -0000 1.34 --- Modules/_hotshot.c 25 Sep 2003 10:18:56 -0000 *************** *** 351,359 **** --- 351,361 ---- goto finally; } if (PyDict_SetItem(self->info, key, list)) { + Py_DECREF(list); err = ERR_EXCEPTION; goto finally; } + Py_DECREF(list); } if (PyList_Append(list, value)) err = ERR_EXCEPTION; *************** *** 523,528 **** --- 525,531 ---- fclose(self->logfp); self->logfp = NULL; } + Py_XDECREF(self->info); PyObject_Del(self); } *************** *** 799,809 **** PyObject *name = PyDict_GetItem(dict, obj); if (name == NULL) { if (pack_define_func(self, fileno, fcode->co_firstlineno, ! PyString_AS_STRING(fcode->co_name)) < 0) return -1; ! if (PyDict_SetItem(dict, obj, fcode->co_name)) return -1; } } return fileno; } --- 802,817 ---- PyObject *name = PyDict_GetItem(dict, obj); if (name == NULL) { if (pack_define_func(self, fileno, fcode->co_firstlineno, ! PyString_AS_STRING(fcode->co_name)) < 0) { ! Py_DECREF(obj); return -1; ! } ! if (PyDict_SetItem(dict, obj, fcode->co_name)) { ! Py_DECREF(obj); return -1; + } } + Py_DECREF(obj); } return fileno; } Index: Modules/gcmodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/gcmodule.c,v retrieving revision 2.72 diff -c -r2.72 gcmodule.c *** Modules/gcmodule.c 4 Sep 2003 11:59:50 -0000 2.72 --- Modules/gcmodule.c 25 Sep 2003 10:18:56 -0000 *************** *** 54,60 **** 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; --- 54,60 ---- static int collecting = 0; /* list of uncollectable objects */ ! PyObject *_PyGC_garbage = NULL; /* Python string to use if unhandled exception occurs */ static PyObject *gc_str = NULL; *************** *** 474,489 **** { PyGC_Head *gc = finalizers->gc.gc_next; ! if (garbage == NULL) { ! garbage = PyList_New(0); ! if (garbage == NULL) Py_FatalError("gc couldn't create gc.garbage list"); } for (; gc != finalizers; gc = gc->gc.gc_next) { PyObject *op = FROM_GC(gc); if ((debug & DEBUG_SAVEALL) || has_finalizer(op)) { ! if (PyList_Append(garbage, op) < 0) return -1; } } --- 474,489 ---- { PyGC_Head *gc = finalizers->gc.gc_next; ! if (_PyGC_garbage == NULL) { ! _PyGC_garbage = PyList_New(0); ! if (_PyGC_garbage == NULL) Py_FatalError("gc couldn't create gc.garbage list"); } for (; gc != finalizers; gc = gc->gc.gc_next) { PyObject *op = FROM_GC(gc); if ((debug & DEBUG_SAVEALL) || has_finalizer(op)) { ! if (PyList_Append(_PyGC_garbage, op) < 0) return -1; } } *************** *** 507,513 **** assert(IS_TENTATIVELY_UNREACHABLE(op)); if (debug & DEBUG_SAVEALL) { ! PyList_Append(garbage, op); } else { if ((clear = op->ob_type->tp_clear) != NULL) { --- 507,513 ---- assert(IS_TENTATIVELY_UNREACHABLE(op)); if (debug & DEBUG_SAVEALL) { ! PyList_Append(_PyGC_garbage, op); } else { if ((clear = op->ob_type->tp_clear) != NULL) { *************** *** 951,957 **** {NULL, NULL} /* Sentinel */ }; ! PyMODINIT_FUNC initgc(void) { PyObject *m; --- 951,957 ---- {NULL, NULL} /* Sentinel */ }; ! void initgc(void) { PyObject *m; *************** *** 962,973 **** NULL, PYTHON_API_VERSION); ! if (garbage == NULL) { ! garbage = PyList_New(0); ! if (garbage == NULL) return; } ! if (PyModule_AddObject(m, "garbage", garbage) < 0) return; #define ADD_INT(NAME) if (PyModule_AddIntConstant(m, #NAME, NAME) < 0) return ADD_INT(DEBUG_STATS); --- 962,973 ---- NULL, PYTHON_API_VERSION); ! if (_PyGC_garbage == NULL) { ! _PyGC_garbage = PyList_New(0); ! if (_PyGC_garbage == NULL) return; } ! if (PyModule_AddObject(m, "garbage", _PyGC_garbage) < 0) return; #define ADD_INT(NAME) if (PyModule_AddIntConstant(m, #NAME, NAME) < 0) return ADD_INT(DEBUG_STATS); Index: Python/import.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/import.c,v retrieving revision 2.223 diff -c -r2.223 import.c *** Python/import.c 4 Sep 2003 11:59:50 -0000 2.223 --- Python/import.c 25 Sep 2003 10:18:56 -0000 *************** *** 353,359 **** void PyImport_Cleanup(void) { ! int pos, ndone; char *name; PyObject *key, *value, *dict; PyInterpreterState *interp = PyThreadState_Get()->interp; --- 353,359 ---- void PyImport_Cleanup(void) { ! int pos; char *name; PyObject *key, *value, *dict; PyInterpreterState *interp = PyThreadState_Get()->interp; *************** *** 395,409 **** } } - /* First, delete __main__ */ - value = PyDict_GetItemString(modules, "__main__"); - if (value != NULL && PyModule_Check(value)) { - if (Py_VerboseFlag) - PySys_WriteStderr("# cleanup __main__\n"); - _PyModule_Clear(value); - PyDict_SetItemString(modules, "__main__", Py_None); - } - /* The special treatment of __builtin__ here is because even when it's not referenced as a module, its dictionary is referenced by almost every module's __builtins__. Since --- 395,400 ---- *************** *** 412,448 **** module last. Likewise, we don't delete sys until the very end because it is implicitly referenced (e.g. by print). ! Also note that we 'delete' modules by replacing their entry ! in the modules dict with None, rather than really deleting ! them; this avoids a rehash of the modules dictionary and ! also marks them as "non existent" so they won't be ! re-imported. */ ! ! /* Next, repeatedly delete modules with a reference count of ! one (skipping __builtin__ and sys) and delete them */ ! do { ! ndone = 0; ! pos = 0; ! while (PyDict_Next(modules, &pos, &key, &value)) { ! if (value->ob_refcnt != 1) ! continue; ! if (PyString_Check(key) && PyModule_Check(value)) { ! name = PyString_AS_STRING(key); ! if (strcmp(name, "__builtin__") == 0) ! continue; ! if (strcmp(name, "sys") == 0) ! continue; ! if (Py_VerboseFlag) ! PySys_WriteStderr( ! "# cleanup[1] %s\n", name); ! _PyModule_Clear(value); ! PyDict_SetItem(modules, key, Py_None); ! ndone++; ! } ! } ! } while (ndone > 0); ! /* Next, delete all modules (still skipping __builtin__ and sys) */ pos = 0; while (PyDict_Next(modules, &pos, &key, &value)) { if (PyString_Check(key) && PyModule_Check(value)) { --- 403,416 ---- module last. Likewise, we don't delete sys until the very end because it is implicitly referenced (e.g. by print). ! We delete modules from the modules dict. Most of them will ! be deallocated, clearing their globals in the process. ! Also note that we 'delete' modules by replacing their entry ! in the modules dict with a (proxy) weak reference object, ! rather than really deleting them; this avoids a rehash of ! the modules dictionary and is also a convenient way to ! keep track of which modules the GC could not release. */ pos = 0; while (PyDict_Next(modules, &pos, &key, &value)) { if (PyString_Check(key) && PyModule_Check(value)) { *************** *** 452,461 **** if (strcmp(name, "sys") == 0) continue; if (Py_VerboseFlag) ! PySys_WriteStderr("# cleanup[2] %s\n", name); ! _PyModule_Clear(value); ! PyDict_SetItem(modules, key, Py_None); } } /* Next, delete sys and __builtin__ (in that order) */ --- 420,464 ---- if (strcmp(name, "sys") == 0) continue; if (Py_VerboseFlag) ! PySys_WriteStderr("# unlink %s\n", name); ! value = PyWeakref_NewProxy(value, NULL); ! if (value == NULL) { ! PyErr_Clear(); ! value = Py_None; ! Py_INCREF(value); ! } ! PyDict_SetItem(modules, key, value); ! Py_DECREF(value); } + } + + /* Run the GC. This will free e.g. cycles of modules that + import each other. */ + PyGC_Collect(); + + /* Some uncleared modules could remain. This occurs if there + are objects with __del__ methods in a cycle of modules. */ + pos = 0; + while (PyDict_Next(modules, &pos, &key, &value)) { + if (PyString_Check(key) && PyWeakref_Check(value) && + PyModule_Check(PyWeakref_GET_OBJECT(value))) { + if (Py_VerboseFlag) { + name = PyString_AS_STRING(key); + PySys_WriteStderr("# cleanup %s\n", name); + } + _PyModule_Clear(PyWeakref_GET_OBJECT(value)); + } + } + + /* The objects with __del__ methods that were previously added to + gc.garbage could now be freeable, e.g. if they were part of + a cycle involving one of the now-cleared module globals. + Objects that genuinely belong to gc.garbage will just get + re-inserted into the list by the following PyGC_Collect(). */ + if (_PyGC_garbage != NULL) { + (void) PyList_SetSlice(_PyGC_garbage, 0, + PyList_Size(_PyGC_garbage), 0); + PyGC_Collect(); } /* Next, delete sys and __builtin__ (in that order) */ Index: Python/pythonrun.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/pythonrun.c,v retrieving revision 2.197 diff -c -r2.197 pythonrun.c *** Python/pythonrun.c 11 Aug 2003 12:20:24 -0000 2.197 --- Python/pythonrun.c 25 Sep 2003 10:18:56 -0000 *************** *** 337,344 **** /* Destroy all modules */ PyImport_Cleanup(); ! /* Collect final garbage. This disposes of cycles created by ! new-style class definitions, for example. */ PyGC_Collect(); /* Destroy the database used by _PyImport_{Fixup,Find}Extension */ --- 337,344 ---- /* Destroy all modules */ PyImport_Cleanup(); ! /* Collect final garbage. This disposes of cycles dependent on ! the __builtin__ and sys modules. */ PyGC_Collect(); /* Destroy the database used by _PyImport_{Fixup,Find}Extension */