[Python-checkins] python/dist/src/Modules gcmodule.c,2.62,2.63
tim_one@users.sourceforge.net
tim_one@users.sourceforge.net
Sat, 05 Apr 2003 16:11:41 -0800
Update of /cvsroot/python/python/dist/src/Modules
In directory sc8-pr-cvs1:/tmp/cvs-serv11222/python/Modules
Modified Files:
gcmodule.c
Log Message:
Reworked move_finalizer_reachable() to create two distinct lists:
externally unreachable objects with finalizers, and externally unreachable
objects without finalizers reachable from such objects. This allows us
to call has_finalizer() at most once per object, and so limit the pain of
nasty getattr hooks. This fixes the failing "boom 2" example Jeremy
posted (a non-printing variant of which is now part of test_gc), via never
triggering the nasty part of its __getattr__ method.
Index: gcmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/gcmodule.c,v
retrieving revision 2.62
retrieving revision 2.63
diff -C2 -d -r2.62 -r2.63
*** gcmodule.c 5 Apr 2003 18:40:50 -0000 2.62
--- gcmodule.c 6 Apr 2003 00:11:39 -0000 2.63
***************
*** 52,62 ****
/* true if we are currently running the collector */
! static int collecting;
/* list of uncollectable objects */
! static PyObject *garbage;
/* Python string to use if unhandled exception occurs */
! static PyObject *gc_str;
/* Python string used to look for __del__ attribute. */
--- 52,62 ----
/* true if we are currently running the collector */
! 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;
/* Python string used to look for __del__ attribute. */
***************
*** 413,429 ****
/* Move objects that are reachable from finalizers, from the unreachable set
! * into the finalizers set.
*/
static void
! move_finalizer_reachable(PyGC_Head *finalizers)
{
traverseproc traverse;
PyGC_Head *gc = finalizers->gc.gc_next;
! for (; gc != finalizers; gc=gc->gc.gc_next) {
! /* careful, finalizers list is growing here */
traverse = FROM_GC(gc)->ob_type->tp_traverse;
(void) traverse(FROM_GC(gc),
! (visitproc)visit_move,
! (void *)finalizers);
}
}
--- 413,430 ----
/* Move objects that are reachable from finalizers, from the unreachable set
! * into the reachable_from_finalizers set.
*/
static void
! move_finalizer_reachable(PyGC_Head *finalizers,
! PyGC_Head *reachable_from_finalizers)
{
traverseproc traverse;
PyGC_Head *gc = finalizers->gc.gc_next;
! for (; gc != finalizers; gc = gc->gc.gc_next) {
! /* Note that the finalizers list may grow during this. */
traverse = FROM_GC(gc)->ob_type->tp_traverse;
(void) traverse(FROM_GC(gc),
! (visitproc)visit_move,
! (void *)reachable_from_finalizers);
}
}
***************
*** 455,471 ****
}
! /* Handle uncollectable garbage (cycles with finalizers). */
static void
! handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
{
PyGC_Head *gc;
if (garbage == NULL) {
garbage = PyList_New(0);
}
! 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
--- 456,478 ----
}
! /* Handle uncollectable garbage (cycles with finalizers, and stuff reachable
! * only from such cycles).
! */
static void
! handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old, int hasfinalizer)
{
PyGC_Head *gc;
if (garbage == NULL) {
garbage = PyList_New(0);
+ if (garbage == NULL)
+ Py_FatalError("gc couldn't create gc.garbage list");
}
! for (gc = finalizers->gc.gc_next;
! gc != finalizers;
! gc = finalizers->gc.gc_next) {
PyObject *op = FROM_GC(gc);
!
! assert(IS_REACHABLE(op));
! if ((debug & DEBUG_SAVEALL) || hasfinalizer) {
/* If SAVEALL is not set then just append objects with
* finalizers to the list of garbage. All objects in
***************
*** 475,480 ****
PyList_Append(garbage, op);
}
- /* object is now reachable again */
- assert(IS_REACHABLE(op));
gc_list_remove(gc);
gc_list_append(gc, old);
--- 482,485 ----
***************
*** 528,531 ****
--- 533,537 ----
PyGC_Head collectable;
PyGC_Head finalizers;
+ PyGC_Head reachable_from_finalizers;
PyGC_Head *gc;
***************
*** 590,603 ****
* instance objects with __del__ methods.
*
! * Move each object into the collectable set or the finalizers set.
! * Because we need to check for __del__ methods on instances of
! * classic classes, arbitrary Python code may get executed by
! * getattr hooks: that may resurrect or deallocate (via refcount
! * falling to 0) unreachable objects, so this is very delicate.
*/
gc_list_init(&collectable);
gc_list_init(&finalizers);
move_finalizers(&unreachable, &collectable, &finalizers);
! move_finalizer_reachable(&finalizers);
/* Collect statistics on collectable objects found and print
--- 596,620 ----
* instance objects with __del__ methods.
*
! * Move each unreachable object into the collectable set or the
! * finalizers set. Because we need to check for __del__ methods on
! * instances of classic classes, arbitrary Python code may get
! * executed by getattr hooks: that may resurrect or deallocate (via
! * refcount falling to 0) unreachable objects, so this is very
! * delicate.
*/
gc_list_init(&collectable);
gc_list_init(&finalizers);
move_finalizers(&unreachable, &collectable, &finalizers);
! /* finalizers contains the unreachable objects with a finalizer;
! * unreachable objects reachable only *from* those are also
! * uncollectable; we move those into a separate list
! * (reachable_from_finalizers) so we don't have to do the dangerous
! * has_finalizer() test again later.
! */
! gc_list_init(&reachable_from_finalizers);
! move_finalizer_reachable(&finalizers, &reachable_from_finalizers);
! /* And move everything only reachable from the reachable stuff. */
! move_finalizer_reachable(&reachable_from_finalizers,
! &reachable_from_finalizers);
/* Collect statistics on collectable objects found and print
***************
*** 611,626 ****
}
/* 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(&collectable, old);
/* Collect statistics on uncollectable objects found and print
* debugging information. */
! for (gc = finalizers.gc.gc_next; gc != &finalizers;
! gc = gc->gc.gc_next) {
n++;
! if (debug & DEBUG_UNCOLLECTABLE) {
debug_cycle("uncollectable", FROM_GC(gc));
- }
}
if (debug & DEBUG_STATS) {
--- 628,650 ----
}
/* 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 and/or reachable_from_finalizers to be freed */
delete_garbage(&collectable, old);
/* Collect statistics on uncollectable objects found and print
* debugging information. */
! for (gc = finalizers.gc.gc_next;
! gc != &finalizers;
! gc = gc->gc.gc_next) {
n++;
! if (debug & DEBUG_UNCOLLECTABLE)
! debug_cycle("uncollectable", FROM_GC(gc));
! }
! for (gc = reachable_from_finalizers.gc.gc_next;
! gc != &reachable_from_finalizers;
! gc = gc->gc.gc_next) {
! n++;
! if (debug & DEBUG_UNCOLLECTABLE)
debug_cycle("uncollectable", FROM_GC(gc));
}
if (debug & DEBUG_STATS) {
***************
*** 637,642 ****
/* 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);
if (PyErr_Occurred()) {
--- 661,668 ----
/* 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, 1);
! handle_finalizers(&reachable_from_finalizers, old, 0);
if (PyErr_Occurred()) {