Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0841606

Browse files
[3.13]gh-119247: Add macros to use PySequence_Fast safely in free-threaded build (GH-119315) (#119419)
Add `Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST` and`Py_END_CRITICAL_SECTION_SEQUENCE_FAST` macros and update `str.join` to usethem. Also add a regression test that would crash reliably without thispatch.(cherry picked from commitbaf347d)Co-authored-by: Josh {*()} Rosenberg <26495692+MojoVampire@users.noreply.github.com>
1 parentcd39da7 commit0841606

File tree

4 files changed

+106
-3
lines changed

4 files changed

+106
-3
lines changed

‎Include/internal/pycore_critical_section.h‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,26 @@ extern "C" {
108108
_PyCriticalSection2_End(&_cs2); \
109109
}
110110

111+
// Specialized version of critical section locking to safely use
112+
// PySequence_Fast APIs without the GIL. For performance, the argument *to*
113+
// PySequence_Fast() is provided to the macro, not the *result* of
114+
// PySequence_Fast(), which would require an extra test to determine if the
115+
// lock must be acquired.
116+
# definePy_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) \
117+
{ \
118+
PyObject *_orig_seq = _PyObject_CAST(original); \
119+
const bool _should_lock_cs = PyList_CheckExact(_orig_seq); \
120+
_PyCriticalSection _cs; \
121+
if (_should_lock_cs) { \
122+
_PyCriticalSection_Begin(&_cs, &_orig_seq->ob_mutex); \
123+
}
124+
125+
# definePy_END_CRITICAL_SECTION_SEQUENCE_FAST() \
126+
if (_should_lock_cs) { \
127+
_PyCriticalSection_End(&_cs); \
128+
} \
129+
}
130+
111131
// Asserts that the mutex is locked. The mutex must be held by the
112132
// top-most critical section otherwise there's the possibility
113133
// that the mutex would be swalled out in some code paths.
@@ -137,6 +157,8 @@ extern "C" {
137157
# definePy_END_CRITICAL_SECTION()
138158
# definePy_BEGIN_CRITICAL_SECTION2(a,b)
139159
# definePy_END_CRITICAL_SECTION2()
160+
# definePy_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original)
161+
# definePy_END_CRITICAL_SECTION_SEQUENCE_FAST()
140162
# define_Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex)
141163
# define_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op)
142164
#endif/* !Py_GIL_DISABLED */
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
importsys
2+
importunittest
3+
4+
fromitertoolsimportcycle
5+
fromthreadingimportEvent,Thread
6+
fromunittestimportTestCase
7+
8+
fromtest.supportimportthreading_helper
9+
10+
@threading_helper.requires_working_threading()
11+
classTestStr(TestCase):
12+
deftest_racing_join_extend(self):
13+
'''Test joining a string being extended by another thread'''
14+
l= []
15+
ITERS=100
16+
READERS=10
17+
done_event=Event()
18+
defwriter_func():
19+
foriinrange(ITERS):
20+
l.extend(map(str,range(i)))
21+
l.clear()
22+
done_event.set()
23+
defreader_func():
24+
whilenotdone_event.is_set():
25+
''.join(l)
26+
writer=Thread(target=writer_func)
27+
readers= []
28+
forxinrange(READERS):
29+
reader=Thread(target=reader_func)
30+
readers.append(reader)
31+
reader.start()
32+
33+
writer.start()
34+
writer.join()
35+
forreaderinreaders:
36+
reader.join()
37+
38+
deftest_racing_join_replace(self):
39+
'''
40+
Test joining a string of characters being replaced with ephemeral
41+
strings by another thread.
42+
'''
43+
l= [*'abcdefg']
44+
MAX_ORDINAL=1_000
45+
READERS=10
46+
done_event=Event()
47+
48+
defwriter_func():
49+
fori,cinzip(cycle(range(len(l))),
50+
map(chr,range(128,MAX_ORDINAL))):
51+
l[i]=c
52+
done_event.set()
53+
54+
defreader_func():
55+
whilenotdone_event.is_set():
56+
''.join(l)
57+
''.join(l)
58+
''.join(l)
59+
''.join(l)
60+
61+
writer=Thread(target=writer_func)
62+
readers= []
63+
forxinrange(READERS):
64+
reader=Thread(target=reader_func)
65+
readers.append(reader)
66+
reader.start()
67+
68+
writer.start()
69+
writer.join()
70+
forreaderinreaders:
71+
reader.join()
72+
73+
74+
if__name__=="__main__":
75+
unittest.main()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Added ``Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST`` and
2+
``Py_END_CRITICAL_SECTION_SEQUENCE_FAST`` macros to make it possible to use
3+
PySequence_Fast APIs safely when free-threaded, and update str.join to work
4+
without the GIL using them.

‎Objects/unicodeobject.c‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4444
#include"pycore_bytesobject.h"// _PyBytes_Repeat()
4545
#include"pycore_ceval.h"// _PyEval_GetBuiltin()
4646
#include"pycore_codecs.h"// _PyCodec_Lookup()
47+
#include"pycore_critical_section.h"// Py_*_CRITICAL_SECTION_SEQUENCE_FAST
4748
#include"pycore_format.h"// F_LJUST
4849
#include"pycore_initconfig.h"// _PyStatus_OK()
4950
#include"pycore_interp.h"// PyInterpreterState.fs_codec
@@ -9559,13 +9560,14 @@ PyUnicode_Join(PyObject *separator, PyObject *seq)
95599560
returnNULL;
95609561
}
95619562

9562-
/* NOTE: the following code can't call back into Python code,
9563-
* so we are sure that fseq won't be mutated.
9564-
*/
9563+
Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(seq);
95659564

95669565
items=PySequence_Fast_ITEMS(fseq);
95679566
seqlen=PySequence_Fast_GET_SIZE(fseq);
95689567
res=_PyUnicode_JoinArray(separator,items,seqlen);
9568+
9569+
Py_END_CRITICAL_SECTION_SEQUENCE_FAST();
9570+
95699571
Py_DECREF(fseq);
95709572
returnres;
95719573
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp