gc list function cleanup.
Introduced gc_list_move(), which captures the common gc_list_remove() +
gc_list_append() sequence. In fact, no uses of gc_list_append() remained
(they were all in a gc_list_move() sequence), so commented that one out.
gc_list_merge(): assert that `from` != `to`; that was an implicit
precondition, now verified in a debug build.
Others: added comments about their purpose.
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 710f0ed..4bfdea6 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -139,6 +139,9 @@
return (list->gc.gc_next == list);
}
+#if 0
+/* This became unused after gc_list_move() was introduced. */
+/* Append `node` to `list`. */
static void
gc_list_append(PyGC_Head *node, PyGC_Head *list)
{
@@ -147,7 +150,9 @@
node->gc.gc_prev->gc.gc_next = node;
list->gc.gc_prev = node;
}
+#endif
+/* Remove `node` from the gc list it's currently in. */
static void
gc_list_remove(PyGC_Head *node)
{
@@ -156,11 +161,29 @@
node->gc.gc_next = NULL; /* object is not currently tracked */
}
-/* append a list onto another list, from becomes an empty list */
+/* Move `node` from the gc list it's currently in (which is not explicitly
+ * named here) to the end of `list`. This is semantically the same as
+ * gc_list_remove(node) followed by gc_list_append(node, list).
+ */
+static void
+gc_list_move(PyGC_Head *node, PyGC_Head *list)
+{
+ PyGC_Head *current_prev = node->gc.gc_prev;
+ PyGC_Head *current_next = node->gc.gc_next;
+ PyGC_Head *new_prev = list->gc.gc_prev;
+ current_prev->gc.gc_next = current_next;
+ current_next->gc.gc_prev = current_prev;
+ node->gc.gc_next = list;
+ node->gc.gc_prev = new_prev;
+ new_prev->gc.gc_next = list->gc.gc_prev = node;
+}
+
+/* append list `from` onto list `to`; `from` becomes an empty list */
static void
gc_list_merge(PyGC_Head *from, PyGC_Head *to)
{
PyGC_Head *tail;
+ assert(from != to);
if (!gc_list_is_empty(from)) {
tail = to->gc.gc_prev;
tail->gc.gc_next = from->gc.gc_next;
@@ -295,8 +318,7 @@
* and move_unreachable will eventually get to it
* again.
*/
- gc_list_remove(gc);
- gc_list_append(gc, reachable);
+ gc_list_move(gc, reachable);
gc->gc.gc_refs = 1;
}
/* Else there's nothing to do.
@@ -368,8 +390,7 @@
* young if that's so, and we'll see it again.
*/
next = gc->gc.gc_next;
- gc_list_remove(gc);
- gc_list_append(gc, unreachable);
+ gc_list_move(gc, unreachable);
gc->gc.gc_refs = GC_TENTATIVELY_UNREACHABLE;
}
gc = next;
@@ -416,8 +437,7 @@
next = gc->gc.gc_next;
if (has_finalizer(op)) {
- gc_list_remove(gc);
- gc_list_append(gc, finalizers);
+ gc_list_move(gc, finalizers);
gc->gc.gc_refs = GC_REACHABLE;
}
}
@@ -430,8 +450,7 @@
if (PyObject_IS_GC(op)) {
if (IS_TENTATIVELY_UNREACHABLE(op)) {
PyGC_Head *gc = AS_GC(op);
- gc_list_remove(gc);
- gc_list_append(gc, tolist);
+ gc_list_move(gc, tolist);
gc->gc.gc_refs = GC_REACHABLE;
}
}
@@ -559,8 +578,7 @@
assert(wrasgc != next); /* wrasgc is reachable, but
next isn't, so they can't
be the same */
- gc_list_remove(wrasgc);
- gc_list_append(wrasgc, &wrcb_to_call);
+ gc_list_move(wrasgc, &wrcb_to_call);
}
}
@@ -600,8 +618,7 @@
Py_DECREF(op);
if (wrcb_to_call.gc.gc_next == gc) {
/* object is still alive -- move it */
- gc_list_remove(gc);
- gc_list_append(gc, old);
+ gc_list_move(gc, old);
}
else
++num_freed;
@@ -694,8 +711,7 @@
}
if (collectable->gc.gc_next == gc) {
/* object is still alive, move it, it may die later */
- gc_list_remove(gc);
- gc_list_append(gc, old);
+ gc_list_move(gc, old);
gc->gc.gc_refs = GC_REACHABLE;
}
}