Skip to content

Commit 7245411

Browse files
sobolevnmiss-islington
authored andcommitted
gh-150750: Fix a race condition in deque.index with free-threading (GH-150779)
(cherry picked from commit d83d50b) Co-authored-by: sobolevn <mail@sobolevn.me>
1 parent 253c518 commit 7245411

6 files changed

Lines changed: 53 additions & 11 deletions

File tree

Lib/test/test_deque.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,22 @@ def test_index(self):
287287
else:
288288
self.assertEqual(d.index(element, start, stop), target)
289289

290+
# Test stop argument
291+
for elem in d:
292+
index = d.index(elem)
293+
self.assertEqual(
294+
index,
295+
d.index(elem, 0),
296+
)
297+
self.assertEqual(
298+
index,
299+
d.index(elem, 0, len(d)),
300+
)
301+
self.assertEqual(
302+
index,
303+
d.index(elem, 0, len(d) + 100),
304+
)
305+
290306
# Test large start argument
291307
d = deque(range(0, 10000, 10))
292308
for step in range(100):

Lib/test/test_free_threading/test_collections.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,30 @@ def copy_loop():
2424

2525
threading_helper.run_concurrently([mutate, copy_loop])
2626

27+
def test_index_race_in_ac(self):
28+
# gh-150750: There was a c_default specified as `Py_SIZE(self)`,
29+
# it was used without a critical section.
30+
31+
d = deque(range(100))
32+
33+
def index():
34+
for _ in range(10000):
35+
try:
36+
d.index(50)
37+
except ValueError:
38+
pass
39+
40+
def mutate():
41+
for _ in range(10000):
42+
d.append(0)
43+
d.clear()
44+
d.extend(range(100))
45+
d.appendleft(-1)
46+
47+
threading_helper.run_concurrently(
48+
[index, *[mutate for _ in range(3)]],
49+
)
50+
2751

2852
if __name__ == "__main__":
2953
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a race condition in :meth:`collections.deque.index` with free-threading.

Modules/_collectionsmodule.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,7 +1251,7 @@ _collections.deque.index as deque_index
12511251
deque: dequeobject
12521252
value as v: object
12531253
start: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='0') = NULL
1254-
stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='Py_SIZE(deque)') = NULL
1254+
stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='PY_SSIZE_T_MAX') = NULL
12551255
/
12561256
12571257
Return first index of value.
@@ -1262,30 +1262,31 @@ Raises ValueError if the value is not present.
12621262
static PyObject *
12631263
deque_index_impl(dequeobject *deque, PyObject *v, Py_ssize_t start,
12641264
Py_ssize_t stop)
1265-
/*[clinic end generated code: output=df45132753175ef9 input=90f48833a91e1743]*/
1265+
/*[clinic end generated code: output=df45132753175ef9 input=1c3b19632cf3484f]*/
12661266
{
12671267
Py_ssize_t i, n;
12681268
PyObject *item;
12691269
block *b = deque->leftblock;
12701270
Py_ssize_t index = deque->leftindex;
12711271
size_t start_state = deque->state;
12721272
int cmp;
1273+
Py_ssize_t size = Py_SIZE(deque);
12731274

12741275
if (start < 0) {
1275-
start += Py_SIZE(deque);
1276+
start += size;
12761277
if (start < 0)
12771278
start = 0;
12781279
}
12791280
if (stop < 0) {
1280-
stop += Py_SIZE(deque);
1281+
stop += size;
12811282
if (stop < 0)
12821283
stop = 0;
12831284
}
1284-
if (stop > Py_SIZE(deque))
1285-
stop = Py_SIZE(deque);
1285+
if (stop > size)
1286+
stop = size;
12861287
if (start > stop)
12871288
start = stop;
1288-
assert(0 <= start && start <= stop && stop <= Py_SIZE(deque));
1289+
assert(0 <= start && start <= stop && stop <= size);
12891290

12901291
for (i=0 ; i < start - BLOCKLEN ; i += BLOCKLEN) {
12911292
b = b->rightlink;

Modules/clinic/_collectionsmodule.c.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/posixmodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3123,7 +3123,7 @@ class path_t_converter(CConverter):
31233123
impl_by_reference = True
31243124
parse_by_reference = True
31253125
default_type = ()
3126-
c_init_default = "<placeholder>" # overridden in pre_render(()
3126+
c_init_default = "<placeholder>" # overridden in pre_render()
31273127

31283128
converter = 'path_converter'
31293129

@@ -3266,7 +3266,7 @@ class confname_converter(CConverter):
32663266
""", argname=argname, converter=self.converter, table=self.table)
32673267

32683268
[python start generated code]*/
3269-
/*[python end generated code: output=da39a3ee5e6b4b0d input=d58f18bdf3bd3565]*/
3269+
/*[python end generated code: output=da39a3ee5e6b4b0d input=ddbf3ac90a981122]*/
32703270

32713271
/*[clinic input]
32723272

0 commit comments

Comments
 (0)