gh-145921: Add "_DuringGC" functions for tp_traverse#145925
gh-145921: Add "_DuringGC" functions for tp_traverse#145925encukou wants to merge 5 commits intopython:mainfrom
Conversation
There are newly documented restrictions on tp_traverse:
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.
* Add several functions that are guaranteed side-effect-free,
with a _DuringGC suffix.
* Use these in ctypes
* Consolidate tp_traverse docs in gcsupport.rst, moving unique
content from typeobj.rst there
zooba
left a comment
There was a problem hiding this comment.
Mostly doc suggestions, the actual change LGTM!
| The traversal function has a limitation: | ||
|
|
||
| .. warning:: |
There was a problem hiding this comment.
I don't think this kind of structure flows well in the docs? It probably works better to go straight to the warning, then in the paragraph after it replace "This means" with "The limitation on side effects means ..."
There was a problem hiding this comment.
Hmm, I'll push back here.
The red box looks like it stands on its own. Maybe it's my banner blindness that kicks in, but my brain doesn't immediately connect "The limitation on side effects" to the box above it, so I added the line to make it a part of the running text more obviously.
Doc/c-api/gcsupport.rst
Outdated
| effects may start having them in future versions, without warning. | ||
|
|
||
| For a list of safe functions, see a | ||
| :ref:`separate section <durniggc-functions>` below. |
There was a problem hiding this comment.
| :ref:`separate section <durniggc-functions>` below. | |
| :ref:`separate section <duringgc-functions>` below. |
If this didn't trigger a build warning, maybe you've got other typos?
There was a problem hiding this comment.
Also, I prefer to name the section even when it's linked, so that someone can find it or recognise it later without actually having to click the link.
There was a problem hiding this comment.
Thanks for the catch!
The other typo is in the link target itself ;)
| .. note:: | ||
|
|
||
| The :c:member:`~PyTypeObject.tp_traverse` function can be called from any | ||
| thread. |
There was a problem hiding this comment.
Should we mention something about "though all other Python threads are guaranteed to be blocked at the time" here?
There was a problem hiding this comment.
AFAIK that's an implementation detail some people want to get rid of.
Doc/c-api/gcsupport.rst
Outdated
| Note that these functions may fail (return ``NULL`` or -1). | ||
| Only creating and setting the exception is suppressed. |
There was a problem hiding this comment.
If they fail during tp_traverse, should we be bailing out as quickly as possible? What value should we return?
There was a problem hiding this comment.
That's a good question; honestly I'm not sure about the intentions behind tp_traverse's result.
@pablogsal: would it be correct to say tp_traverse should return zero on recoverable errors, including being unable to visit some objects?
Currently gc.c ignores errors*, but non-zero a result makes VISIT return early; gc_free_threading.c calls PyErr_NoMemory if traversal fails.
* except _PyGC_GetReferrers (gc.get_referrers) which instead assumes that tp_traverse sets an exception on failure -- that's wrong, right?
Doc/c-api/gcsupport.rst
Outdated
| Note that these functions may fail (return ``NULL`` or -1). | ||
| Only creating and setting the exception is suppressed. |
There was a problem hiding this comment.
| Note that these functions may fail (return ``NULL`` or -1). | |
| Only creating and setting the exception is suppressed. | |
| Note that these functions may fail (return ``NULL`` or -1), | |
| but no exception is set and no error information is available. |
Slight rephrasing to be a little less ambiguous, though I'm not totally committed to that wording - you might have something better. There are a few other places this phrase appears below
There was a problem hiding this comment.
With additional info on error handling, it makes sense to only have this in one place.
Modules/_testmultiphase.c
Outdated
| PyObject *m = PyType_GetModuleByDef(Py_TYPE(self), &def_nonmodule); | ||
| assert(PyErr_Occurred()); | ||
| assert(m == NULL); | ||
| return NULL; |
There was a problem hiding this comment.
Returning m is probably the easiest way to avoid those warnings, and to avoid an actual refleak if the function passes but assertions are disabled.
There are newly documented restrictions on tp_traverse:
📚 Documentation preview 📚: https://cpython-previews--145925.org.readthedocs.build/