mirror of
https://github.com/python/cpython.git
synced 2025-11-01 06:01:29 +00:00
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`).
This commit is contained in:
parent
f183996eb7
commit
89c220b93c
8 changed files with 96 additions and 15 deletions
|
|
@ -2306,6 +2306,12 @@ is resumed, and its locks reacquired. This means the critical section API
|
||||||
provides weaker guarantees than traditional locks -- they are useful because
|
provides weaker guarantees than traditional locks -- they are useful because
|
||||||
their behavior is similar to the :term:`GIL`.
|
their behavior is similar to the :term:`GIL`.
|
||||||
|
|
||||||
|
Variants that accept :c:type:`PyMutex` pointers rather than Python objects are also
|
||||||
|
available. Use these variants to start a critical section in a situation where
|
||||||
|
there is no :c:type:`PyObject` -- for example, when working with a C type that
|
||||||
|
does not extend or wrap :c:type:`PyObject` but still needs to call into the C
|
||||||
|
API in a manner that might lead to deadlocks.
|
||||||
|
|
||||||
The functions and structs used by the macros are exposed for cases
|
The functions and structs used by the macros are exposed for cases
|
||||||
where C macros are not available. They should only be used as in the
|
where C macros are not available. They should only be used as in the
|
||||||
given macro expansions. Note that the sizes and contents of the structures may
|
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`.
|
||||||
|
|
||||||
.. versionadded:: 3.13
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
|
.. c:macro:: Py_BEGIN_CRITICAL_SECTION_MUTEX(m)
|
||||||
|
|
||||||
|
Locks the mutex *m* and begins a critical section.
|
||||||
|
|
||||||
|
In the free-threaded build, this macro expands to::
|
||||||
|
|
||||||
|
{
|
||||||
|
PyCriticalSection _py_cs;
|
||||||
|
PyCriticalSection_BeginMutex(&_py_cs, m)
|
||||||
|
|
||||||
|
Note that unlike :c:macro:`Py_BEGIN_CRITICAL_SECTION`, there is no cast for
|
||||||
|
the argument of the macro - it must be a :c:type:`PyMutex` pointer.
|
||||||
|
|
||||||
|
On the default build, this macro expands to ``{``.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
.. c:macro:: Py_END_CRITICAL_SECTION()
|
.. c:macro:: Py_END_CRITICAL_SECTION()
|
||||||
|
|
||||||
Ends the critical section and releases the per-object lock.
|
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`.
|
||||||
|
|
||||||
.. versionadded:: 3.13
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
|
.. c:macro:: Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2)
|
||||||
|
|
||||||
|
Locks the mutexes *m1* and *m2* and begins a critical section.
|
||||||
|
|
||||||
|
In the free-threaded build, this macro expands to::
|
||||||
|
|
||||||
|
{
|
||||||
|
PyCriticalSection2 _py_cs2;
|
||||||
|
PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)
|
||||||
|
|
||||||
|
Note that unlike :c:macro:`Py_BEGIN_CRITICAL_SECTION2`, there is no cast for
|
||||||
|
the arguments of the macro - they must be :c:type:`PyMutex` pointers.
|
||||||
|
|
||||||
|
On the default build, this macro expands to ``{``.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
.. c:macro:: Py_END_CRITICAL_SECTION2()
|
.. c:macro:: Py_END_CRITICAL_SECTION2()
|
||||||
|
|
||||||
Ends the critical section and releases the per-object locks.
|
Ends the critical section and releases the per-object locks.
|
||||||
|
|
|
||||||
|
|
@ -73,22 +73,32 @@ typedef struct PyCriticalSection2 PyCriticalSection2;
|
||||||
PyAPI_FUNC(void)
|
PyAPI_FUNC(void)
|
||||||
PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op);
|
PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op);
|
||||||
|
|
||||||
|
PyAPI_FUNC(void)
|
||||||
|
PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m);
|
||||||
|
|
||||||
PyAPI_FUNC(void)
|
PyAPI_FUNC(void)
|
||||||
PyCriticalSection_End(PyCriticalSection *c);
|
PyCriticalSection_End(PyCriticalSection *c);
|
||||||
|
|
||||||
PyAPI_FUNC(void)
|
PyAPI_FUNC(void)
|
||||||
PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b);
|
PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b);
|
||||||
|
|
||||||
|
PyAPI_FUNC(void)
|
||||||
|
PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2);
|
||||||
|
|
||||||
PyAPI_FUNC(void)
|
PyAPI_FUNC(void)
|
||||||
PyCriticalSection2_End(PyCriticalSection2 *c);
|
PyCriticalSection2_End(PyCriticalSection2 *c);
|
||||||
|
|
||||||
#ifndef Py_GIL_DISABLED
|
#ifndef Py_GIL_DISABLED
|
||||||
# define Py_BEGIN_CRITICAL_SECTION(op) \
|
# define Py_BEGIN_CRITICAL_SECTION(op) \
|
||||||
{
|
{
|
||||||
|
# define Py_BEGIN_CRITICAL_SECTION_MUTEX(mutex) \
|
||||||
|
{
|
||||||
# define Py_END_CRITICAL_SECTION() \
|
# define Py_END_CRITICAL_SECTION() \
|
||||||
}
|
}
|
||||||
# define Py_BEGIN_CRITICAL_SECTION2(a, b) \
|
# define Py_BEGIN_CRITICAL_SECTION2(a, b) \
|
||||||
{
|
{
|
||||||
|
# define Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2) \
|
||||||
|
{
|
||||||
# define Py_END_CRITICAL_SECTION2() \
|
# define Py_END_CRITICAL_SECTION2() \
|
||||||
}
|
}
|
||||||
#else /* !Py_GIL_DISABLED */
|
#else /* !Py_GIL_DISABLED */
|
||||||
|
|
@ -118,6 +128,11 @@ struct PyCriticalSection2 {
|
||||||
PyCriticalSection _py_cs; \
|
PyCriticalSection _py_cs; \
|
||||||
PyCriticalSection_Begin(&_py_cs, _PyObject_CAST(op))
|
PyCriticalSection_Begin(&_py_cs, _PyObject_CAST(op))
|
||||||
|
|
||||||
|
# define Py_BEGIN_CRITICAL_SECTION_MUTEX(mutex) \
|
||||||
|
{ \
|
||||||
|
PyCriticalSection _py_cs; \
|
||||||
|
PyCriticalSection_BeginMutex(&_py_cs, mutex)
|
||||||
|
|
||||||
# define Py_END_CRITICAL_SECTION() \
|
# define Py_END_CRITICAL_SECTION() \
|
||||||
PyCriticalSection_End(&_py_cs); \
|
PyCriticalSection_End(&_py_cs); \
|
||||||
}
|
}
|
||||||
|
|
@ -127,6 +142,11 @@ struct PyCriticalSection2 {
|
||||||
PyCriticalSection2 _py_cs2; \
|
PyCriticalSection2 _py_cs2; \
|
||||||
PyCriticalSection2_Begin(&_py_cs2, _PyObject_CAST(a), _PyObject_CAST(b))
|
PyCriticalSection2_Begin(&_py_cs2, _PyObject_CAST(a), _PyObject_CAST(b))
|
||||||
|
|
||||||
|
# define Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2) \
|
||||||
|
{ \
|
||||||
|
PyCriticalSection2 _py_cs2; \
|
||||||
|
PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)
|
||||||
|
|
||||||
# define Py_END_CRITICAL_SECTION2() \
|
# define Py_END_CRITICAL_SECTION2() \
|
||||||
PyCriticalSection2_End(&_py_cs2); \
|
PyCriticalSection2_End(&_py_cs2); \
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,16 +21,6 @@ extern "C" {
|
||||||
#define _Py_CRITICAL_SECTION_MASK 0x3
|
#define _Py_CRITICAL_SECTION_MASK 0x3
|
||||||
|
|
||||||
#ifdef Py_GIL_DISABLED
|
#ifdef Py_GIL_DISABLED
|
||||||
# define Py_BEGIN_CRITICAL_SECTION_MUT(mutex) \
|
|
||||||
{ \
|
|
||||||
PyCriticalSection _py_cs; \
|
|
||||||
_PyCriticalSection_BeginMutex(&_py_cs, mutex)
|
|
||||||
|
|
||||||
# define Py_BEGIN_CRITICAL_SECTION2_MUT(m1, m2) \
|
|
||||||
{ \
|
|
||||||
PyCriticalSection2 _py_cs2; \
|
|
||||||
_PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)
|
|
||||||
|
|
||||||
// Specialized version of critical section locking to safely use
|
// Specialized version of critical section locking to safely use
|
||||||
// PySequence_Fast APIs without the GIL. For performance, the argument *to*
|
// PySequence_Fast APIs without the GIL. For performance, the argument *to*
|
||||||
// PySequence_Fast() is provided to the macro, not the *result* of
|
// PySequence_Fast() is provided to the macro, not the *result* of
|
||||||
|
|
@ -75,8 +65,6 @@ extern "C" {
|
||||||
|
|
||||||
#else /* !Py_GIL_DISABLED */
|
#else /* !Py_GIL_DISABLED */
|
||||||
// The critical section APIs are no-ops with the GIL.
|
// The critical section APIs are no-ops with the GIL.
|
||||||
# define Py_BEGIN_CRITICAL_SECTION_MUT(mut) {
|
|
||||||
# define Py_BEGIN_CRITICAL_SECTION2_MUT(m1, m2) {
|
|
||||||
# define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) {
|
# define Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(original) {
|
||||||
# define Py_END_CRITICAL_SECTION_SEQUENCE_FAST() }
|
# define Py_END_CRITICAL_SECTION_SEQUENCE_FAST() }
|
||||||
# define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex)
|
# define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex)
|
||||||
|
|
@ -119,6 +107,7 @@ _PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m)
|
||||||
_PyCriticalSection_BeginSlow(c, m);
|
_PyCriticalSection_BeginSlow(c, m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#define PyCriticalSection_BeginMutex _PyCriticalSection_BeginMutex
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
_PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op)
|
_PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op)
|
||||||
|
|
@ -194,6 +183,7 @@ _PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
|
||||||
_PyCriticalSection2_BeginSlow(c, m1, m2, 0);
|
_PyCriticalSection2_BeginSlow(c, m1, m2, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#define PyCriticalSection2_BeginMutex _PyCriticalSection2_BeginMutex
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
_PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b)
|
_PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
New variants for the critical section API that accept one or two
|
||||||
|
:c:type:`PyMutex` pointers rather than :c:type:`PyObject` instances are now
|
||||||
|
public in the non-limited C API.
|
||||||
|
|
@ -431,7 +431,7 @@ typedef struct {
|
||||||
visible to other threads before the `dict_final` bit is set.
|
visible to other threads before the `dict_final` bit is set.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define STGINFO_LOCK(stginfo) Py_BEGIN_CRITICAL_SECTION_MUT(&(stginfo)->mutex)
|
#define STGINFO_LOCK(stginfo) Py_BEGIN_CRITICAL_SECTION_MUTEX(&(stginfo)->mutex)
|
||||||
#define STGINFO_UNLOCK() Py_END_CRITICAL_SECTION()
|
#define STGINFO_UNLOCK() Py_END_CRITICAL_SECTION()
|
||||||
|
|
||||||
static inline uint8_t
|
static inline uint8_t
|
||||||
|
|
|
||||||
|
|
@ -2418,6 +2418,16 @@ test_critical_sections(PyObject *module, PyObject *Py_UNUSED(args))
|
||||||
Py_BEGIN_CRITICAL_SECTION2(module, module);
|
Py_BEGIN_CRITICAL_SECTION2(module, module);
|
||||||
Py_END_CRITICAL_SECTION2();
|
Py_END_CRITICAL_SECTION2();
|
||||||
|
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
// avoid unused variable compiler warning on GIL-enabled build
|
||||||
|
PyMutex mut = {0};
|
||||||
|
Py_BEGIN_CRITICAL_SECTION_MUTEX(&mut);
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
|
||||||
|
Py_BEGIN_CRITICAL_SECTION2_MUTEX(&mut, &mut);
|
||||||
|
Py_END_CRITICAL_SECTION2();
|
||||||
|
#endif
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,11 @@ class object "PyObject *" "&PyBaseObject_Type"
|
||||||
// while the stop-the-world mechanism is active. The slots and flags are read
|
// while the stop-the-world mechanism is active. The slots and flags are read
|
||||||
// in many places without holding a lock and without atomics.
|
// in many places without holding a lock and without atomics.
|
||||||
#define TYPE_LOCK &PyInterpreterState_Get()->types.mutex
|
#define TYPE_LOCK &PyInterpreterState_Get()->types.mutex
|
||||||
#define BEGIN_TYPE_LOCK() Py_BEGIN_CRITICAL_SECTION_MUT(TYPE_LOCK)
|
#define BEGIN_TYPE_LOCK() Py_BEGIN_CRITICAL_SECTION_MUTEX(TYPE_LOCK)
|
||||||
#define END_TYPE_LOCK() Py_END_CRITICAL_SECTION()
|
#define END_TYPE_LOCK() Py_END_CRITICAL_SECTION()
|
||||||
|
|
||||||
#define BEGIN_TYPE_DICT_LOCK(d) \
|
#define BEGIN_TYPE_DICT_LOCK(d) \
|
||||||
Py_BEGIN_CRITICAL_SECTION2_MUT(TYPE_LOCK, &_PyObject_CAST(d)->ob_mutex)
|
Py_BEGIN_CRITICAL_SECTION2_MUTEX(TYPE_LOCK, &_PyObject_CAST(d)->ob_mutex)
|
||||||
|
|
||||||
#define END_TYPE_DICT_LOCK() Py_END_CRITICAL_SECTION2()
|
#define END_TYPE_DICT_LOCK() Py_END_CRITICAL_SECTION2()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,15 @@ PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef PyCriticalSection_BeginMutex
|
||||||
|
void
|
||||||
|
PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m)
|
||||||
|
{
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
_PyCriticalSection_BeginMutex(c, m);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#undef PyCriticalSection_End
|
#undef PyCriticalSection_End
|
||||||
void
|
void
|
||||||
PyCriticalSection_End(PyCriticalSection *c)
|
PyCriticalSection_End(PyCriticalSection *c)
|
||||||
|
|
@ -148,6 +157,15 @@ PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef PyCriticalSection2_BeginMutex
|
||||||
|
void
|
||||||
|
PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
|
||||||
|
{
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
_PyCriticalSection2_BeginMutex(c, m1, m2);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#undef PyCriticalSection2_End
|
#undef PyCriticalSection2_End
|
||||||
void
|
void
|
||||||
PyCriticalSection2_End(PyCriticalSection2 *c)
|
PyCriticalSection2_End(PyCriticalSection2 *c)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue