@@ -728,8 +728,9 @@ functions. With :c:func:`Py_VISIT`, :c:func:`Noddy_traverse` can be simplified:
728728 uniformity across these boring implementations.
729729
730730We also need to provide a method for clearing any subobjects that can
731- participate in cycles. We implement the method and reimplement the deallocator
732- to use it::
731+ participate in cycles.
732+
733+ ::
733734
734735 static int
735736 Noddy_clear(Noddy *self)
@@ -747,13 +748,6 @@ to use it::
747748 return 0;
748749 }
749750
750- static void
751- Noddy_dealloc(Noddy* self)
752- {
753- Noddy_clear(self);
754- Py_TYPE(self)->tp_free((PyObject*)self);
755- }
756-
757751Notice the use of a temporary variable in :c:func: `Noddy_clear `. We use the
758752temporary variable so that we can set each member to *NULL * before decrementing
759753its reference count. We do this because, as was discussed earlier, if the
@@ -776,6 +770,23 @@ be simplified::
776770 return 0;
777771 }
778772
773+ Note that :c:func: `Noddy_dealloc ` may call arbitrary functions through
774+ ``__del__ `` method or weakref callback. It means circular GC can be
775+ triggered inside the function. Since GC assumes reference count is not zero,
776+ we need to untrack the object from GC by calling :c:func: `PyObject_GC_UnTrack `
777+ before clearing members. Here is reimplemented deallocator which uses
778+ :c:func: `PyObject_GC_UnTrack ` and :c:func: `Noddy_clear `.
779+
780+ ::
781+
782+ static void
783+ Noddy_dealloc(Noddy* self)
784+ {
785+ PyObject_GC_UnTrack(self);
786+ Noddy_clear(self);
787+ Py_TYPE(self)->tp_free((PyObject*)self);
788+ }
789+
779790Finally, we add the :const: `Py_TPFLAGS_HAVE_GC ` flag to the class flags::
780791
781792 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0 commit comments