Skip to content

Commit b4f3a62

Browse files
miss-islingtonencukouhugovkericsnowcurrently
authored
[3.11] gh-110812: Isolating Extension Modules HOWTO: List GC-related gotchas (GH-111504) (GH-112147)
gh-110812: Isolating Extension Modules HOWTO: List GC-related gotchas (GH-111504) (cherry picked from commit 985679f) Co-authored-by: Petr Viktorin <encukou@gmail.com> Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com> Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
1 parent 132a749 commit b4f3a62

File tree

1 file changed

+97
-6
lines changed

1 file changed

+97
-6
lines changed

Doc/howto/isolating-extensions.rst

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -337,12 +337,44 @@ That is, heap types should:
337337
- Define a traverse function using ``Py_tp_traverse``, which
338338
visits the type (e.g. using :c:expr:`Py_VISIT(Py_TYPE(self))`).
339339

340-
Please refer to the :ref:`the documentation <type-structs>` of
340+
Please refer to the the documentation of
341341
:c:macro:`Py_TPFLAGS_HAVE_GC` and :c:member:`~PyTypeObject.tp_traverse`
342342
for additional considerations.
343343

344-
If your traverse function delegates to the ``tp_traverse`` of its base class
345-
(or another type), ensure that ``Py_TYPE(self)`` is visited only once.
344+
The API for defining heap types grew organically, leaving it
345+
somewhat awkward to use in its current state.
346+
The following sections will guide you through common issues.
347+
348+
349+
``tp_traverse`` in Python 3.8 and lower
350+
.......................................
351+
352+
The requirement to visit the type from ``tp_traverse`` was added in Python 3.9.
353+
If you support Python 3.8 and lower, the traverse function must *not*
354+
visit the type, so it must be more complicated::
355+
356+
static int my_traverse(PyObject *self, visitproc visit, void *arg)
357+
{
358+
if (Py_Version >= 0x03090000) {
359+
Py_VISIT(Py_TYPE(self));
360+
}
361+
return 0;
362+
}
363+
364+
Unfortunately, :c:data:`Py_Version` was only added in Python 3.11.
365+
As a replacement, use:
366+
367+
* :c:macro:`PY_VERSION_HEX`, if not using the stable ABI, or
368+
* :py:data:`sys.version_info` (via :c:func:`PySys_GetObject` and
369+
:c:func:`PyArg_ParseTuple`).
370+
371+
372+
Delegating ``tp_traverse``
373+
..........................
374+
375+
If your traverse function delegates to the :c:member:`~PyTypeObject.tp_traverse`
376+
of its base class (or another type), ensure that ``Py_TYPE(self)`` is visited
377+
only once.
346378
Note that only heap type are expected to visit the type in ``tp_traverse``.
347379

348380
For example, if your traverse function includes::
@@ -354,11 +386,70 @@ For example, if your traverse function includes::
354386
if (base->tp_flags & Py_TPFLAGS_HEAPTYPE) {
355387
// a heap type's tp_traverse already visited Py_TYPE(self)
356388
} else {
357-
Py_VISIT(Py_TYPE(self));
389+
if (Py_Version >= 0x03090000) {
390+
Py_VISIT(Py_TYPE(self));
391+
}
358392
}
359393

360-
It is not necessary to handle the type's reference count in ``tp_new``
361-
and ``tp_clear``.
394+
It is not necessary to handle the type's reference count in
395+
:c:member:`~PyTypeObject.tp_new` and :c:member:`~PyTypeObject.tp_clear`.
396+
397+
398+
Defining ``tp_dealloc``
399+
.......................
400+
401+
If your type has a custom :c:member:`~PyTypeObject.tp_dealloc` function,
402+
it needs to:
403+
404+
- call :c:func:`PyObject_GC_UnTrack` before any fields are invalidated, and
405+
- decrement the reference count of the type.
406+
407+
To keep the type valid while ``tp_free`` is called, the type's refcount needs
408+
to be decremented *after* the instance is deallocated. For example::
409+
410+
static void my_dealloc(PyObject *self)
411+
{
412+
PyObject_GC_UnTrack(self);
413+
...
414+
PyTypeObject *type = Py_TYPE(self);
415+
type->tp_free(self);
416+
Py_DECREF(type);
417+
}
418+
419+
The default ``tp_dealloc`` function does this, so
420+
if your type does *not* override
421+
``tp_dealloc`` you don't need to add it.
422+
423+
424+
Not overriding ``tp_free``
425+
..........................
426+
427+
The :c:member:`~PyTypeObject.tp_free` slot of a heap type must be set to
428+
:c:func:`PyObject_GC_Del`.
429+
This is the default; do not override it.
430+
431+
432+
Avoiding ``PyObject_New``
433+
.........................
434+
435+
GC-tracked objects need to be allocated using GC-aware functions.
436+
437+
If you use use :c:func:`PyObject_New` or :c:func:`PyObject_NewVar`:
438+
439+
- Get and call type's :c:member:`~PyTypeObject.tp_alloc` slot, if possible.
440+
That is, replace ``TYPE *o = PyObject_New(TYPE, typeobj)`` with::
441+
442+
TYPE *o = typeobj->tp_alloc(typeobj, 0);
443+
444+
Replace ``o = PyObject_NewVar(TYPE, typeobj, size)`` with the same,
445+
but use size instead of the 0.
446+
447+
- If the above is not possible (e.g. inside a custom ``tp_alloc``),
448+
call :c:func:`PyObject_GC_New` or :c:func:`PyObject_GC_NewVar`::
449+
450+
TYPE *o = PyObject_GC_New(TYPE, typeobj);
451+
452+
TYPE *o = PyObject_GC_NewVar(TYPE, typeobj, size);
362453

363454

364455
Module State Access from Classes

0 commit comments

Comments
 (0)