Skip to content

Commit a4b2bc7

Browse files
bpo-9263: Use _PyObject_ASSERT() in gcmodule.c (GH-10112)
Replace assert() with _PyObject_ASSERT() in Modules/gcmodule.c to dump the faulty object on assertion failure to ease debugging. Fix also indentation of a large comment. Initial patch written by David Malcolm. Co-Authored-By: David Malcolm <dmalcolm@redhat.com>
1 parent 2470204 commit a4b2bc7

File tree

1 file changed

+56
-46
lines changed

1 file changed

+56
-46
lines changed

Modules/gcmodule.c

Lines changed: 56 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ update_refs(PyGC_Head *containers)
366366
* so serious that maybe this should be a release-build
367367
* check instead of an assert?
368368
*/
369-
assert(gc_get_refs(gc) != 0);
369+
_PyObject_ASSERT(FROM_GC(gc), gc_get_refs(gc) != 0);
370370
}
371371
}
372372

@@ -432,8 +432,10 @@ visit_reachable(PyObject *op, PyGC_Head *reachable)
432432
// Manually unlink gc from unreachable list because
433433
PyGC_Head *prev = GC_PREV(gc);
434434
PyGC_Head *next = (PyGC_Head*)(gc->_gc_next & ~NEXT_MASK_UNREACHABLE);
435-
assert(prev->_gc_next & NEXT_MASK_UNREACHABLE);
436-
assert(next->_gc_next & NEXT_MASK_UNREACHABLE);
435+
_PyObject_ASSERT(FROM_GC(prev),
436+
prev->_gc_next & NEXT_MASK_UNREACHABLE);
437+
_PyObject_ASSERT(FROM_GC(next),
438+
next->_gc_next & NEXT_MASK_UNREACHABLE);
437439
prev->_gc_next = gc->_gc_next; // copy NEXT_MASK_UNREACHABLE
438440
_PyGCHead_SET_PREV(next, prev);
439441

@@ -453,7 +455,7 @@ visit_reachable(PyObject *op, PyGC_Head *reachable)
453455
* list, and move_unreachable will eventually get to it.
454456
*/
455457
else {
456-
assert(gc_refs > 0);
458+
_PyObject_ASSERT_WITH_MSG(op, gc_refs > 0, "refcount is too small");
457459
}
458460
return 0;
459461
}
@@ -498,7 +500,8 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
498500
*/
499501
PyObject *op = FROM_GC(gc);
500502
traverseproc traverse = Py_TYPE(op)->tp_traverse;
501-
assert(gc_get_refs(gc) > 0);
503+
_PyObject_ASSERT_WITH_MSG(op, gc_get_refs(gc) > 0,
504+
"refcount is too small");
502505
// NOTE: visit_reachable may change gc->_gc_next when
503506
// young->_gc_prev == gc. Don't do gc = GC_NEXT(gc) before!
504507
(void) traverse(op,
@@ -593,7 +596,7 @@ move_legacy_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
593596
for (gc = GC_NEXT(unreachable); gc != unreachable; gc = next) {
594597
PyObject *op = FROM_GC(gc);
595598

596-
assert(gc->_gc_next & NEXT_MASK_UNREACHABLE);
599+
_PyObject_ASSERT(op, gc->_gc_next & NEXT_MASK_UNREACHABLE);
597600
gc->_gc_next &= ~NEXT_MASK_UNREACHABLE;
598601
next = (PyGC_Head*)gc->_gc_next;
599602

@@ -690,40 +693,42 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
690693
* the callback pointer intact. Obscure: it also
691694
* changes *wrlist.
692695
*/
693-
assert(wr->wr_object == op);
696+
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == op);
694697
_PyWeakref_ClearRef(wr);
695-
assert(wr->wr_object == Py_None);
696-
if (wr->wr_callback == NULL)
697-
continue; /* no callback */
698+
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None);
699+
if (wr->wr_callback == NULL) {
700+
/* no callback */
701+
continue;
702+
}
698703

699-
/* Headache time. `op` is going away, and is weakly referenced by
700-
* `wr`, which has a callback. Should the callback be invoked? If wr
701-
* is also trash, no:
702-
*
703-
* 1. There's no need to call it. The object and the weakref are
704-
* both going away, so it's legitimate to pretend the weakref is
705-
* going away first. The user has to ensure a weakref outlives its
706-
* referent if they want a guarantee that the wr callback will get
707-
* invoked.
708-
*
709-
* 2. It may be catastrophic to call it. If the callback is also in
710-
* cyclic trash (CT), then although the CT is unreachable from
711-
* outside the current generation, CT may be reachable from the
712-
* callback. Then the callback could resurrect insane objects.
713-
*
714-
* Since the callback is never needed and may be unsafe in this case,
715-
* wr is simply left in the unreachable set. Note that because we
716-
* already called _PyWeakref_ClearRef(wr), its callback will never
717-
* trigger.
718-
*
719-
* OTOH, if wr isn't part of CT, we should invoke the callback: the
720-
* weakref outlived the trash. Note that since wr isn't CT in this
721-
* case, its callback can't be CT either -- wr acted as an external
722-
* root to this generation, and therefore its callback did too. So
723-
* nothing in CT is reachable from the callback either, so it's hard
724-
* to imagine how calling it later could create a problem for us. wr
725-
* is moved to wrcb_to_call in this case.
726-
*/
704+
/* Headache time. `op` is going away, and is weakly referenced by
705+
* `wr`, which has a callback. Should the callback be invoked? If wr
706+
* is also trash, no:
707+
*
708+
* 1. There's no need to call it. The object and the weakref are
709+
* both going away, so it's legitimate to pretend the weakref is
710+
* going away first. The user has to ensure a weakref outlives its
711+
* referent if they want a guarantee that the wr callback will get
712+
* invoked.
713+
*
714+
* 2. It may be catastrophic to call it. If the callback is also in
715+
* cyclic trash (CT), then although the CT is unreachable from
716+
* outside the current generation, CT may be reachable from the
717+
* callback. Then the callback could resurrect insane objects.
718+
*
719+
* Since the callback is never needed and may be unsafe in this case,
720+
* wr is simply left in the unreachable set. Note that because we
721+
* already called _PyWeakref_ClearRef(wr), its callback will never
722+
* trigger.
723+
*
724+
* OTOH, if wr isn't part of CT, we should invoke the callback: the
725+
* weakref outlived the trash. Note that since wr isn't CT in this
726+
* case, its callback can't be CT either -- wr acted as an external
727+
* root to this generation, and therefore its callback did too. So
728+
* nothing in CT is reachable from the callback either, so it's hard
729+
* to imagine how calling it later could create a problem for us. wr
730+
* is moved to wrcb_to_call in this case.
731+
*/
727732
if (gc_is_collecting(AS_GC(wr))) {
728733
continue;
729734
}
@@ -751,10 +756,10 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
751756

752757
gc = (PyGC_Head*)wrcb_to_call._gc_next;
753758
op = FROM_GC(gc);
754-
assert(PyWeakref_Check(op));
759+
_PyObject_ASSERT(op, PyWeakref_Check(op));
755760
wr = (PyWeakReference *)op;
756761
callback = wr->wr_callback;
757-
assert(callback != NULL);
762+
_PyObject_ASSERT(op, callback != NULL);
758763

759764
/* copy-paste of weakrefobject.c's handle_callback() */
760765
temp = PyObject_CallFunctionObjArgs(callback, wr, NULL);
@@ -874,12 +879,14 @@ check_garbage(PyGC_Head *collectable)
874879
for (gc = GC_NEXT(collectable); gc != collectable; gc = GC_NEXT(gc)) {
875880
// Use gc_refs and break gc_prev again.
876881
gc_set_refs(gc, Py_REFCNT(FROM_GC(gc)));
877-
assert(gc_get_refs(gc) != 0);
882+
_PyObject_ASSERT(FROM_GC(gc), gc_get_refs(gc) != 0);
878883
}
879884
subtract_refs(collectable);
880885
PyGC_Head *prev = collectable;
881886
for (gc = GC_NEXT(collectable); gc != collectable; gc = GC_NEXT(gc)) {
882-
assert(gc_get_refs(gc) >= 0);
887+
_PyObject_ASSERT_WITH_MSG(FROM_GC(gc),
888+
gc_get_refs(gc) >= 0,
889+
"refcount is too small");
883890
if (gc_get_refs(gc) != 0) {
884891
ret = -1;
885892
}
@@ -905,7 +912,8 @@ delete_garbage(PyGC_Head *collectable, PyGC_Head *old)
905912
PyGC_Head *gc = GC_NEXT(collectable);
906913
PyObject *op = FROM_GC(gc);
907914

908-
assert(Py_REFCNT(FROM_GC(gc)) > 0);
915+
_PyObject_ASSERT_WITH_MSG(op, Py_REFCNT(op) > 0,
916+
"refcount is too small");
909917

910918
if (_PyRuntime.gc.debug & DEBUG_SAVEALL) {
911919
assert(_PyRuntime.gc.garbage != NULL);
@@ -1933,10 +1941,12 @@ PyVarObject *
19331941
_PyObject_GC_Resize(PyVarObject *op, Py_ssize_t nitems)
19341942
{
19351943
const size_t basicsize = _PyObject_VAR_SIZE(Py_TYPE(op), nitems);
1936-
PyGC_Head *g = AS_GC(op);
1937-
assert(!_PyObject_GC_IS_TRACKED(op));
1938-
if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head))
1944+
_PyObject_ASSERT((PyObject *)op, !_PyObject_GC_IS_TRACKED(op));
1945+
if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head)) {
19391946
return (PyVarObject *)PyErr_NoMemory();
1947+
}
1948+
1949+
PyGC_Head *g = AS_GC(op);
19401950
g = (PyGC_Head *)PyObject_REALLOC(g, sizeof(PyGC_Head) + basicsize);
19411951
if (g == NULL)
19421952
return (PyVarObject *)PyErr_NoMemory();

0 commit comments

Comments
 (0)