Skip to content

Commit 3b8997b

Browse files
Update changes
1 parent 45bb965 commit 3b8997b

File tree

4 files changed

+31
-42
lines changed

4 files changed

+31
-42
lines changed

Lib/test/test_free_threading/test_maketrans_threading.py

Lines changed: 0 additions & 26 deletions
This file was deleted.

Lib/test/test_free_threading/test_str.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@ def reader_func():
6969
for reader in readers:
7070
reader.join()
7171

72+
def test_maketrans_dict_concurrent_modification(self):
73+
for _ in range(5):
74+
d = {2000: 'a'}
75+
76+
def work(dct):
77+
for i in range(100):
78+
str.maketrans(dct)
79+
dct[2000 + i] = chr(i % 16)
80+
dct.pop(2000 + i, None)
81+
82+
threading_helper.run_concurrently(
83+
work,
84+
nthreads=5,
85+
args=(d,),
86+
)
87+
7288

7389
if __name__ == "__main__":
7490
unittest.main()
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
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.
1+
Fix a crash in the free-threaded build when the dictionary argument to
2+
:meth:`str.maketrans()` is concurrently modified.

Objects/unicodeobject.c

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13154,42 +13154,42 @@ 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);
1316013157
/* copy entries into the new dict, converting string keys to int keys */
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);
13158+
Py_BEGIN_CRITICAL_SECTION(x);
13159+
while (PyDict_Next(x, &i, &key, &value)) {
1316513160
if (PyUnicode_Check(key)) {
1316613161
/* convert string keys to integer keys */
1316713162
PyObject *newkey;
1316813163
if (PyUnicode_GET_LENGTH(key) != 1) {
1316913164
PyErr_SetString(PyExc_ValueError, "string keys in translate "
1317013165
"table must be of length 1");
13171-
goto err;
13166+
goto err_in_cs;
1317213167
}
1317313168
kind = PyUnicode_KIND(key);
1317413169
data = PyUnicode_DATA(key);
1317513170
newkey = PyLong_FromLong(PyUnicode_READ(kind, data, 0));
1317613171
if (!newkey)
13177-
goto err;
13172+
goto err_in_cs;
1317813173
res = PyDict_SetItem(new, newkey, value);
1317913174
Py_DECREF(newkey);
1318013175
if (res < 0)
13181-
goto err;
13176+
goto err_in_cs;
1318213177
} else if (PyLong_Check(key)) {
1318313178
/* just keep integer keys */
1318413179
if (PyDict_SetItem(new, key, value) < 0)
13185-
goto err;
13180+
goto err_in_cs;
1318613181
} else {
1318713182
PyErr_SetString(PyExc_TypeError, "keys in translate table must "
1318813183
"be strings or integers");
13189-
goto err;
13184+
goto err_in_cs;
1319013185
}
1319113186
}
13192-
Py_DECREF(items);
13187+
goto done;
13188+
err_in_cs:
13189+
Py_CLEAR(new);
13190+
done:
13191+
Py_END_CRITICAL_SECTION();
13192+
return new;
1319313193
}
1319413194
return new;
1319513195
err:

0 commit comments

Comments
 (0)