Fix Tim's boom example.

move_finalizers() moves every object from unreachable to collectable
or finalizers, unless the object is deallocated first.
This commit is contained in:
Jeremy Hylton 2003-04-04 19:59:06 +00:00
parent 76ffb1918d
commit ce136e985a
1 changed files with 54 additions and 20 deletions

View File

@ -59,6 +59,9 @@ static PyObject *garbage;
/* Python string to use if unhandled exception occurs */
static PyObject *gc_str;
/* Python string used to looked for __del__ attribute. */
static PyObject *delstr;
/* set for debugging information */
#define DEBUG_STATS (1<<0) /* print collection statistics */
#define DEBUG_COLLECTABLE (1<<1) /* print collectable objects */
@ -340,31 +343,52 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
static int
has_finalizer(PyObject *op)
{
static PyObject *delstr = NULL;
if (delstr == NULL) {
delstr = PyString_InternFromString("__del__");
if (delstr == NULL)
Py_FatalError("PyGC: can't initialize __del__ string");
}
return PyInstance_Check(op) ? PyObject_HasAttr(op, delstr) :
PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE) ?
op->ob_type->tp_del != NULL : 0;
}
/* Move all objects with finalizers (instances with __del__) */
/* Move all objects out of unreachable and into collectable or finalizers.
*/
static void
move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
move_finalizers(PyGC_Head *unreachable, PyGC_Head *collectable,
PyGC_Head *finalizers)
{
PyGC_Head *next;
PyGC_Head *gc = unreachable->gc.gc_next;
for (; gc != unreachable; gc=next) {
while (!gc_list_is_empty(unreachable)) {
PyGC_Head *gc = unreachable->gc.gc_next;
PyObject *op = FROM_GC(gc);
next = gc->gc.gc_next;
if (has_finalizer(op)) {
int finalizer;
if (PyInstance_Check(op)) {
/* The HasAttr() check may run enough Python
code to deallocate the object or make it
reachable again. INCREF the object before
calling HasAttr() to guard against the client
code deallocating the object.
*/
Py_INCREF(op);
finalizer = PyObject_HasAttr(op, delstr);
if (op->ob_refcnt == 1) {
/* The object will be deallocated.
Nothing left to do.
*/
Py_DECREF(op);
continue;
}
Py_DECREF(op);
}
else
finalizer = has_finalizer(op);
if (finalizer) {
gc_list_remove(gc);
gc_list_append(gc, finalizers);
gc->gc.gc_refs = GC_REACHABLE;
}
else {
gc_list_remove(gc);
gc_list_append(gc, collectable);
/* XXX change gc_refs? */
}
}
}
@ -437,6 +461,7 @@ handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
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
@ -457,12 +482,12 @@ handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
* objects may be freed. It is possible I screwed something up here.
*/
static void
delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
delete_garbage(PyGC_Head *collectable, PyGC_Head *old)
{
inquiry clear;
while (!gc_list_is_empty(unreachable)) {
PyGC_Head *gc = unreachable->gc.gc_next;
while (!gc_list_is_empty(collectable)) {
PyGC_Head *gc = collectable->gc.gc_next;
PyObject *op = FROM_GC(gc);
assert(IS_TENTATIVELY_UNREACHABLE(op));
@ -476,7 +501,7 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
Py_DECREF(op);
}
}
if (unreachable->gc.gc_next == gc) {
if (collectable->gc.gc_next == gc) {
/* object is still alive, move it, it may die later */
gc_list_remove(gc);
gc_list_append(gc, old);
@ -496,6 +521,7 @@ collect(int generation)
PyGC_Head *young; /* the generation we are examining */
PyGC_Head *old; /* next older generation */
PyGC_Head unreachable;
PyGC_Head collectable;
PyGC_Head finalizers;
PyGC_Head *gc;
@ -552,14 +578,19 @@ collect(int generation)
* finalizers can't safely be deleted. Python programmers should take
* care not to create such things. For Python, finalizers means
* instance objects with __del__ methods.
*
* Move each object into the collectable set or the finalizers set.
* It's possible that a classic class with a getattr() hook will
* be revived or deallocated in this step.
*/
gc_list_init(&collectable);
gc_list_init(&finalizers);
move_finalizers(&unreachable, &finalizers);
move_finalizers(&unreachable, &collectable, &finalizers);
move_finalizer_reachable(&finalizers);
/* Collect statistics on collectable objects found and print
* debugging information. */
for (gc = unreachable.gc.gc_next; gc != &unreachable;
for (gc = collectable.gc.gc_next; gc != &collectable;
gc = gc->gc.gc_next) {
m++;
if (debug & DEBUG_COLLECTABLE) {
@ -569,7 +600,7 @@ collect(int generation)
/* 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);
delete_garbage(&collectable, old);
/* Collect statistics on uncollectable objects found and print
* debugging information. */
@ -938,6 +969,9 @@ initgc(void)
PyObject *m;
PyObject *d;
delstr = PyString_InternFromString("__del__");
if (!delstr)
return;
m = Py_InitModule4("gc",
GcMethods,
gc__doc__,