[Python-Dev] Combining the best of PEP 288 and PEP 325: generator exceptions and cleanup

Tim Peters tim.peters at gmail.com
Thu May 19 20:30:53 CEST 2005


[Phillip J. Eby]
> ...
> However, Tim's new post brings up a different issue: if the collector can't
> tell the difference between a cycle participant and an object that's only
> reachable from a cycle, then the mere existence of a generator __del__ will
> prevent the cycle collection of the entire traceback/frame system that
> includes a generator-iterator reference anywhere!  And that's a pretty
> serious problem.

It's not that simple <wink>.  If an object with a __del__ is not part
of a cycle, but is reachable only from trash cycles, that __del__ does
not inhibit garbage collection.  Like:

    A<->B -> C -> D -> E

where C, D and E have __del__, but A and B don't, and all are trash.

Relatively early on, gc "moves" C, D, and E into a special
"finalizers" list, and doesn't look at this list again until near the
end.  Then A.tp_clear() and B.tp_clear() are called in some order.  As
a *side effect* of calling B.tp_clear(), C's refcount falls to 0, and
Python's normal refcount-based reclamation (probably) recovers all of
C, D and E, and runs their __del__ methods.  Note that refcount-based
reclamation necessarily follows a DAG order:  E is still intact when
D.__del__ is called, and likewise D is still intact when C.__del__ is
called.  It's possible that C.__del__ will resurrect D and/or E, and
that D.__del__ will resurrect E.  In such cases, D and/or E's
refcounts don't fall to 0, and their __del__ methods won't be called
then.

Cyclic gc doesn't force any of that, though -- it's all a side effect
of the clear() in gcmodule.c's:

		if ((clear = op->ob_type->tp_clear) != NULL) {
			Py_INCREF(op);
			clear(op);
			Py_DECREF(op);

In turn, one of A and B get reclaimed as a side effect of the
Py_DECREF there -- it's one of the delights of gcmodule.c that if you
don't know the trick, you can stare at it for hours and never discover
where exactly it is anything gets released <0.9 wink>.  In fact, it
doesn't release anything directly -- "all it really does" now is break
reference cycles, so that Py_DECREF can do its end-of-life thing.


More information about the Python-Dev mailing list