]> granicus.if.org Git - python/commitdiff
Finished transitioning to using gc_refs to track gc objects' states.
authorTim Peters <tim.peters@gmail.com>
Tue, 2 Jul 2002 18:12:35 +0000 (18:12 +0000)
committerTim Peters <tim.peters@gmail.com>
Tue, 2 Jul 2002 18:12:35 +0000 (18:12 +0000)
This was mostly a matter of adding comments and light code rearrangement.
Upon untracking, gc_next is still set to NULL.  It's a cheap way to
provoke memory faults if calling code is insane.  It's also used in some
way by the trashcan mechanism.

Include/objimpl.h
Modules/gcmodule.c

index 28f3661574dbdd056e56ee7b3c11cd8956565da5..3a7488a3223f360d387d591b0772a223e7b277df 100644 (file)
@@ -251,7 +251,7 @@ extern DL_IMPORT(PyVarObject *) _PyObject_GC_Resize(PyVarObject *, int);
 /* GC information is stored BEFORE the object structure. */
 typedef union _gc_head {
        struct {
-               union _gc_head *gc_next; /* not NULL if object is tracked */
+               union _gc_head *gc_next;
                union _gc_head *gc_prev;
                int gc_refs;
        } gc;
@@ -272,7 +272,6 @@ extern PyGC_Head *_PyGC_generation0;
        PyGC_Head *g = _Py_AS_GC(o); \
        if (g->gc.gc_refs != _PyGC_REFS_UNTRACKED) \
                Py_FatalError("GC object already tracked"); \
-       assert(g->gc.gc_refs == _PyGC_REFS_UNTRACKED); \
        g->gc.gc_refs = _PyGC_REFS_REACHABLE; \
        g->gc.gc_next = _PyGC_generation0; \
        g->gc.gc_prev = _PyGC_generation0->gc.gc_prev; \
@@ -280,7 +279,10 @@ extern PyGC_Head *_PyGC_generation0;
        _PyGC_generation0->gc.gc_prev = g; \
     } while (0);
 
-/* Tell the GC to stop tracking this object. */
+/* Tell the GC to stop tracking this object.
+ * gc_next doesn't need to be set to NULL, but doing so is a good
+ * way to provoke memory errors if calling code is confused.
+ */
 #define _PyObject_GC_UNTRACK(o) do { \
        PyGC_Head *g = _Py_AS_GC(o); \
        assert(g->gc.gc_refs != _PyGC_REFS_UNTRACKED); \
index 7148e4c8e936b31605d74238a472258883904fe7..a623e8b0c34bd3d71e680e4aeb9ca9d5c64df5ac 100644 (file)
@@ -28,9 +28,6 @@
 /* Get the object given the GC head */
 #define FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1))
 
-/* True if an object is tracked by the GC */
-#define IS_TRACKED(o) ((AS_GC(o))->gc.gc_next != NULL)
-
 /*** Global GC state ***/
 
 struct gc_generation {
@@ -58,6 +55,12 @@ static int enabled = 1; /* automatic collection enabled? */
 /* 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;
+
 /* set for debugging information */
 #define DEBUG_STATS            (1<<0) /* print collection statistics */
 #define DEBUG_COLLECTABLE      (1<<1) /* print collectable objects */
@@ -72,30 +75,54 @@ static int collecting;
                                DEBUG_SAVEALL
 static int debug;
 
-/* When a collection begins, gc_refs is set to ob_refcnt for, and only for,
- * the objects in the generation being collected, called the "young"
- * generation at that point.  As collection proceeds, the gc_refs members
- * of young objects are set to GC_REACHABLE when it becomes known that they're
- * uncollectable, and to GC_TENTATIVELY_UNREACHABLE when the evidence
- * suggests they are collectable (this can't be known for certain until all
- * of the young generation is scanned).
- */
-
-/* Special gc_refs values. */
+/*--------------------------------------------------------------------------
+gc_refs values.
+
+Between collections, every gc'ed object has one of two gc_refs values:
+
+GC_UNTRACKED
+    The initial state; objects returned by PyObject_GC_Malloc are in this
+    state.  The object doesn't live in any generation list, and its
+    tp_traverse slot must not be called.
+
+GC_REACHABLE
+    The object lives in some generation list, and its tp_traverse is safe to
+    call.  An object transitions to GC_REACHABLE when PyObject_GC_Track
+    is called.
+
+During a collection, gc_refs can temporarily take on other states:
+
+>= 0
+    At the start of a collection, update_refs() copies the true refcount
+    to gc_refs, for each object in the generation being collected.
+    subtract_refs() then adjusts gc_refs so that it equals the number of
+    times an object is referenced directly from outside the generation
+    being collected.
+    gc_refs reamins >= 0 throughout these steps.
+
+GC_TENTATIVELY_UNREACHABLE
+    move_unreachable() then moves objects not reachable (whether directly or
+    indirectly) from outside the generation into an "unreachable" set.
+    Objects that are found to be reachable have gc_refs set to GC_REACHABLE
+    again.  Objects that are found to be unreachable have gc_refs set to
+    GC_TENTATIVELY_UNREACHABLE.  It's "tentatively" because the pass doing
+    this can't be sure until it ends, and GC_TENTATIVELY_UNREACHABLE may
+    transition back to GC_REACHABLE.
+
+    Only objects with GC_TENTATIVELY_UNREACHABLE still set are candidates
+    for collection.  If it's decided not to collect such an object (e.g.,
+    it has a __del__ method), its gc_refs is restored to GC_REACHABLE again.
+----------------------------------------------------------------------------
+*/
 #define GC_UNTRACKED                   _PyGC_REFS_UNTRACKED
 #define GC_REACHABLE                   _PyGC_REFS_REACHABLE
 #define GC_TENTATIVELY_UNREACHABLE     _PyGC_REFS_TENTATIVELY_UNREACHABLE
 
+#define IS_TRACKED(o) ((AS_GC(o))->gc.gc_refs != GC_UNTRACKED)
 #define IS_REACHABLE(o) ((AS_GC(o))->gc.gc_refs == GC_REACHABLE)
 #define IS_TENTATIVELY_UNREACHABLE(o) ( \
        (AS_GC(o))->gc.gc_refs == GC_TENTATIVELY_UNREACHABLE)
 
-/* list of uncollectable objects */
-static PyObject *garbage;
-
-/* Python string to use if unhandled exception occurs */
-static PyObject *gc_str;
-
 /*** list functions ***/
 
 static void
@@ -253,7 +280,7 @@ visit_reachable(PyObject *op, PyGC_Head *reachable)
                 * 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 deat with it.
+                * already dealt with it.
                 * If gc_refs == GC_UNTRACKED, it must be ignored.
                 */
                 else {
@@ -290,7 +317,25 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
        while (gc != young) {
                PyGC_Head *next;
 
-               if (gc->gc.gc_refs == 0) {
+               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 = op->ob_type->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;
+               }
+               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
@@ -303,23 +348,6 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
                        gc_list_append(gc, unreachable);
                        gc->gc.gc_refs = GC_TENTATIVELY_UNREACHABLE;
                }
-               else {
-                       /* 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 = op->ob_type->tp_traverse;
-                       gc->gc.gc_refs = GC_REACHABLE;
-                       (void) traverse(op,
-                                       (visitproc)visit_reachable,
-                                       (void *)young);
-                       next = gc->gc.gc_next;
-               }
                gc = next;
        }
 }
@@ -974,7 +1002,6 @@ _PyObject_GC_Malloc(size_t basicsize)
        PyGC_Head *g = PyObject_MALLOC(sizeof(PyGC_Head) + basicsize);
        if (g == NULL)
                return PyErr_NoMemory();
-       g->gc.gc_next = NULL;
        g->gc.gc_refs = GC_UNTRACKED;
        generations[0].count++; /* number of allocated GC objects */
        if (generations[0].count > generations[0].threshold &&