Skip to content

Commit faadd30

Browse files
committed
Add a test
1 parent 1d3d02e commit faadd30

1 file changed

Lines changed: 41 additions & 0 deletions

File tree

Lib/test/test_thread.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,47 @@ def func():
345345
handle = thread.start_joinable_thread(func, handle=None)
346346
handle.join()
347347

348+
class StartNewThreadKwargsRace(unittest.TestCase):
349+
350+
@unittest.skipUnless(support.Py_GIL_DISABLED, "GIL must be disabled")
351+
def test_dict_growsup_when_thread_start(self):
352+
# See gh-149816 - (62) Concurrent kwargs growth causes heap overwrite
353+
# This test is meant to be run under a free-threaded build, where the GIL is
354+
# disabled and concurrent mutations of the same dict can cause heap
355+
# corruption.
356+
results = []
357+
def mutator(shared, stop, prefix, burst):
358+
i = 0
359+
while not stop.locked():
360+
for _ in range(burst):
361+
shared[f"{prefix}_{i}"] = i
362+
i += 1
363+
time.sleep(0)
364+
results.append(prefix)
365+
366+
def nop(i, **kwargs):
367+
pass
368+
369+
stop = thread.lock()
370+
shared = {f"base_{i}": i for i in range(20000)}
371+
n = 4
372+
for i in range(n):
373+
args=(shared, stop, f"dynamic_{i}", 1000)
374+
thread.start_new_thread(mutator, args)
375+
376+
snt = 32
377+
for i in range(snt):
378+
try:
379+
thread.start_new_thread(nop, (i,), shared)
380+
except RuntimeError:
381+
break
382+
383+
stop.acquire()
384+
# wait for all mutator threads stop.
385+
wait_t = time.monotonic()
386+
while len(results) < n and time.monotonic() - wait_t < 5.0:
387+
time.sleep(0.1)
388+
348389

349390
class Barrier:
350391
def __init__(self, num_threads):

0 commit comments

Comments
 (0)