@@ -748,8 +748,9 @@ simplified::
748748 uniformity across these boring implementations.
749749
750750We also need to provide a method for clearing any subobjects that can
751- participate in cycles. We implement the method and reimplement the deallocator
752- to use it::
751+ participate in cycles.
752+
753+ ::
753754
754755 static int
755756 Noddy_clear(Noddy *self)
@@ -767,13 +768,6 @@ to use it::
767768 return 0;
768769 }
769770
770- static void
771- Noddy_dealloc(Noddy* self)
772- {
773- Noddy_clear(self);
774- self->ob_type->tp_free((PyObject*)self);
775- }
776-
777771Notice the use of a temporary variable in :c:func: `Noddy_clear `. We use the
778772temporary variable so that we can set each member to *NULL * before decrementing
779773its reference count. We do this because, as was discussed earlier, if the
@@ -796,6 +790,23 @@ decrementing of reference counts. With :c:func:`Py_CLEAR`, the
796790 return 0;
797791 }
798792
793+ Note that :c:func: `Noddy_dealloc ` may call arbitrary functions through
794+ ``__del__ `` method or weakref callback. It means circular GC can be
795+ triggered inside the function. Since GC assumes reference count is not zero,
796+ we need to untrack the object from GC by calling :c:func: `PyObject_GC_UnTrack `
797+ before clearing members. Here is reimplemented deallocator which uses
798+ :c:func: `PyObject_GC_UnTrack ` and :c:func: `Noddy_clear `.
799+
800+ ::
801+
802+ static void
803+ Noddy_dealloc(Noddy* self)
804+ {
805+ PyObject_GC_UnTrack(self);
806+ Noddy_clear(self);
807+ Py_TYPE(self)->tp_free((PyObject*)self);
808+ }
809+
799810Finally, we add the :const: `Py_TPFLAGS_HAVE_GC ` flag to the class flags::
800811
801812 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0 commit comments