Skip to content
Merged
Prev Previous commit
Next Next commit
Test for efficacy of fast sequence locking macro on str.join, both te…
…sts segfault or die with assertion failures >95% of the time without locking macro, pass reliably with locking macro
  • Loading branch information
MojoVampire committed May 22, 2024
commit 2b4fe199c77df4acad6525234142a9f6c27207c3
2 changes: 1 addition & 1 deletion Include/internal/pycore_critical_section.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ extern "C" {
// For performance, the argument *to* PySequence_Fast is provided to the
// macro, not the *result* of PySequence_Fast (which would require an extra
// test to determine if the lock must be held)
# define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) \
# define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) \
{ \
PyObject *_orig_seq = _PyObject_CAST(original); \
const bool _should_lock_cs = PyList_CheckExact(_orig_seq); \
Expand Down
74 changes: 74 additions & 0 deletions Lib/test/test_free_threading/test_str.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import sys
import unittest

from itertools import cycle
from threading import Thread
from unittest import TestCase

from test.support import threading_helper

@threading_helper.requires_working_threading()
class TestStr(TestCase):
def test_racing_join_extend(self):
'''Test joining a string being extended by another thread'''
l = []
OBJECT_COUNT = 100_000

def writer_func():
l.extend(map(str, range(OBJECT_COUNT, OBJECT_COUNT*2)))

def reader_func():
while True:
count = len(l)
''.join(l)
if count == OBJECT_COUNT:
break

writer = Thread(target=writer_func)
readers = []
for x in range(30):
Comment thread
MojoVampire marked this conversation as resolved.
Outdated
reader = Thread(target=reader_func)
readers.append(reader)
reader.start()

writer.start()
writer.join()
for reader in readers:
reader.join()

def test_racing_join_replace(self):
Comment thread
MojoVampire marked this conversation as resolved.
'''
Test joining a string of characters being replaced with ephemeral
strings by another thread.
'''
l = [*'abcdefg']
MAX_ORDINAL = 10_000

def writer_func():
for i, c in zip(cycle(range(len(l))),
map(chr, range(128, MAX_ORDINAL))):
l[i] = c
del l[:] # Empty list to tell readers to exit

def reader_func():
while True:
empty = not l
''.join(l)
if empty:
break
Comment thread
MojoVampire marked this conversation as resolved.
Outdated

writer = Thread(target=writer_func)
readers = []
for x in range(30):
reader = Thread(target=reader_func)
readers.append(reader)
reader.start()

writer.start()
writer.join()
for reader in readers:
reader.join()


if __name__ == "__main__":
unittest.main()