mirror of
https://github.com/python/cpython.git
synced 2026-06-06 01:41:04 +00:00
44 lines
1.4 KiB
Python
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()
|