/* 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 delete_garbage(PyGC_Head *collectable, PyGC_Head *old) { inquiry clear; while (!gc_list_is_empty(collectable)) { PyGC_Head *gc = collectable->gc.gc_next; PyObject *op = FROM_GC(gc); assert(IS_TENTATIVELY_UNREACHABLE(op)); if (debug & DEBUG_SAVEALL) { PyList_Append(garbage, op); } else { if ((clear = op->ob_type->tp_clear) != NULL) { Py_INCREF(op); clear(op); Py_DECREF(op); } } if (collectable->gc.gc_next == gc) { /* object is still alive, move it, it may die later */ gc_list_move(gc, old); gc->gc.gc_refs = GC_REACHABLE; } } }
void gc_move_upwards( gbg_collector_t *gc, void *thing, unsigned color ){ if ( thing ){ gbg_node_t *node = thing; if ( color > node->status ){ gc_list_move( gc, node, color ); } } }
/* Move the unreachable objects from young to unreachable. After this, * all objects in young have gc_refs = GC_REACHABLE, and all objects in * unreachable have gc_refs = GC_TENTATIVELY_UNREACHABLE. All tracked * gc objects not in young or unreachable still have gc_refs = GC_REACHABLE. * All objects in young after this are directly or indirectly reachable * from outside the original young; and all objects in unreachable are * not. */ static void move_unreachable(PyGC_Head *young, PyGC_Head *unreachable) { PyGC_Head *gc = young->gc.gc_next; /* Invariants: all objects "to the left" of us in young have gc_refs * = GC_REACHABLE, and are indeed reachable (directly or indirectly) * from outside the young list as it was at entry. All other objects * from the original young "to the left" of us are in unreachable now, * and have gc_refs = GC_TENTATIVELY_UNREACHABLE. All objects to the * left of us in 'young' now have been scanned, and no objects here * or to the right have been scanned yet. */ while (gc != young) { PyGC_Head *next; if (gc->gc.gc_refs) { /* gc is definitely reachable from outside the * original 'young'. Mark it as such, and traverse * its pointers to find any other objects that may * be directly reachable from it. Note that the * call to tp_traverse may append objects to young, * so we have to wait until it returns to determine * the next object to visit. */ PyObject *op = FROM_GC(gc); traverseproc traverse = Py_TYPE(op)->tp_traverse; assert(gc->gc.gc_refs > 0); gc->gc.gc_refs = GC_REACHABLE; (void) traverse(op, (visitproc)visit_reachable, (void *)young); next = gc->gc.gc_next; if (PyTuple_CheckExact(op)) { _PyTuple_MaybeUntrack(op); } else if (PyDict_CheckExact(op)) { _PyDict_MaybeUntrack(op); } } else { /* This *may* be unreachable. To make progress, * assume it is. gc isn't directly reachable from * any object we've already traversed, but may be * reachable from an object we haven't gotten to yet. * visit_reachable will eventually move gc back into * young if that's so, and we'll see it again. */ next = gc->gc.gc_next; gc_list_move(gc, unreachable); gc->gc.gc_refs = GC_TENTATIVELY_UNREACHABLE; } gc = next; } }
/* A traversal callback for move_finalizer_reachable. */ static int visit_move(PyObject *op, PyGC_Head *tolist) { if (PyObject_IS_GC(op)) { if (IS_TENTATIVELY_UNREACHABLE(op)) { PyGC_Head *gc = AS_GC(op); gc_list_move(gc, tolist); gc->gc.gc_refs = GC_REACHABLE; } } return 0; }
/* A traversal callback for move_unreachable. */ static int visit_reachable(PyObject *op, PyGC_Head *reachable) { if (PyObject_IS_GC(op)) { PyGC_Head *gc = AS_GC(op); const int gc_refs = gc->gc.gc_refs; if (gc_refs == 0) { /* This is in move_unreachable's 'young' list, but * the traversal hasn't yet gotten to it. All * we need to do is tell move_unreachable that it's * reachable. */ gc->gc.gc_refs = 1; } else if (gc_refs == GC_TENTATIVELY_UNREACHABLE) { /* This had gc_refs = 0 when move_unreachable got * to it, but turns out it's reachable after all. * Move it back to move_unreachable's 'young' list, * and move_unreachable will eventually get to it * again. */ gc_list_move(gc, reachable); gc->gc.gc_refs = 1; } /* Else there's nothing to do. * If gc_refs > 0, it must be in move_unreachable's 'young' * list, and move_unreachable will eventually get to it. * If gc_refs == GC_REACHABLE, it's either in some other * generation so we don't care about it, or move_unreachable * already dealt with it. * If gc_refs == GC_UNTRACKED, it must be ignored. */ else { assert(gc_refs > 0 || gc_refs == GC_REACHABLE || gc_refs == GC_UNTRACKED); } } return 0; }
/* Move the objects in unreachable with __del__ methods into `finalizers`. * Objects moved into `finalizers` have gc_refs set to GC_REACHABLE; the * objects remaining in unreachable are left at GC_TENTATIVELY_UNREACHABLE. */ static void move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers) { PyGC_Head *gc; PyGC_Head *next; /* March over unreachable. Move objects with finalizers into * `finalizers`. */ for (gc = unreachable->gc.gc_next; gc != unreachable; gc = next) { PyObject *op = FROM_GC(gc); assert(IS_TENTATIVELY_UNREACHABLE(op)); next = gc->gc.gc_next; if (has_finalizer(op)) { gc_list_move(gc, finalizers); gc->gc.gc_refs = GC_REACHABLE; } } }
/* Clear all weakrefs to unreachable objects, and if such a weakref has a * callback, invoke it if necessary. Note that it's possible for such * weakrefs to be outside the unreachable set -- indeed, those are precisely * the weakrefs whose callbacks must be invoked. See gc_weakref.txt for * overview & some details. Some weakrefs with callbacks may be reclaimed * directly by this routine; the number reclaimed is the return value. Other * weakrefs with callbacks may be moved into the `old` generation. Objects * moved into `old` have gc_refs set to GC_REACHABLE; the objects remaining in * unreachable are left at GC_TENTATIVELY_UNREACHABLE. When this returns, * no object in `unreachable` is weakly referenced anymore. */ static int handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old) { PyGC_Head *gc; PyObject *op; /* generally FROM_GC(gc) */ PyWeakReference *wr; /* generally a cast of op */ PyGC_Head wrcb_to_call; /* weakrefs with callbacks to call */ PyGC_Head *next; int num_freed = 0; gc_list_init(&wrcb_to_call); /* Clear all weakrefs to the objects in unreachable. If such a weakref * also has a callback, move it into `wrcb_to_call` if the callback * needs to be invoked. Note that we cannot invoke any callbacks until * all weakrefs to unreachable objects are cleared, lest the callback * resurrect an unreachable object via a still-active weakref. We * make another pass over wrcb_to_call, invoking callbacks, after this * pass completes. */ for (gc = unreachable->gc.gc_next; gc != unreachable; gc = next) { PyWeakReference **wrlist; op = FROM_GC(gc); assert(IS_TENTATIVELY_UNREACHABLE(op)); next = gc->gc.gc_next; if (! PyType_SUPPORTS_WEAKREFS(op->ob_type)) continue; /* It supports weakrefs. Does it have any? */ wrlist = (PyWeakReference **) PyObject_GET_WEAKREFS_LISTPTR(op); /* `op` may have some weakrefs. March over the list, clear * all the weakrefs, and move the weakrefs with callbacks * that must be called into wrcb_to_call. */ for (wr = *wrlist; wr != NULL; wr = *wrlist) { PyGC_Head *wrasgc; /* AS_GC(wr) */ /* _PyWeakref_ClearRef clears the weakref but leaves * the callback pointer intact. Obscure: it also * changes *wrlist. */ assert(wr->wr_object == op); _PyWeakref_ClearRef(wr); assert(wr->wr_object == Py_None); if (wr->wr_callback == NULL) continue; /* no callback */ /* Headache time. `op` is going away, and is weakly referenced by * `wr`, which has a callback. Should the callback be invoked? If wr * is also trash, no: * * 1. There's no need to call it. The object and the weakref are * both going away, so it's legitimate to pretend the weakref is * going away first. The user has to ensure a weakref outlives its * referent if they want a guarantee that the wr callback will get * invoked. * * 2. It may be catastrophic to call it. If the callback is also in * cyclic trash (CT), then although the CT is unreachable from * outside the current generation, CT may be reachable from the * callback. Then the callback could resurrect insane objects. * * Since the callback is never needed and may be unsafe in this case, * wr is simply left in the unreachable set. Note that because we * already called _PyWeakref_ClearRef(wr), its callback will never * trigger. * * OTOH, if wr isn't part of CT, we should invoke the callback: the * weakref outlived the trash. Note that since wr isn't CT in this * case, its callback can't be CT either -- wr acted as an external * root to this generation, and therefore its callback did too. So * nothing in CT is reachable from the callback either, so it's hard * to imagine how calling it later could create a problem for us. wr * is moved to wrcb_to_call in this case. */ if (IS_TENTATIVELY_UNREACHABLE(wr)) continue; assert(IS_REACHABLE(wr)); /* Create a new reference so that wr can't go away * before we can process it again. */ Py_INCREF(wr); /* Move wr to wrcb_to_call, for the next pass. */ wrasgc = AS_GC(wr); assert(wrasgc != next); /* wrasgc is reachable, but next isn't, so they can't be the same */ gc_list_move(wrasgc, &wrcb_to_call); } } /* Invoke the callbacks we decided to honor. It's safe to invoke them * because they can't reference unreachable objects. */ while (! gc_list_is_empty(&wrcb_to_call)) { PyObject *temp; PyObject *callback; gc = wrcb_to_call.gc.gc_next; op = FROM_GC(gc); assert(IS_REACHABLE(op)); assert(PyWeakref_Check(op)); wr = (PyWeakReference *)op; callback = wr->wr_callback; assert(callback != NULL); /* copy-paste of weakrefobject.c's handle_callback() */ temp = PyObject_CallFunction(callback, "O", wr); if (temp == NULL) PyErr_WriteUnraisable(callback); else Py_DECREF(temp); /* Give up the reference we created in the first pass. When * op's refcount hits 0 (which it may or may not do right now), * op's tp_dealloc will decref op->wr_callback too. Note * that the refcount probably will hit 0 now, and because this * weakref was reachable to begin with, gc didn't already * add it to its count of freed objects. Example: a reachable * weak value dict maps some key to this reachable weakref. * The callback removes this key->weakref mapping from the * dict, leaving no other references to the weakref (excepting * ours). */ Py_DECREF(op); if (wrcb_to_call.gc.gc_next == gc) { /* object is still alive -- move it */ gc_list_move(gc, old); } else ++num_freed; } return num_freed; }