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
16 changes: 16 additions & 0 deletions Lib/test/test_deque.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,22 @@ def test_index(self):
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):
Expand Down
29 changes: 29 additions & 0 deletions Lib/test/test_free_threading/test_collections.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unittest
from collections import deque
from copy import copy
from threading import Barrier
from test.support import threading_helper

threading_helper.requires_working_threading(module=True)
Expand All @@ -24,6 +25,34 @@ def copy_loop():

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))
N_MUT = 3
barrier = Barrier(1 + N_MUT)

def index():
barrier.wait()
for _ in range(10000):
try:
d.index(50)
except ValueError:
pass

def mutate():
barrier.wait()
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(N_MUT)]],
)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a race condition in :meth:`collections.deque.index` with free-threading.
15 changes: 8 additions & 7 deletions Modules/_collectionsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ _collections.deque.index as deque_index
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.
Expand All @@ -1262,30 +1262,31 @@ Raises ValueError if the value is not present.
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;
block *b = deque->leftblock;
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;
Expand Down
4 changes: 2 additions & 2 deletions Modules/clinic/_collectionsmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3123,7 +3123,7 @@ class path_t_converter(CConverter):
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'

Expand Down Expand Up @@ -3266,7 +3266,7 @@ class confname_converter(CConverter):
""", 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]

Expand Down
Loading