-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix itertools.count in free-threading mode
Thread safety in count_next. count_next has two modes. slow mode (obj->cnt set to PY_SSIZE_T_MAX), which now uses the object mutex (only if GIL is disabled) and fast mode, which is either simple cnt++ if GIL is enabled, or uses atomic_compare_exchange if GIL is disabled.
- Loading branch information
Arnon Yaari
committed
May 20, 2024
1 parent
bf17986
commit 0813cc2
Showing
3 changed files
with
60 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
#include "Python.h" | ||
#include "pycore_call.h" // _PyObject_CallNoArgs() | ||
#include "pycore_ceval.h" // _PyEval_GetBuiltin() | ||
#include "pycore_long.h" // _PyLong_GetZero() | ||
#include "pycore_moduleobject.h" // _PyModule_GetState() | ||
#include "pycore_typeobject.h" // _PyType_GetModuleState() | ||
#include "pycore_object.h" // _PyObject_GC_TRACK() | ||
#include "pycore_tuple.h" // _PyTuple_ITEMS() | ||
#include "pycore_call.h" // _PyObject_CallNoArgs() | ||
#include "pycore_ceval.h" // _PyEval_GetBuiltin() | ||
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() | ||
#include "pycore_long.h" // _PyLong_GetZero() | ||
#include "pycore_moduleobject.h" // _PyModule_GetState() | ||
#include "pycore_typeobject.h" // _PyType_GetModuleState() | ||
#include "pycore_object.h" // _PyObject_GC_TRACK() | ||
#include "pycore_tuple.h" // _PyTuple_ITEMS() | ||
|
||
#include <stddef.h> // offsetof() | ||
#include <stddef.h> // offsetof() | ||
|
||
/* Itertools module written and maintained | ||
by Raymond D. Hettinger <[email protected]> | ||
|
@@ -3254,7 +3255,7 @@ fast_mode: when cnt an integer < PY_SSIZE_T_MAX and no step is specified. | |
assert(cnt != PY_SSIZE_T_MAX && long_cnt == NULL && long_step==PyLong(1)); | ||
Advances with: cnt += 1 | ||
When count hits Y_SSIZE_T_MAX, switch to slow_mode. | ||
When count hits PY_SSIZE_T_MAX, switch to slow_mode. | ||
slow_mode: when cnt == PY_SSIZE_T_MAX, step is not int(1), or cnt is a float. | ||
|
@@ -3386,7 +3387,7 @@ count_nextlong(countobject *lz) | |
|
||
long_cnt = lz->long_cnt; | ||
if (long_cnt == NULL) { | ||
/* Switch to slow_mode */ | ||
/* Switching from fast mode */ | ||
long_cnt = PyLong_FromSsize_t(PY_SSIZE_T_MAX); | ||
if (long_cnt == NULL) | ||
return NULL; | ||
|
@@ -3403,9 +3404,35 @@ count_nextlong(countobject *lz) | |
static PyObject * | ||
count_next(countobject *lz) | ||
{ | ||
if (lz->cnt == PY_SSIZE_T_MAX) | ||
return count_nextlong(lz); | ||
return PyLong_FromSsize_t(lz->cnt++); | ||
PyObject *returned; | ||
Py_ssize_t cnt; | ||
|
||
cnt = FT_ATOMIC_LOAD_SSIZE_RELAXED(lz->cnt); | ||
for (;;) { | ||
if (cnt == PY_SSIZE_T_MAX) { | ||
/* slow mode */ | ||
Py_BEGIN_CRITICAL_SECTION(lz); | ||
returned = count_nextlong(lz); | ||
Py_END_CRITICAL_SECTION(); | ||
return returned; | ||
} | ||
#ifdef Py_GIL_DISABLED | ||
/* thread-safe fast version (increment by one). | ||
* If lz->cnt changed between the pervious read and now, | ||
* that means another thread got in our way. In this case, | ||
* update cnt to new value of lz->cnt, and try again. | ||
* Otherwise, (no other thread updated lz->cnt), | ||
* atomically update lz->cnt with the incremented value and | ||
* then return cnt (the previous value) | ||
*/ | ||
if (_Py_atomic_compare_exchange_ssize(&lz->cnt, &cnt, cnt + 1)) { | ||
return PyLong_FromSsize_t(cnt); | ||
} | ||
#else | ||
/* fast mode when GIL is enabled */ | ||
return PyLong_FromSsize_t(lz->cnt++); | ||
#endif | ||
} | ||
} | ||
|
||
static PyObject * | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters