cpython/Lib/test/test_free_threading/test_pickle.py
Miss Islington (bot) 77cc4428a7
[3.15] gh-146452: Fix pickle segfault on concurrent mutation of dict in pickle (GH-146470) (#150292)
gh-146452: Fix pickle segfault on concurrent mutation of dict in pickle (GH-146470)
(cherry picked from commit e62a61177f)

Co-authored-by: Farhan Saif <fsaif@uic.edu>
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
2026-05-23 07:33:21 +00:00

44 lines
1.4 KiB
Python

import pickle
import threading
import unittest
from test.support import threading_helper
@threading_helper.requires_working_threading()
class TestPickleFreeThreading(unittest.TestCase):
def test_pickle_dumps_with_concurrent_dict_mutation(self):
# gh-146452: Pickling a dict while another thread mutates it
# used to segfault. batch_dict_exact() iterated dict items via
# PyDict_Next() which returns borrowed references, and a
# concurrent pop/replace could free the value before Py_INCREF
# got to it.
shared = {str(i): list(range(20)) for i in range(50)}
def dumper():
for _ in range(1000):
try:
pickle.dumps(shared)
except RuntimeError:
# "dictionary changed size during iteration" is expected
pass
def mutator():
for j in range(1000):
key = str(j % 50)
shared[key] = list(range(j % 20))
if j % 10 == 0:
shared.pop(key, None)
shared[key] = [j]
threads = []
for _ in range(10):
threads.append(threading.Thread(target=dumper))
threads.append(threading.Thread(target=mutator))
with threading_helper.start_threads(threads):
pass
if __name__ == "__main__":
unittest.main()