detection; it's not expected that users will need to write their own
visitor functions.
-The :c:member:`~PyTypeObject.tp_traverse` handler must have the following type:
+The :c:member:`~PyTypeObject.tp_clear` handler must be of the :c:type:`inquiry` type, or ``NULL``
+if the object is immutable.
+
+
+.. c:type:: int (*inquiry)(PyObject *self)
+
+ Drop references that may have created reference cycles. Immutable objects
+ do not have to define this method since they can never directly create
+ reference cycles. Note that the object must still be valid after calling
+ this method (don't just call :c:func:`Py_DECREF` on a reference). The
+ collector will call this method if it detects that this object is involved
+ in a reference cycle.
+
+
+.. _gc-traversal:
+Traversal
+---------
+
+The :c:member:`~PyTypeObject.tp_traverse` handler must have the following type:
.. c:type:: int (*traverseproc)(PyObject *self, visitproc visit, void *arg)
- Traversal function for a container object. Implementations must call the
+ Traversal function for a garbage-collected object, used by the garbage
+ collector to detect reference cycles.
+ Implementations must call the
*visit* function for each object directly contained by *self*, with the
parameters to *visit* being the contained object and the *arg* value passed
to the handler. The *visit* function must not be called with a ``NULL``
- object argument. If *visit* returns a non-zero value that value should be
+ object argument. If *visit* returns a non-zero value, that value should be
returned immediately.
- The traversal function must not have any side effects. Implementations
- may not modify the reference counts of any Python objects nor create or
- destroy any Python objects.
+ A typical :c:member:`!tp_traverse` function calls the :c:func:`Py_VISIT`
+ convenience macro on each of the instance's members that are Python
+ objects that the instance owns.
+ For example, this is a (slightly outdated) traversal function for
+ the :py:class:`threading.local` class::
+
+ static int
+ local_traverse(PyObject *op, visitproc visit, void *arg)
+ {
+ localobject *self = (localobject *) op;
+ Py_VISIT(Py_TYPE(self));
+ Py_VISIT(self->args);
+ Py_VISIT(self->kw);
+ Py_VISIT(self->dict);
+ return 0;
+ }
+
+ .. note::
+ :c:func:`Py_VISIT` requires the *visit* and *arg* parameters to
+ :c:func:`!local_traverse` to have these specific names; don't name them just
+ anything.
+
+ Instances of :ref:`heap-allocated types <heap-types>` hold a reference to
+ their type. Their traversal function must therefore visit the type::
+
+ Py_VISIT(Py_TYPE(self));
+
+ Alternately, the type may delegate this responsibility by
+ calling ``tp_traverse`` of a heap-allocated superclass (or another
+ heap-allocated type, if applicable).
+ If they do not, the type object may not be garbage-collected.
+
+ If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the
+ :c:member:`~PyTypeObject.tp_flags` field, the traverse function must call
+ :c:func:`PyObject_VisitManagedDict` like this::
+
+ int err = PyObject_VisitManagedDict((PyObject*)self, visit, arg);
+ if (err) {
+ return err;
+ }
+
+ Only the members that the instance *owns* (by having
+ :term:`strong references <strong reference>` to them) must be
+ visited. For instance, if an object supports weak references via the
+ :c:member:`~PyTypeObject.tp_weaklist` slot, the pointer supporting
+ the linked list (what *tp_weaklist* points to) must **not** be
+ visited as the instance does not directly own the weak references to itself.
+
+ The traversal function has a limitation:
+
+ .. warning::
+
+ The traversal function must not have any side effects. Implementations
+ may not modify the reference counts of any Python objects nor create or
+ destroy any Python objects, directly or indirectly.
+
+ This means that *most* Python C API functions may not be used, since
+ they can raise a new exception, return a new reference to a result object,
+ have internal logic that uses side effects.
+ Also, unless documented otherwise, functions that happen to not have side
+ effects may start having them in future versions, without warning.
+
+ For a list of safe functions, see a
+ :ref:`separate section <duringgc-functions>` below.
+
+ .. note::
+
+ The :c:func:`Py_VISIT` call may be skipped for those members that provably
+ cannot participate in reference cycles.
+ In the ``local_traverse`` example above, there is also a ``self->key``
+ member, but it can only be ``NULL`` or a Python string and therefore
+ cannot be part of a reference cycle.
+
+ On the other hand, even if you know a member can never be part of a cycle,
+ as a debugging aid you may want to visit it anyway just so the :mod:`gc`
+ module's :func:`~gc.get_referents` function will include it.
+
+ .. note::
+
+ The :c:member:`~PyTypeObject.tp_traverse` function can be called from any
+ thread.
-To simplify writing :c:member:`~PyTypeObject.tp_traverse` handlers, a :c:func:`Py_VISIT` macro is
-provided. In order to use this macro, the :c:member:`~PyTypeObject.tp_traverse` implementation
-must name its arguments exactly *visit* and *arg*:
+ .. impl-detail::
+ Garbage collection is a "stop-the-world" operation:
+ even in :term:`free threading` builds, only one thread state is
+ :term:`attached <attached thread state>` when :c:member:`!tp_traverse`
+ handlers run.
+
+ .. versionchanged:: 3.9
+
+ Heap-allocated types are expected to visit ``Py_TYPE(self)`` in
+ ``tp_traverse``. In earlier versions of Python, due to
+ `bug 40217 <https://bugs.python.org/issue40217>`_, doing this
+ may lead to crashes in subclasses.
+
+To simplify writing :c:member:`~PyTypeObject.tp_traverse` handlers,
+a :c:func:`Py_VISIT` macro is provided.
+In order to use this macro, the :c:member:`~PyTypeObject.tp_traverse`
+implementation must name its arguments exactly *visit* and *arg*:
.. c:macro:: Py_VISIT(o)
- If the :c:expr:`PyObject *` *o* is not ``NULL``, call the *visit* callback, with arguments *o*
- and *arg*. If *visit* returns a non-zero value, then return it.
- Using this macro, :c:member:`~PyTypeObject.tp_traverse` handlers
- look like::
+ If the :c:expr:`PyObject *` *o* is not ``NULL``, call the *visit*
+ callback, with arguments *o* and *arg*.
+ If *visit* returns a non-zero value, then return it.
- static int
- my_traverse(Noddy *self, visitproc visit, void *arg)
- {
- Py_VISIT(self->foo);
- Py_VISIT(self->bar);
- return 0;
- }
+ This corresponds roughly to::
-The :c:member:`~PyTypeObject.tp_clear` handler must be of the :c:type:`inquiry` type, or ``NULL``
-if the object is immutable.
+ #define Py_VISIT(o) \
+ if (op) { \
+ int visit_result = visit(o, arg); \
+ if (visit_result != 0) { \
+ return visit_result; \
+ } \
+ }
-.. c:type:: int (*inquiry)(PyObject *self)
+Traversal-safe functions
+^^^^^^^^^^^^^^^^^^^^^^^^
- Drop references that may have created reference cycles. Immutable objects
- do not have to define this method since they can never directly create
- reference cycles. Note that the object must still be valid after calling
- this method (don't just call :c:func:`Py_DECREF` on a reference). The
- collector will call this method if it detects that this object is involved
- in a reference cycle.
+The following functions and macros are safe to use in a
+:c:member:`~PyTypeObject.tp_traverse` handler:
+
+* the *visit* function passed to ``tp_traverse``
+* :c:func:`Py_VISIT`
+* :c:func:`Py_SIZE`
+* :c:func:`Py_TYPE`: if called from a :c:member:`!tp_traverse` handler,
+ :c:func:`!Py_TYPE`'s result will be valid for the duration of the handler call
+* :c:func:`PyObject_VisitManagedDict`
+* :c:func:`PyObject_TypeCheck`, :c:func:`PyType_IsSubtype`,
+ :c:func:`PyType_HasFeature`
+* :samp:`Py{<type>}_Check` and :samp:`Py{<type>}_CheckExact` -- for example,
+ :c:func:`PyTuple_Check`
+* :ref:`duringgc-functions`
+
+.. _duringgc-functions:
+
+"DuringGC" functions
+^^^^^^^^^^^^^^^^^^^^
+
+The following functions should *only* be used in a
+:c:member:`~PyTypeObject.tp_traverse` handler; calling them in other
+contexts may have unintended consequences.
+
+These functions act like their counterparts without the ``_DuringGC`` suffix,
+but they are guaranteed to not have side effects, they do not set an exception
+on failure, and they return/set :term:`borrowed references <borrowed reference>`
+as detailed in the individual documentation.
+
+Note that these functions may fail (return ``NULL`` or ``-1``),
+but as they do not set an exception, no error information is available.
+In some cases, failure is not distinguishable from a successful ``NULL`` result.
+
+.. c:function:: void *PyObject_GetTypeData_DuringGC(PyObject *o, PyTypeObject *cls)
+ void *PyObject_GetItemData_DuringGC(PyObject *o)
+ void *PyType_GetModuleState_DuringGC(PyTypeObject *type)
+ void *PyModule_GetState_DuringGC(PyObject *module)
+ int PyModule_GetToken_DuringGC(PyObject *module, void** result)
+
+ See :ref:`duringgc-functions` for common information.
+
+ .. versionadded:: next
+
+ .. seealso::
+
+ :c:func:`PyObject_GetTypeData`,
+ :c:func:`PyObject_GetItemData`,
+ :c:func:`PyType_GetModuleState`,
+ :c:func:`PyModule_GetState`,
+ :c:func:`PyModule_GetToken`,
+ :c:func:`PyType_GetBaseByToken`
+
+.. c:function:: int PyType_GetBaseByToken_DuringGC(PyTypeObject *type, void *tp_token, PyTypeObject **result)
+
+ See :ref:`duringgc-functions` for common information.
+
+ Sets *\*result* to a :term:`borrowed reference` rather than a strong one.
+ The reference is valid for the duration
+ of the :c:member:`!tp_traverse` handler call.
+
+ .. versionadded:: next
+
+ .. seealso:: :c:func:`PyType_GetBaseByToken`
+
+.. c:function:: PyObject* PyType_GetModule_DuringGC(PyTypeObject *type)
+ PyObject* PyType_GetModuleByToken_DuringGC(PyTypeObject *type, const void *mod_token)
+
+ See :ref:`duringgc-functions` for common information.
+
+ These functions return a :term:`borrowed reference`, which is
+ valid for the duration of the :c:member:`!tp_traverse` handler call.
+
+ .. versionadded:: next
+
+ .. seealso::
+
+ :c:func:`PyType_GetModule`,
+ :c:func:`PyType_GetModuleByToken`
Controlling the Garbage Collector State
.. corresponding-type-slot:: Py_tp_traverse
An optional pointer to a traversal function for the garbage collector. This is
- only used if the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is set. The signature is::
+ only used if the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is set.
- int tp_traverse(PyObject *self, visitproc visit, void *arg);
-
- More information about Python's garbage collection scheme can be found
- in section :ref:`supporting-cycle-detection`.
-
- The :c:member:`~PyTypeObject.tp_traverse` pointer is used by the garbage collector to detect
- reference cycles. A typical implementation of a :c:member:`~PyTypeObject.tp_traverse` function
- simply calls :c:func:`Py_VISIT` on each of the instance's members that are Python
- objects that the instance owns. For example, this is function :c:func:`!local_traverse` from the
- :mod:`!_thread` extension module::
-
- static int
- local_traverse(PyObject *op, visitproc visit, void *arg)
- {
- localobject *self = (localobject *) op;
- Py_VISIT(self->args);
- Py_VISIT(self->kw);
- Py_VISIT(self->dict);
- return 0;
- }
-
- Note that :c:func:`Py_VISIT` is called only on those members that can participate
- in reference cycles. Although there is also a ``self->key`` member, it can only
- be ``NULL`` or a Python string and therefore cannot be part of a reference cycle.
-
- On the other hand, even if you know a member can never be part of a cycle, as a
- debugging aid you may want to visit it anyway just so the :mod:`gc` module's
- :func:`~gc.get_referents` function will include it.
-
- Heap types (:c:macro:`Py_TPFLAGS_HEAPTYPE`) must visit their type with::
-
- Py_VISIT(Py_TYPE(self));
-
- It is only needed since Python 3.9. To support Python 3.8 and older, this
- line must be conditional::
-
- #if PY_VERSION_HEX >= 0x03090000
- Py_VISIT(Py_TYPE(self));
- #endif
-
- If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the
- :c:member:`~PyTypeObject.tp_flags` field, the traverse function must call
- :c:func:`PyObject_VisitManagedDict` like this::
-
- PyObject_VisitManagedDict((PyObject*)self, visit, arg);
-
- .. warning::
- When implementing :c:member:`~PyTypeObject.tp_traverse`, only the
- members that the instance *owns* (by having :term:`strong references
- <strong reference>` to them) must be
- visited. For instance, if an object supports weak references via the
- :c:member:`~PyTypeObject.tp_weaklist` slot, the pointer supporting
- the linked list (what *tp_weaklist* points to) must **not** be
- visited as the instance does not directly own the weak references to itself
- (the weakreference list is there to support the weak reference machinery,
- but the instance has no strong reference to the elements inside it, as they
- are allowed to be removed even if the instance is still alive).
-
- .. warning::
- The traversal function must not have any side effects. It must not
- modify the reference counts of any Python objects nor create or destroy
- any Python objects.
-
- Note that :c:func:`Py_VISIT` requires the *visit* and *arg* parameters to
- :c:func:`!local_traverse` to have these specific names; don't name them just
- anything.
-
- Instances of :ref:`heap-allocated types <heap-types>` hold a reference to
- their type. Their traversal function must therefore either visit
- :c:func:`Py_TYPE(self) <Py_TYPE>`, or delegate this responsibility by
- calling ``tp_traverse`` of another heap-allocated type (such as a
- heap-allocated superclass).
- If they do not, the type object may not be garbage-collected.
-
- .. note::
-
- The :c:member:`~PyTypeObject.tp_traverse` function can be called from any
- thread.
-
- .. versionchanged:: 3.9
-
- Heap-allocated types are expected to visit ``Py_TYPE(self)`` in
- ``tp_traverse``. In earlier versions of Python, due to
- `bug 40217 <https://bugs.python.org/issue40217>`_, doing this
- may lead to crashes in subclasses.
+ See :ref:`gc-traversal` for documentation.
**Inheritance:**
PyType_GetModule:PyObject*::0:
PyType_GetModule:PyTypeObject*:type:0:
+PyType_GetModule_DuringGC:PyObject*::0:
+PyType_GetModule_DuringGC:PyTypeObject*:type:0:
+
PyType_GetModuleByToken:PyObject*::+1:
PyType_GetModuleByToken:PyTypeObject*:type:0:
PyType_GetModuleByToken:PyModuleDef*:def::
+PyType_GetModuleByToken_DuringGC:PyObject*::0:
+PyType_GetModuleByToken_DuringGC:PyTypeObject*:type:0:
+PyType_GetModuleByToken_DuringGC:PyModuleDef*:mod_token::
+
PyType_GetModuleByDef:PyObject*::0:
PyType_GetModuleByDef:PyTypeObject*:type:0:
PyType_GetModuleByDef:PyModuleDef*:def::
func,PyModule_GetNameObject,3.7,,
func,PyModule_GetState,3.2,,
func,PyModule_GetStateSize,3.15,,
+func,PyModule_GetState_DuringGC,3.15,,
func,PyModule_GetToken,3.15,,
+func,PyModule_GetToken_DuringGC,3.15,,
func,PyModule_New,3.2,,
func,PyModule_NewObject,3.7,,
func,PyModule_SetDocString,3.7,,
func,PyObject_GetOptionalAttr,3.13,,
func,PyObject_GetOptionalAttrString,3.13,,
func,PyObject_GetTypeData,3.12,,
+func,PyObject_GetTypeData_DuringGC,3.15,,
func,PyObject_HasAttr,3.2,,
func,PyObject_HasAttrString,3.2,,
func,PyObject_HasAttrStringWithError,3.13,,
func,PyType_GenericAlloc,3.2,,
func,PyType_GenericNew,3.2,,
func,PyType_GetBaseByToken,3.14,,
+func,PyType_GetBaseByToken_DuringGC,3.15,,
func,PyType_GetFlags,3.2,,
func,PyType_GetFullyQualifiedName,3.13,,
func,PyType_GetModule,3.10,,
func,PyType_GetModuleByDef,3.13,,
func,PyType_GetModuleByToken,3.15,,
+func,PyType_GetModuleByToken_DuringGC,3.15,,
func,PyType_GetModuleName,3.13,,
func,PyType_GetModuleState,3.10,,
+func,PyType_GetModuleState_DuringGC,3.15,,
+func,PyType_GetModule_DuringGC,3.15,,
func,PyType_GetName,3.11,,
func,PyType_GetQualName,3.11,,
func,PyType_GetSlot,3.4,,
* Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array.
(Contributed by Victor Stinner in :gh:`111489`.)
+* Add functions that are guaranteed to be safe for use in
+ :c:member:`~PyTypeObject.tp_traverse` handlers:
+ :c:func:`PyObject_GetTypeData_DuringGC`,
+ :c:func:`PyObject_GetItemData_DuringGC`,
+ :c:func:`PyType_GetModuleState_DuringGC`,
+ :c:func:`PyModule_GetState_DuringGC`, :c:func:`PyModule_GetToken_DuringGC`,
+ :c:func:`PyType_GetBaseByToken_DuringGC`,
+ :c:func:`PyType_GetModule_DuringGC`,
+ :c:func:`PyType_GetModuleByToken_DuringGC`.
+ (Contributed by Petr Viktorin in :gh:`145925`.)
+
* Add :c:func:`PyObject_Dump` to dump an object to ``stderr``.
It should only be used for debugging.
(Contributed by Victor Stinner in :gh:`141070`.)
PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj);
+PyAPI_FUNC(void *) PyObject_GetItemData_DuringGC(PyObject *obj);
PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg);
PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj);
return NULL;
}
+// Get md_token. Used in _DuringGC functions; must have no side effects.
static inline PyModuleDef *_PyModule_GetToken(PyObject *arg) {
PyModuleObject *mod = _PyModule_CAST(arg);
return (PyModuleDef *)mod->md_token;
}
+// Get md_state. Used in _DuringGC functions; must have no side effects.
static inline void* _PyModule_GetState(PyObject* mod) {
return _PyModule_CAST(mod)->md_state;
}
PyAPI_FUNC(int) PyModule_Exec(PyObject *module);
PyAPI_FUNC(int) PyModule_GetStateSize(PyObject *module, Py_ssize_t *result);
PyAPI_FUNC(int) PyModule_GetToken(PyObject *module, void **result);
+PyAPI_FUNC(void*) PyModule_GetState_DuringGC(PyObject*);
+PyAPI_FUNC(int) PyModule_GetToken_DuringGC(PyObject *module, void **result);
#endif
#ifndef _Py_OPAQUE_PYOBJECT
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
PyAPI_FUNC(PyObject *) PyType_GetModuleByToken(PyTypeObject *type,
const void *token);
+PyAPI_FUNC(void *) PyObject_GetTypeData_DuringGC(PyObject *obj,
+ PyTypeObject *cls);
+PyAPI_FUNC(void *) PyType_GetModuleState_DuringGC(PyTypeObject *);
+PyAPI_FUNC(int) PyType_GetBaseByToken_DuringGC(PyTypeObject *,
+ void *, PyTypeObject **);
+PyAPI_FUNC(PyObject *) PyType_GetModule_DuringGC(PyTypeObject *);
+PyAPI_FUNC(PyObject *) PyType_GetModuleByToken_DuringGC(PyTypeObject *type,
+ const void *token);
#endif
#ifdef __cplusplus
"PyModule_GetNameObject",
"PyModule_GetState",
"PyModule_GetStateSize",
+ "PyModule_GetState_DuringGC",
"PyModule_GetToken",
+ "PyModule_GetToken_DuringGC",
"PyModule_New",
"PyModule_NewObject",
"PyModule_SetDocString",
"PyObject_GetOptionalAttr",
"PyObject_GetOptionalAttrString",
"PyObject_GetTypeData",
+ "PyObject_GetTypeData_DuringGC",
"PyObject_HasAttr",
"PyObject_HasAttrString",
"PyObject_HasAttrStringWithError",
"PyType_GenericAlloc",
"PyType_GenericNew",
"PyType_GetBaseByToken",
+ "PyType_GetBaseByToken_DuringGC",
"PyType_GetFlags",
"PyType_GetFullyQualifiedName",
"PyType_GetModule",
"PyType_GetModuleByDef",
"PyType_GetModuleByToken",
+ "PyType_GetModuleByToken_DuringGC",
"PyType_GetModuleName",
"PyType_GetModuleState",
+ "PyType_GetModuleState_DuringGC",
+ "PyType_GetModule_DuringGC",
"PyType_GetName",
"PyType_GetQualName",
"PyType_GetSlot",
--- /dev/null
+Add functions that are guaranteed to be safe for use in
+:c:member:`~PyTypeObject.tp_traverse` handlers:
+:c:func:`PyObject_GetTypeData_DuringGC`,
+:c:func:`PyObject_GetItemData_DuringGC`,
+:c:func:`PyType_GetModuleState_DuringGC`,
+:c:func:`PyModule_GetState_DuringGC`, :c:func:`PyModule_GetToken_DuringGC`,
+:c:func:`PyType_GetBaseByToken_DuringGC`,
+:c:func:`PyType_GetModule_DuringGC`,
+:c:func:`PyType_GetModuleByToken_DuringGC`.
[function.Py_SET_SIZE]
# Before 3.15, this was a macro that accessed the PyObject member
added = '3.15'
+[function.PyObject_GetTypeData_DuringGC]
+ added = '3.15'
+[function.PyType_GetModuleState_DuringGC]
+ added = '3.15'
+[function.PyModule_GetState_DuringGC]
+ added = '3.15'
+[function.PyModule_GetToken_DuringGC]
+ added = '3.15'
+[function.PyType_GetBaseByToken_DuringGC]
+ added = '3.15'
+[function.PyType_GetModule_DuringGC]
+ added = '3.15'
+[function.PyType_GetModuleByToken_DuringGC]
+ added = '3.15'
# PEP 757 import/export API.
static int
CType_Type_traverse(PyObject *self, visitproc visit, void *arg)
{
- StgInfo *info = _PyStgInfo_FromType_NoState(self);
- if (!info) {
- PyErr_FormatUnraisable("Exception ignored while "
- "calling ctypes traverse function %R", self);
- }
+ StgInfo *info = _PyStgInfo_FromType_DuringGC(self);
if (info) {
Py_VISIT(info->proto);
Py_VISIT(info->argtypes);
static int
CType_Type_clear(PyObject *self)
{
- StgInfo *info = _PyStgInfo_FromType_NoState(self);
- if (!info) {
- PyErr_FormatUnraisable("Exception ignored while "
- "clearing ctypes %R", self);
- }
+ StgInfo *info = _PyStgInfo_FromType_DuringGC(self);
if (info) {
ctype_clear_stginfo(info);
}
static void
CType_Type_dealloc(PyObject *self)
{
- StgInfo *info = _PyStgInfo_FromType_NoState(self);
- if (!info) {
- PyErr_FormatUnraisable("Exception ignored while "
- "deallocating ctypes %R", self);
- }
+ StgInfo *info = _PyStgInfo_FromType_DuringGC(self);
if (info) {
ctype_free_stginfo_members(info);
}
* state is torn down.
*/
static inline StgInfo *
-_PyStgInfo_FromType_NoState(PyObject *type)
+_PyStgInfo_FromType_DuringGC(PyObject *type)
{
PyTypeObject *PyCType_Type;
- if (_PyType_GetBaseByToken_Borrow(Py_TYPE(type), &pyctype_type_spec, &PyCType_Type) < 0 ||
- PyCType_Type == NULL) {
+ PyType_GetBaseByToken_DuringGC(Py_TYPE(type), &pyctype_type_spec, &PyCType_Type);
+ if (PyCType_Type == NULL) {
return NULL;
}
-
- return PyObject_GetTypeData(type, PyCType_Type);
+ return PyObject_GetTypeData_DuringGC(type, PyCType_Type);
}
// Initialize StgInfo on a newly created type
pyobject_getitemdata(PyObject *self, PyObject *o)
{
void *pointer = PyObject_GetItemData(o);
+ assert(pointer == PyObject_GetItemData_DuringGC(o));
if (pointer == NULL) {
return NULL;
}
mro_save = type->tp_mro;
type->tp_mro = NULL;
}
-
void *token = PyLong_AsVoidPtr(py_token);
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+
+ void *result_duringgc;
+ int ret_duringgc = PyType_GetBaseByToken_DuringGC(
+ type, token, (PyTypeObject **)&result_duringgc);
+ assert(!PyErr_Occurred());
+
PyObject *result;
int ret;
if (need_result == Py_True) {
ret = PyType_GetBaseByToken(type, token, (PyTypeObject **)&result);
+ assert(result == result_duringgc);
}
else {
result = NULL;
ret = PyType_GetBaseByToken(type, token, NULL);
}
+ assert(ret == ret_duringgc);
if (use_mro != Py_True) {
type->tp_mro = mro_save;
error:
Py_XDECREF(py_ret);
Py_XDECREF(result);
+ assert(PyErr_Occurred());
return NULL;
}
pytype_getmodulebydef(PyObject *self, PyObject *type)
{
PyObject *mod = PyType_GetModuleByDef((PyTypeObject *)type, _testcapimodule);
+ assert(mod == PyType_GetModuleByToken_DuringGC((PyTypeObject *)type, _testcapimodule));
return Py_XNewRef(mod);
}
if ((!token) && PyErr_Occurred()) {
return NULL;
}
- return PyType_GetModuleByToken((PyTypeObject *)type, token);
+ PyObject *result = PyType_GetModuleByToken((PyTypeObject *)type, token);
+ assert(result == PyType_GetModuleByToken_DuringGC((PyTypeObject *)type, token));
+ return result;
}
static PyType_Slot HeapCTypeWithBasesSlotNone_slots[] = {
PyObject *exc = PyErr_GetRaisedException();
PyObject *m = PyType_GetModule(Py_TYPE(self));
+ assert(m == PyType_GetModule_DuringGC(Py_TYPE(self)));
if (m == NULL) {
goto cleanup_finalize;
}
goto finally;
}
PyObject **data = PyObject_GetItemData(self);
+ assert(data == PyObject_GetItemData_DuringGC(self));
if (!data) {
goto finally;
}
return PyErr_Format(PyExc_IndexError, "index %zd out of range", i);
}
PyObject **data = PyObject_GetItemData(self);
+ assert(data == PyObject_GetItemData_DuringGC(self));
if (!data) {
return NULL;
}
HeapCCollection_traverse(PyObject *self, visitproc visit, void *arg)
{
PyObject **data = PyObject_GetItemData(self);
+ assert(data == PyObject_GetItemData_DuringGC(self));
if (!data) {
return -1;
}
HeapCCollection_clear(PyObject *self)
{
PyObject **data = PyObject_GetItemData(self);
+ assert(data == PyObject_GetItemData_DuringGC(self));
if (!data) {
return -1;
}
return NULL;
}
assert(got_token == &test_token);
+ assert(PyModule_GetToken_DuringGC(mod, &got_token) >= 0);
+ assert(got_token == &test_token);
return mod;
}
pymodule_get_token(PyObject *self, PyObject *module)
{
void *token;
- if (PyModule_GetToken(module, &token) < 0) {
+ int res = PyModule_GetToken(module, &token);
+ void *token_duringgc;
+ int res_duringgc = PyModule_GetToken_DuringGC(module, &token_duringgc);
+ assert(res == res_duringgc);
+ assert(token == token_duringgc);
+ if (res < 0) {
return NULL;
}
return PyLong_FromVoidPtr(token);
-// Need limited C API version 3.12 for PyType_FromMetaclass()
+// Need limited C API version 3.15 for _DuringGC functions
#include "pyconfig.h" // Py_GIL_DISABLED
#if !defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API)
-# define Py_LIMITED_API 0x030c0000
+# define Py_LIMITED_API 0x030f0000
#endif
#include "parts.h"
goto finally;
}
char *data_ptr = PyObject_GetTypeData(instance, (PyTypeObject *)sub);
+ assert(data_ptr == PyObject_GetTypeData_DuringGC(instance,
+ (PyTypeObject *)sub));
if (!data_ptr) {
goto finally;
}
PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
void *data_ptr = PyObject_GetTypeData(self, defining_class);
+ assert(data_ptr == PyObject_GetTypeData_DuringGC(self, defining_class));
if (!data_ptr) {
return NULL;
}
PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
void *data_ptr = PyObject_GetTypeData(self, defining_class);
+ assert(data_ptr == PyObject_GetTypeData_DuringGC(self, defining_class));
if (!data_ptr) {
return NULL;
}
{
PyTypeObject *tp = Py_TYPE(self);
HeapCTypeWithDictStruct *data = PyObject_GetTypeData(self, tp);
+ assert(data == PyObject_GetTypeData_DuringGC(self, tp));
Py_XDECREF(data->dict);
PyObject_Free(self);
Py_DECREF(tp);
{
PyTypeObject *tp = Py_TYPE(self);
HeapCTypeWithWeakrefStruct *data = PyObject_GetTypeData(self, tp);
+ assert(data == PyObject_GetTypeData_DuringGC(self, tp));
if (data->weakreflist != NULL) {
PyObject_ClearWeakRefs(self);
}
{
PyObject *retval;
retval = PyType_GetModule(cls);
+ assert(retval == PyType_GetModule_DuringGC(cls));
if (retval == NULL) {
return NULL;
}
assert(PyType_GetModuleByDef(Py_TYPE(self), &def_meth_state_access) == retval);
+ assert(PyType_GetModuleByToken_DuringGC(Py_TYPE(self), &def_meth_state_access)
+ == retval);
return Py_NewRef(retval);
}
PyTypeObject *cls)
/*[clinic end generated code: output=64509074dfcdbd31 input=edaff09aa4788204]*/
{
- PyType_GetModuleByDef(Py_TYPE(self), &def_nonmodule); // should raise
+ // DuringGC: does not raise
+ assert(PyType_GetModuleByToken_DuringGC(Py_TYPE(self), &def_nonmodule) == NULL);
+ assert(!PyErr_Occurred());
+ // should raise:
+ PyObject *m = PyType_GetModuleByDef(Py_TYPE(self), &def_nonmodule);
assert(PyErr_Occurred());
- return NULL;
+ assert(m == NULL);
+ return m;
}
/*[clinic input]
/*[clinic end generated code: output=3b34f86bc5473204 input=551d482e1fe0b8f5]*/
{
meth_state *m_state = PyType_GetModuleState(cls);
+ assert(m_state == PyType_GetModuleState_DuringGC(cls));
if (twice) {
n *= 2;
}
n *= 2;
}
meth_state *m_state = PyType_GetModuleState(defining_class);
+ assert(m_state == PyType_GetModuleState_DuringGC(defining_class));
m_state->counter += n;
Py_RETURN_NONE;
/*[clinic end generated code: output=64600f95b499a319 input=d5d181f12384849f]*/
{
meth_state *m_state = PyType_GetModuleState(cls);
+ assert(m_state == PyType_GetModuleState_DuringGC(cls));
return PyLong_FromLong(m_state->counter);
}
meth_state *m_state;
m_state = PyModule_GetState(m);
+ assert(m_state == PyModule_GetState_DuringGC(m));
if (m_state == NULL) {
return -1;
}
return 0;
}
int *state = PyModule_GetState(mod);
+ assert(state == PyModule_GetState_DuringGC(mod));
if (!state) {
return -1;
}
modexport_smoke_get_state_int(PyObject *mod, PyObject *arg)
{
int *state = PyModule_GetState(mod);
+ assert(state == PyModule_GetState_DuringGC(mod));
if (!state) {
return NULL;
}
{
PyObject *mod = (PyObject *)op;
int *state = PyModule_GetState(mod);
+ assert(state == PyModule_GetState_DuringGC(mod));
if (!state) {
PyErr_FormatUnraisable("Exception ignored in module %R free", mod);
}
}
else {
module_state *state = (module_state*)PyModule_GetState(module);
+ assert(state == PyModule_GetState_DuringGC(module));
assert(state != NULL);
return state;
}
return 0;
}
+int
+PyModule_GetToken_DuringGC(PyObject *m, void **token_p)
+{
+ *token_p = NULL;
+ if (!PyModule_Check(m)) {
+ return -1;
+ }
+ *token_p = _PyModule_GetToken(m);
+ return 0;
+}
+
int
PyModule_GetToken(PyObject *m, void **token_p)
{
return _PyModule_GetDefOrNull(m);
}
+void*
+PyModule_GetState_DuringGC(PyObject* m)
+{
+ if (!PyModule_Check(m)) {
+ return NULL;
+ }
+ return _PyModule_GetState(m);
+}
+
void*
PyModule_GetState(PyObject* m)
{
return *(void**)((char*)parent_slot + pyslot_offsets[slot].subslot_offset);
}
+PyObject *
+PyType_GetModule_DuringGC(PyTypeObject *type)
+{
+ assert(PyType_Check(type));
+ if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
+ return NULL;
+ }
+ PyHeapTypeObject* et = (PyHeapTypeObject*)type;
+ return et->ht_module;
+}
+
PyObject *
PyType_GetModule(PyTypeObject *type)
{
return NULL;
}
return et->ht_module;
+}
+void *
+PyType_GetModuleState_DuringGC(PyTypeObject *type)
+{
+ PyObject *m = PyType_GetModule_DuringGC(type);
+ if (m == NULL) {
+ return NULL;
+ }
+ return _PyModule_GetState(m);
}
void *
return _PyModule_GetState(m);
}
-
/* Return borrowed ref to the module of the first superclass where the module
* has the given token.
*/
-static PyObject *
-borrow_module_by_token(PyTypeObject *type, const void *token)
+PyObject *
+PyType_GetModuleByToken_DuringGC(PyTypeObject *type, const void *token)
{
assert(PyType_Check(type));
if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
// type_ready_mro() ensures that no heap type is
// contained in a static type MRO.
- goto error;
+ return NULL;
}
else {
PyHeapTypeObject *ht = (PyHeapTypeObject*)type;
}
END_TYPE_LOCK();
- if (res != NULL) {
- return res;
- }
-error:
- PyErr_Format(
- PyExc_TypeError,
- "PyType_GetModuleByDef: No superclass of '%s' has the given module",
- type->tp_name);
- return NULL;
+ return res;
}
PyObject *
-PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def)
+PyType_GetModuleByToken(PyTypeObject *type, const void *token)
{
- return borrow_module_by_token(type, def);
+ PyObject *mod = PyType_GetModuleByToken_DuringGC(type, token);
+ if (!mod) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "PyType_GetModuleByDef: No superclass of '%s' has the given module",
+ type->tp_name);
+ return NULL;
+ }
+ return Py_NewRef(mod);
}
PyObject *
-PyType_GetModuleByToken(PyTypeObject *type, const void *token)
+PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def)
{
- return Py_XNewRef(borrow_module_by_token(type, token));
+ PyObject *mod = PyType_GetModuleByToken(type, def);
+ Py_XDECREF(mod); // return borrowed ref
+ return mod;
}
}
int
-_PyType_GetBaseByToken_Borrow(PyTypeObject *type, void *token, PyTypeObject **result)
+PyType_GetBaseByToken_DuringGC(PyTypeObject *type, void *token, PyTypeObject **result)
{
- assert(token != NULL);
- assert(PyType_Check(type));
-
if (result != NULL) {
*result = NULL;
}
+ if (token == NULL) {
+ return -1;
+ }
+ if (!PyType_Check(type)) {
+ return -1;
+ }
if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
// No static type has a heaptype superclass,
return -1;
}
- int res = _PyType_GetBaseByToken_Borrow(type, token, result);
+ int res = PyType_GetBaseByToken_DuringGC(type, token, result);
if (res > 0 && result) {
Py_INCREF(*result);
}
void *
-PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls)
+PyObject_GetTypeData_DuringGC(PyObject *obj, PyTypeObject *cls)
{
assert(PyObject_TypeCheck(obj, cls));
return (char *)obj + _align_up(cls->tp_base->tp_basicsize);
}
+void *
+PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls)
+{
+ return PyObject_GetTypeData_DuringGC(obj, cls);
+}
+
Py_ssize_t
PyType_GetTypeDataSize(PyTypeObject *cls)
{
return result;
}
-void *
-PyObject_GetItemData(PyObject *obj)
+static inline void *
+getitemdata(PyObject *obj, bool raise)
{
- if (!PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_ITEMS_AT_END)) {
- PyErr_Format(PyExc_TypeError,
- "type '%s' does not have Py_TPFLAGS_ITEMS_AT_END",
- Py_TYPE(obj)->tp_name);
+ if (!_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_ITEMS_AT_END)) {
+ if (raise) {
+ PyErr_Format(PyExc_TypeError,
+ "type '%T' does not have Py_TPFLAGS_ITEMS_AT_END",
+ obj);
+ }
return NULL;
}
return (char *)obj + Py_TYPE(obj)->tp_basicsize;
}
+void *
+PyObject_GetItemData_DuringGC(PyObject *obj)
+{
+ return getitemdata(obj, false);
+}
+
+void *
+PyObject_GetItemData(PyObject *obj)
+{
+ return getitemdata(obj, true);
+}
+
/* Internal API to look for a name through the MRO, bypassing the method cache.
The result is stored as a _PyStackRef in `out`. It never set an exception.
Returns -1 if there was an error, 0 if the name was not found, and 1 if
EXPORT_FUNC(PyModule_GetName)
EXPORT_FUNC(PyModule_GetNameObject)
EXPORT_FUNC(PyModule_GetState)
+EXPORT_FUNC(PyModule_GetState_DuringGC)
EXPORT_FUNC(PyModule_GetStateSize)
EXPORT_FUNC(PyModule_GetToken)
+EXPORT_FUNC(PyModule_GetToken_DuringGC)
EXPORT_FUNC(PyModule_New)
EXPORT_FUNC(PyModule_NewObject)
EXPORT_FUNC(PyModule_SetDocString)
EXPORT_FUNC(PyObject_GetOptionalAttr)
EXPORT_FUNC(PyObject_GetOptionalAttrString)
EXPORT_FUNC(PyObject_GetTypeData)
+EXPORT_FUNC(PyObject_GetTypeData_DuringGC)
EXPORT_FUNC(PyObject_HasAttr)
EXPORT_FUNC(PyObject_HasAttrString)
EXPORT_FUNC(PyObject_HasAttrStringWithError)
EXPORT_FUNC(PyType_GenericAlloc)
EXPORT_FUNC(PyType_GenericNew)
EXPORT_FUNC(PyType_GetBaseByToken)
+EXPORT_FUNC(PyType_GetBaseByToken_DuringGC)
EXPORT_FUNC(PyType_GetFlags)
EXPORT_FUNC(PyType_GetFullyQualifiedName)
EXPORT_FUNC(PyType_GetModule)
+EXPORT_FUNC(PyType_GetModule_DuringGC)
EXPORT_FUNC(PyType_GetModuleByDef)
EXPORT_FUNC(PyType_GetModuleByToken)
+EXPORT_FUNC(PyType_GetModuleByToken_DuringGC)
EXPORT_FUNC(PyType_GetModuleName)
EXPORT_FUNC(PyType_GetModuleState)
+EXPORT_FUNC(PyType_GetModuleState_DuringGC)
EXPORT_FUNC(PyType_GetName)
EXPORT_FUNC(PyType_GetQualName)
EXPORT_FUNC(PyType_GetSlot)