Skip to content

Commit 89c220b

Browse files
authored
gh-133296: Publicly expose critical section API that accepts PyMutex (gh-135899)
This makes the following APIs public: * `Py_BEGIN_CRITICAL_SECTION_MUTEX(mutex),` * `Py_BEGIN_CRITICAL_SECTION2_MUTEX(mutex1, mutex2)` * `void PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *mutex)` * `void PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *mutex1, PyMutex *mutex2)` The macros are identical to the corresponding `Py_BEGIN_CRITICAL_SECTION` and `Py_BEGIN_CRITICAL_SECTION2` macros (e.g., they include braces), but they accept a `PyMutex` instead of an object. The new macros are still paired with the existing END macros (`Py_END_CRITICAL_SECTION`, `Py_END_CRITICAL_SECTION2`).
1 parent f183996 commit 89c220b

File tree

8 files changed

+96
-15
lines changed

8 files changed

+96
-15
lines changed

Doc/c-api/init.rst

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2306,6 +2306,12 @@ is resumed, and its locks reacquired. This means the critical section API
23062306
provides weaker guarantees than traditional locks -- they are useful because
23072307
their behavior is similar to the :term:`GIL`.
23082308
2309+
Variants that accept :c:type:`PyMutex` pointers rather than Python objects are also
2310+
available. Use these variants to start a critical section in a situation where
2311+
there is no :c:type:`PyObject` -- for example, when working with a C type that
2312+
does not extend or wrap :c:type:`PyObject` but still needs to call into the C
2313+
API in a manner that might lead to deadlocks.
2314+
23092315
The functions and structs used by the macros are exposed for cases
23102316
where C macros are not available. They should only be used as in the
23112317
given macro expansions. Note that the sizes and contents of the structures may
@@ -2351,6 +2357,23 @@ code triggered by the finalizer blocks and calls :c:func:`PyEval_SaveThread`.
23512357
23522358
.. versionadded:: 3.13
23532359
2360+
.. c:macro:: Py_BEGIN_CRITICAL_SECTION_MUTEX(m)
2361+
2362+
Locks the mutex *m* and begins a critical section.
2363+
2364+
In the free-threaded build, this macro expands to::
2365+
2366+
{
2367+
PyCriticalSection _py_cs;
2368+
PyCriticalSection_BeginMutex(&_py_cs, m)
2369+
2370+
Note that unlike :c:macro:`Py_BEGIN_CRITICAL_SECTION`, there is no cast for
2371+
the argument of the macro - it must be a :c:type:`PyMutex` pointer.
2372+
2373+
On the default build, this macro expands to ``{``.
2374+
2375+
.. versionadded:: next
2376+
23542377
.. c:macro:: Py_END_CRITICAL_SECTION()
23552378
23562379
Ends the critical section and releases the per-object lock.
@@ -2380,6 +2403,23 @@ code triggered by the finalizer blocks and calls :c:func:`PyEval_SaveThread`.
23802403
23812404
.. versionadded:: 3.13
23822405
2406+
.. c:macro:: Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2)
2407+
2408+
Locks the mutexes *m1* and *m2* and begins a critical section.
2409+
2410+
In the free-threaded build, this macro expands to::
2411+
2412+
{
2413+
PyCriticalSection2 _py_cs2;
2414+
PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)
2415+
2416+
Note that unlike :c:macro:`Py_BEGIN_CRITICAL_SECTION2`, there is no cast for
2417+
the arguments of the macro - they must be :c:type:`PyMutex` pointers.
2418+
2419+
On the default build, this macro expands to ``{``.
2420+
2421+
.. versionadded:: next
2422+
23832423
.. c:macro:: Py_END_CRITICAL_SECTION2()
23842424
23852425
Ends the critical section and releases the per-object locks.

Include/cpython/critical_section.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,32 @@ typedef struct PyCriticalSection2 PyCriticalSection2;
7373
PyAPI_FUNC(void)
7474
PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op);
7575

76+
PyAPI_FUNC(void)
77+
PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m);
78+
7679
PyAPI_FUNC(void)
7780
PyCriticalSection_End(PyCriticalSection *c);
7881

7982
PyAPI_FUNC(void)
8083
PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b);
8184

85+
PyAPI_FUNC(void)
86+
PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2);
87+
8288
PyAPI_FUNC(void)
8389
PyCriticalSection2_End(PyCriticalSection2 *c);
8490

8591
#ifndef Py_GIL_DISABLED
8692
# define Py_BEGIN_CRITICAL_SECTION(op) \
8793
{
94+
# define Py_BEGIN_CRITICAL_SECTION_MUTEX(mutex) \
95+
{
8896
# define Py_END_CRITICAL_SECTION() \
8997
}
9098
# define Py_BEGIN_CRITICAL_SECTION2(a, b) \
9199
{
100+
# define Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2) \
101+
{
92102
# define Py_END_CRITICAL_SECTION2() \
93103
}
94104
#else /* !Py_GIL_DISABLED */
@@ -118,6 +128,11 @@ struct PyCriticalSection2 {
118128
PyCriticalSection _py_cs; \
119129
PyCriticalSection_Begin(&_py_cs, _PyObject_CAST(op))
120130

131+
# define Py_BEGIN_CRITICAL_SECTION_MUTEX(mutex) \
132+
{ \
133+
PyCriticalSection _py_cs; \
134+
PyCriticalSection_BeginMutex(&_py_cs, mutex)
135+
121136
# define Py_END_CRITICAL_SECTION() \
122137
PyCriticalSection_End(&_py_cs); \
123138
}
@@ -127,6 +142,11 @@ struct PyCriticalSection2 {
127142
PyCriticalSection2 _py_cs2; \
128143
PyCriticalSection2_Begin(&_py_cs2, _PyObject_CAST(a), _PyObject_CAST(b))
129144

145+
# define Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2) \
146+
{ \
147+
PyCriticalSection2 _py_cs2; \
148+
PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)
149+
130150
# define Py_END_CRITICAL_SECTION2() \
131151
PyCriticalSection2_End(&_py_cs2); \
132152
}

Include/internal/pycore_critical_section.h

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,6 @@ extern "C" {
2121
#define _Py_CRITICAL_SECTION_MASK 0x3
2222

2323
#ifdef Py_GIL_DISABLED
24-
# define Py_BEGIN_CRITICAL_SECTION_MUT(mutex) \
25-
{ \
26-
PyCriticalSection _py_cs; \
27-
_PyCriticalSection_BeginMutex(&_py_cs, mutex)
28-
29-
# define Py_BEGIN_CRITICAL_SECTION2_MUT(m1, m2) \
30-
{ \
31-
PyCriticalSection2 _py_cs2; \
32-
_PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)
33-
3424
// Specialized version of critical section locking to safely use
3525
// PySequence_Fast APIs without the GIL. For performance, the argument *to*
3626
// PySequence_Fast() is provided to the macro, not the *result* of
@@ -75,8 +65,6 @@ extern "C" {
7565

7666
#else /* !Py_GIL_DISABLED */
7767
// The critical section APIs are no-ops with the GIL.
78-
# define Py_BEGIN_CRITICAL_SECTION_MUT(mut) {
79-
# define Py_BEGIN_CRITICAL_SECTION2_MUT(m1, m2) {
8068
# define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) {
8169
# define Py_END_CRITICAL_SECTION_SEQUENCE_FAST() }
8270
# define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex)
@@ -119,6 +107,7 @@ _PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m)
119107
_PyCriticalSection_BeginSlow(c, m);
120108
}
121109
}
110+
#define PyCriticalSection_BeginMutex _PyCriticalSection_BeginMutex
122111

123112
static inline void
124113
_PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op)
@@ -194,6 +183,7 @@ _PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
194183
_PyCriticalSection2_BeginSlow(c, m1, m2, 0);
195184
}
196185
}
186+
#define PyCriticalSection2_BeginMutex _PyCriticalSection2_BeginMutex
197187

198188
static inline void
199189
_PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
New variants for the critical section API that accept one or two
2+
:c:type:`PyMutex` pointers rather than :c:type:`PyObject` instances are now
3+
public in the non-limited C API.

Modules/_ctypes/ctypes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ typedef struct {
431431
visible to other threads before the `dict_final` bit is set.
432432
*/
433433

434-
#define STGINFO_LOCK(stginfo) Py_BEGIN_CRITICAL_SECTION_MUT(&(stginfo)->mutex)
434+
#define STGINFO_LOCK(stginfo) Py_BEGIN_CRITICAL_SECTION_MUTEX(&(stginfo)->mutex)
435435
#define STGINFO_UNLOCK() Py_END_CRITICAL_SECTION()
436436

437437
static inline uint8_t

Modules/_testcapimodule.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2418,6 +2418,16 @@ test_critical_sections(PyObject *module, PyObject *Py_UNUSED(args))
24182418
Py_BEGIN_CRITICAL_SECTION2(module, module);
24192419
Py_END_CRITICAL_SECTION2();
24202420

2421+
#ifdef Py_GIL_DISABLED
2422+
// avoid unused variable compiler warning on GIL-enabled build
2423+
PyMutex mut = {0};
2424+
Py_BEGIN_CRITICAL_SECTION_MUTEX(&mut);
2425+
Py_END_CRITICAL_SECTION();
2426+
2427+
Py_BEGIN_CRITICAL_SECTION2_MUTEX(&mut, &mut);
2428+
Py_END_CRITICAL_SECTION2();
2429+
#endif
2430+
24212431
Py_RETURN_NONE;
24222432
}
24232433

Objects/typeobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,11 @@ class object "PyObject *" "&PyBaseObject_Type"
7373
// while the stop-the-world mechanism is active. The slots and flags are read
7474
// in many places without holding a lock and without atomics.
7575
#define TYPE_LOCK &PyInterpreterState_Get()->types.mutex
76-
#define BEGIN_TYPE_LOCK() Py_BEGIN_CRITICAL_SECTION_MUT(TYPE_LOCK)
76+
#define BEGIN_TYPE_LOCK() Py_BEGIN_CRITICAL_SECTION_MUTEX(TYPE_LOCK)
7777
#define END_TYPE_LOCK() Py_END_CRITICAL_SECTION()
7878

7979
#define BEGIN_TYPE_DICT_LOCK(d) \
80-
Py_BEGIN_CRITICAL_SECTION2_MUT(TYPE_LOCK, &_PyObject_CAST(d)->ob_mutex)
80+
Py_BEGIN_CRITICAL_SECTION2_MUTEX(TYPE_LOCK, &_PyObject_CAST(d)->ob_mutex)
8181

8282
#define END_TYPE_DICT_LOCK() Py_END_CRITICAL_SECTION2()
8383

Python/critical_section.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,15 @@ PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op)
130130
#endif
131131
}
132132

133+
#undef PyCriticalSection_BeginMutex
134+
void
135+
PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m)
136+
{
137+
#ifdef Py_GIL_DISABLED
138+
_PyCriticalSection_BeginMutex(c, m);
139+
#endif
140+
}
141+
133142
#undef PyCriticalSection_End
134143
void
135144
PyCriticalSection_End(PyCriticalSection *c)
@@ -148,6 +157,15 @@ PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b)
148157
#endif
149158
}
150159

160+
#undef PyCriticalSection2_BeginMutex
161+
void
162+
PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
163+
{
164+
#ifdef Py_GIL_DISABLED
165+
_PyCriticalSection2_BeginMutex(c, m1, m2);
166+
#endif
167+
}
168+
151169
#undef PyCriticalSection2_End
152170
void
153171
PyCriticalSection2_End(PyCriticalSection2 *c)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy