Index: Modules/gcmodule.c =================================================================== --- Modules/gcmodule.c (revision 78226) +++ Modules/gcmodule.c (working copy) @@ -32,6 +32,8 @@ /* Get the object given the GC head */ #define FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1)) +static void debug_cycle(char *msg, PyObject *op); + /*** Global GC state ***/ struct gc_generation { @@ -408,8 +410,12 @@ { if (PyGen_CheckExact(op)) return PyGen_NeedsFinalizing((PyGenObject *)op); - else + else { + if (op->ob_type->tp_del != NULL) { + + } return op->ob_type->tp_del != NULL; + } } /* Move the objects in unreachable with __del__ methods into `finalizers`. @@ -432,8 +438,11 @@ next = gc->gc.gc_next; if (has_finalizer(op)) { + debug_cycle("move_to_finalizer", op); gc_list_move(gc, finalizers); gc->gc.gc_refs = GC_REACHABLE; + } else { + debug_cycle("unreachable", op); } } } @@ -445,6 +454,8 @@ if (PyObject_IS_GC(op)) { if (IS_TENTATIVELY_UNREACHABLE(op)) { PyGC_Head *gc = AS_GC(op); + debug_cycle("move_finalizer_reachable_callback", + op); gc_list_move(gc, tolist); gc->gc.gc_refs = GC_REACHABLE; } @@ -452,8 +463,9 @@ return 0; } -/* Move objects that are reachable from finalizers, from the unreachable set - * into finalizers set. +/* Move objects that are reachable from finalizers, from the + * unreachable set into finalizers set. Objects reachable from + * finalizers are marked as reachable. */ static void move_finalizer_reachable(PyGC_Head *finalizers) @@ -463,10 +475,15 @@ for (; gc != finalizers; gc = gc->gc.gc_next) { /* Note that the finalizers list may grow during this. */ traverse = Py_TYPE(FROM_GC(gc))->tp_traverse; + debug_cycle("traverse_finalizer", FROM_GC(gc)); (void) traverse(FROM_GC(gc), (visitproc)visit_move, (void *)finalizers); } + gc = finalizers->gc.gc_next; + for (; gc != finalizers; gc = gc->gc.gc_next) { + debug_cycle("final_finalizers", FROM_GC(gc)); + } } /* Clear all weakrefs to unreachable objects, and if such a weakref has a @@ -638,7 +655,7 @@ * Returns 0 if all OK, <0 on error (out of memory to grow the garbage list). * The finalizers list is made empty on a successful return. */ -static int +static void handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old) { PyGC_Head *gc = finalizers->gc.gc_next; @@ -652,22 +669,22 @@ PyObject *op = FROM_GC(gc); if ((debug & DEBUG_SAVEALL) || has_finalizer(op)) { + debug_cycle("garbage", op); if (PyList_Append(garbage, op) < 0) - return -1; + return; } } - gc_list_merge(finalizers, old); - return 0; } /* 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 +static int delete_garbage(PyGC_Head *collectable, PyGC_Head *old) { + int cleared = 0; inquiry clear; while (!gc_list_is_empty(collectable)) { @@ -676,21 +693,26 @@ assert(IS_TENTATIVELY_UNREACHABLE(op)); if (debug & DEBUG_SAVEALL) { + debug_cycle("SAVEALL garbage", op); PyList_Append(garbage, op); } else { if ((clear = Py_TYPE(op)->tp_clear) != NULL) { Py_INCREF(op); + debug_cycle("clear", op); clear(op); Py_DECREF(op); + cleared++; } } if (collectable->gc.gc_next == gc) { + debug_cycle("still alive", FROM_GC(gc)); /* object is still alive, move it, it may die later */ gc_list_move(gc, old); gc->gc.gc_refs = GC_REACHABLE; } } + return cleared; } /* Clear all free lists @@ -727,56 +749,22 @@ return result; } -/* This is the main function. Read this to understand how the - * collection process works. */ + +/* Find unreachable objects and clear them. + Move reachable objects to the next older generation. + Return a list of unreachable objects with or reachable from finalizers. + Return number of objects collected. +*/ static Py_ssize_t -collect(int generation) +collect_and_delete_garbage(PyGC_Head *young, /* the generation we are examining */ + PyGC_Head *old, /* next older generation */ + PyGC_Head *finalizers, /* objects with, & reachable from, __del__ */ + Py_ssize_t *deleted) /* number of objects cleared */ { - int i; + PyGC_Head *gc; + PyGC_Head unreachable; /* non-problematic unreachable trash */ Py_ssize_t m = 0; /* # objects collected */ - Py_ssize_t n = 0; /* # unreachable objects that couldn't be collected */ - PyGC_Head *young; /* the generation we are examining */ - PyGC_Head *old; /* next older generation */ - PyGC_Head unreachable; /* non-problematic unreachable trash */ - PyGC_Head finalizers; /* objects with, & reachable from, __del__ */ - PyGC_Head *gc; - double t1 = 0.0; - if (delstr == NULL) { - delstr = PyUnicode_InternFromString("__del__"); - if (delstr == NULL) - Py_FatalError("gc couldn't allocate \"__del__\""); - } - - if (debug & DEBUG_STATS) { - t1 = get_time(); - PySys_WriteStderr("gc: collecting generation %d...\n", - generation); - PySys_WriteStderr("gc: objects in each generation:"); - for (i = 0; i < NUM_GENERATIONS; i++) - PySys_WriteStderr(" %" PY_FORMAT_SIZE_T "d", - gc_list_size(GEN_HEAD(i))); - PySys_WriteStderr("\n"); - } - - /* update collection and allocation counters */ - if (generation+1 < NUM_GENERATIONS) - generations[generation+1].count += 1; - for (i = 0; i <= generation; i++) - generations[i].count = 0; - - /* merge younger generations with one we are currently collecting */ - for (i = 0; i < generation; i++) { - gc_list_merge(GEN_HEAD(i), GEN_HEAD(generation)); - } - - /* handy references */ - young = GEN_HEAD(generation); - if (generation < NUM_GENERATIONS-1) - old = GEN_HEAD(generation+1); - else - old = young; - /* Using ob_refcnt and gc_refs, calculate which objects in the * container set are reachable from outside the set (i.e., have a * refcount greater than 0 when all the references within the @@ -805,13 +793,12 @@ * can also call arbitrary Python code but they will be dealt with by * handle_weakrefs(). */ - gc_list_init(&finalizers); - move_finalizers(&unreachable, &finalizers); + move_finalizers(&unreachable, finalizers); /* finalizers contains the unreachable objects with a finalizer; * unreachable objects reachable *from* those are also uncollectable, * and we move those into the finalizers list too. */ - move_finalizer_reachable(&finalizers); + move_finalizer_reachable(finalizers); /* Collect statistics on collectable objects found and print * debugging information. @@ -827,12 +814,68 @@ /* Clear weakrefs and invoke callbacks as necessary. */ m += handle_weakrefs(&unreachable, old); - /* Call tp_clear on objects in the unreachable set. This will cause - * the reference cycles to be broken. It may also cause some objects - * in finalizers to be freed. + /* Call tp_clear on objects in the unreachable set. This will + * cause the reference cycles to be broken. It may also cause + * some objects in finalizers to be freed or to become reachable. */ - delete_garbage(&unreachable, old); + *deleted = delete_garbage(&unreachable, old); + return m; +} + +/* This is the main function. Read this to understand how the + * collection process works. */ +static Py_ssize_t +collect(int generation) +{ + int i; + Py_ssize_t n; + Py_ssize_t m; + Py_ssize_t deleted; + PyGC_Head *gc; + PyGC_Head *young; /* the generation we are examining */ + PyGC_Head *old; /* next older generation */ + PyGC_Head finalizers; + double t1 = 0.0; + + if (delstr == NULL) { + delstr = PyUnicode_InternFromString("__del__"); + if (delstr == NULL) + Py_FatalError("gc couldn't allocate \"__del__\""); + } + + if (debug & DEBUG_STATS) { + t1 = get_time(); + PySys_WriteStderr("gc: collecting generation %d...\n", + generation); + PySys_WriteStderr("gc: objects in each generation:"); + for (i = 0; i < NUM_GENERATIONS; i++) + PySys_WriteStderr(" %" PY_FORMAT_SIZE_T "d", + gc_list_size(GEN_HEAD(i))); + PySys_WriteStderr("\n"); + } + + /* update collection and allocation counters */ + if (generation+1 < NUM_GENERATIONS) + generations[generation+1].count += 1; + for (i = 0; i <= generation; i++) + generations[i].count = 0; + + /* merge younger generations with one we are currently collecting */ + for (i = 0; i < generation; i++) { + gc_list_merge(GEN_HEAD(i), GEN_HEAD(generation)); + } + + /* handy references */ + young = GEN_HEAD(generation); + if (generation < NUM_GENERATIONS-1) + old = GEN_HEAD(generation+1); + else + old = young; + + gc_list_init(&finalizers); + m = collect_and_delete_garbage(young, old, &finalizers, &deleted); + /* Collect statistics on uncollectable objects found and print * debugging information. */ for (gc = finalizers.gc.gc_next; @@ -842,28 +885,40 @@ if (debug & DEBUG_UNCOLLECTABLE) debug_cycle("uncollectable", FROM_GC(gc)); } + + /* 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. + */ + if (n) { + if (deleted) { + Py_ssize_t o; + fprintf(stderr, "Recursive collection about to run " + "with %ld objects\n", n); + o = collect_and_delete_garbage(&finalizers, old, &finalizers, &deleted); + fprintf(stderr, "Recursive collection %d\n", o); + n += o; + } + } + if (n && !deleted) { + handle_finalizers(&finalizers, old); + } if (debug & DEBUG_STATS) { double t2 = get_time(); - if (m == 0 && n == 0) + if (n == 0 && n == 0) PySys_WriteStderr("gc: done"); else PySys_WriteStderr( "gc: done, " "%" PY_FORMAT_SIZE_T "d unreachable, " "%" PY_FORMAT_SIZE_T "d uncollectable", - n+m, n); + n, m); if (t1 && t2) { PySys_WriteStderr(", %.4fs elapsed", t2-t1); } PySys_WriteStderr(".\n"); } - /* 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. - */ - (void)handle_finalizers(&finalizers, old); - /* Clear free list only during the collection of the higest * generation */ if (generation == NUM_GENERATIONS-1) { @@ -876,7 +931,8 @@ PyErr_WriteUnraisable(gc_str); Py_FatalError("unexpected exception during garbage collection"); } - return n+m; + + return n + m; } static Py_ssize_t