Skip to content

Commit 3bedfff

Browse files
gh-145142: Make str.maketrans safe under free-threading
1 parent 6194a55 commit 3bedfff

File tree

3 files changed

+37
-1
lines changed

3 files changed

+37
-1
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import unittest
2+
from test.support import threading_helper
3+
4+
threading_helper.requires_working_threading(module=True)
5+
6+
7+
class TestUnicodeFreeThreading(unittest.TestCase):
8+
@threading_helper.reap_threads
9+
def test_maketrans_dict_concurrent_modification(self):
10+
number_of_iterations = 5
11+
number_of_attempts = 100
12+
for _ in range(number_of_iterations):
13+
d = {2000: 'a'}
14+
15+
def work_iterator(dct):
16+
for i in range(number_of_attempts):
17+
str.maketrans(dct)
18+
idx = 2000 + i
19+
dct[idx] = chr(idx % 16)
20+
dct.pop(idx, None)
21+
22+
threading_helper.run_concurrently(
23+
work_iterator,
24+
nthreads=5,
25+
args=(d,),
26+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash in free-threaded builds when str.maketrans() iterates over a
2+
dictionary that is concurrently modified. Wrap PyDict_Next iteration in a
3+
critical section.

Objects/unicodeobject.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13154,8 +13154,14 @@ unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z)
1315413154
"to maketrans it must be a dict");
1315513155
goto err;
1315613156
}
13157+
PyObject *items = PyDict_Items(x);
13158+
if(items == NULL) goto err;
13159+
Py_ssize_t n = PyList_GET_SIZE(items);
1315713160
/* copy entries into the new dict, converting string keys to int keys */
13158-
while (PyDict_Next(x, &i, &key, &value)) {
13161+
for (i = 0; i < n; i++) {
13162+
PyObject *pair = PyList_GET_ITEM(items, i);
13163+
key = PyTuple_GET_ITEM(pair, 0);
13164+
value = PyTuple_GET_ITEM(pair, 1);
1315913165
if (PyUnicode_Check(key)) {
1316013166
/* convert string keys to integer keys */
1316113167
PyObject *newkey;
@@ -13183,6 +13189,7 @@ unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z)
1318313189
goto err;
1318413190
}
1318513191
}
13192+
Py_DECREF(items);
1318613193
}
1318713194
return new;
1318813195
err:

0 commit comments

Comments
 (0)