Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix a data race in :meth:`!__reduce__` of a shared :class:`types.GenericAlias`
iterator under the :term:`free-threaded build`. Follow-up to
:gh:`154043`, which only covered iteration itself.
20 changes: 18 additions & 2 deletions Objects/genericaliasobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -997,8 +997,19 @@ ga_iter_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
* call must be before access of iterator pointers.
* see issue #101765 */

if (gi->obj)
return Py_BuildValue("N(O)", iter, gi->obj);
/* ga_iternext takes gi->obj out with an atomic exchange and then drops the
* reference, so a plain read here is not just a data race: the two reads of
* gi->obj below could straddle that exchange and hand Py_BuildValue a
* pointer whose last reference is already gone. Take a strong reference
* once instead. Racing with next() may legitimately observe either the
* object or the exhausted iterator; both reductions are correct. */
#ifdef Py_GIL_DISABLED
PyObject *obj = _Py_XGetRef(&gi->obj);
#else
PyObject *obj = Py_XNewRef(gi->obj);
#endif
if (obj != NULL)
return Py_BuildValue("N(N)", iter, obj);
else
return Py_BuildValue("N(())", iter);
}
Expand Down Expand Up @@ -1030,6 +1041,11 @@ ga_iter(PyObject *self) {
return NULL;
}
gi->obj = Py_NewRef(self);
#ifdef Py_GIL_DISABLED
/* _Py_XGetRef in ga_iter_reduce needs the stored object to be flagged, or
* its try-incref cannot succeed from another thread and it would spin. */
_PyObject_SetMaybeWeakref(self);
#endif
PyObject_GC_Track(gi);
return (PyObject *)gi;
}
Expand Down
Loading