else:
self.assertEqual(d.index(element, start, stop), target)
+ # Test stop argument
+ for elem in d:
+ index = d.index(elem)
+ self.assertEqual(
+ index,
+ d.index(elem, 0),
+ )
+ self.assertEqual(
+ index,
+ d.index(elem, 0, len(d)),
+ )
+ self.assertEqual(
+ index,
+ d.index(elem, 0, len(d) + 100),
+ )
+
# Test large start argument
d = deque(range(0, 10000, 10))
for step in range(100):
threading_helper.run_concurrently([mutate, copy_loop])
+ def test_index_race_in_ac(self):
+ # gh-150750: There was a c_default specified as `Py_SIZE(self)`,
+ # it was used without a critical section.
+
+ d = deque(range(100))
+
+ def index():
+ for _ in range(10000):
+ try:
+ d.index(50)
+ except ValueError:
+ pass
+
+ def mutate():
+ for _ in range(10000):
+ d.append(0)
+ d.clear()
+ d.extend(range(100))
+ d.appendleft(-1)
+
+ threading_helper.run_concurrently(
+ [index, *[mutate for _ in range(3)]],
+ )
+
if __name__ == "__main__":
unittest.main()
--- /dev/null
+Fix a race condition in :meth:`collections.deque.index` with free-threading.
deque: dequeobject
value as v: object
start: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='0') = NULL
- stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='Py_SIZE(deque)') = NULL
+ stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='PY_SSIZE_T_MAX') = NULL
/
Return first index of value.
static PyObject *
deque_index_impl(dequeobject *deque, PyObject *v, Py_ssize_t start,
Py_ssize_t stop)
-/*[clinic end generated code: output=df45132753175ef9 input=90f48833a91e1743]*/
+/*[clinic end generated code: output=df45132753175ef9 input=1c3b19632cf3484f]*/
{
Py_ssize_t i, n;
PyObject *item;
Py_ssize_t index = deque->leftindex;
size_t start_state = deque->state;
int cmp;
+ Py_ssize_t size = Py_SIZE(deque);
if (start < 0) {
- start += Py_SIZE(deque);
+ start += size;
if (start < 0)
start = 0;
}
if (stop < 0) {
- stop += Py_SIZE(deque);
+ stop += size;
if (stop < 0)
stop = 0;
}
- if (stop > Py_SIZE(deque))
- stop = Py_SIZE(deque);
+ if (stop > size)
+ stop = size;
if (start > stop)
start = stop;
- assert(0 <= start && start <= stop && stop <= Py_SIZE(deque));
+ assert(0 <= start && start <= stop && stop <= size);
for (i=0 ; i < start - BLOCKLEN ; i += BLOCKLEN) {
b = b->rightlink;
PyObject *return_value = NULL;
PyObject *v;
Py_ssize_t start = 0;
- Py_ssize_t stop = Py_SIZE(deque);
+ Py_ssize_t stop = PY_SSIZE_T_MAX;
if (!_PyArg_CheckPositional("index", nargs, 1, 3)) {
goto exit;
exit:
return return_value;
}
-/*[clinic end generated code: output=b9d4d647c221cb9f input=a9049054013a1b77]*/
+/*[clinic end generated code: output=f5a388add99d3d15 input=a9049054013a1b77]*/
impl_by_reference = True
parse_by_reference = True
default_type = ()
- c_init_default = "<placeholder>" # overridden in pre_render(()
+ c_init_default = "<placeholder>" # overridden in pre_render()
converter = 'path_converter'
""", argname=argname, converter=self.converter, table=self.table)
[python start generated code]*/
-/*[python end generated code: output=da39a3ee5e6b4b0d input=d58f18bdf3bd3565]*/
+/*[python end generated code: output=da39a3ee5e6b4b0d input=ddbf3ac90a981122]*/
/*[clinic input]