Skip to content

Commit ad4f909

Browse files
authored
gh-115432: Add critical section variant that handles a NULL object (#115433)
This adds `Py_XBEGIN_CRITICAL_SECTION` and `Py_XEND_CRITICAL_SECTION`, which accept a possibly NULL object as an argument. If the argument is NULL, then nothing is locked or unlocked. Otherwise, they behave like `Py_BEGIN/END_CRITICAL_SECTION`.
1 parent b0e5c35 commit ad4f909

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

Include/internal/pycore_critical_section.h

+29
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ extern "C" {
9696
_PyCriticalSection_End(&_cs); \
9797
}
9898

99+
# define Py_XBEGIN_CRITICAL_SECTION(op) \
100+
{ \
101+
_PyCriticalSection _cs_opt = {0}; \
102+
_PyCriticalSection_XBegin(&_cs_opt, _PyObject_CAST(op))
103+
104+
# define Py_XEND_CRITICAL_SECTION() \
105+
_PyCriticalSection_XEnd(&_cs_opt); \
106+
}
107+
99108
# define Py_BEGIN_CRITICAL_SECTION2(a, b) \
100109
{ \
101110
_PyCriticalSection2 _cs2; \
@@ -131,6 +140,8 @@ extern "C" {
131140
// The critical section APIs are no-ops with the GIL.
132141
# define Py_BEGIN_CRITICAL_SECTION(op)
133142
# define Py_END_CRITICAL_SECTION()
143+
# define Py_XBEGIN_CRITICAL_SECTION(op)
144+
# define Py_XEND_CRITICAL_SECTION()
134145
# define Py_BEGIN_CRITICAL_SECTION2(a, b)
135146
# define Py_END_CRITICAL_SECTION2()
136147
# define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex)
@@ -187,6 +198,16 @@ _PyCriticalSection_Begin(_PyCriticalSection *c, PyMutex *m)
187198
}
188199
}
189200

201+
static inline void
202+
_PyCriticalSection_XBegin(_PyCriticalSection *c, PyObject *op)
203+
{
204+
#ifdef Py_GIL_DISABLED
205+
if (op != NULL) {
206+
_PyCriticalSection_Begin(c, &_PyObject_CAST(op)->ob_mutex);
207+
}
208+
#endif
209+
}
210+
190211
// Removes the top-most critical section from the thread's stack of critical
191212
// sections. If the new top-most critical section is inactive, then it is
192213
// resumed.
@@ -209,6 +230,14 @@ _PyCriticalSection_End(_PyCriticalSection *c)
209230
_PyCriticalSection_Pop(c);
210231
}
211232

233+
static inline void
234+
_PyCriticalSection_XEnd(_PyCriticalSection *c)
235+
{
236+
if (c->mutex) {
237+
_PyCriticalSection_End(c);
238+
}
239+
}
240+
212241
static inline void
213242
_PyCriticalSection2_Begin(_PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
214243
{

Modules/_testinternalcapi/test_critical_sections.c

+9
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ test_critical_sections(PyObject *self, PyObject *Py_UNUSED(args))
4949
Py_END_CRITICAL_SECTION2();
5050
assert_nogil(!PyMutex_IsLocked(&d2->ob_mutex));
5151

52+
// Optional variant behaves the same if the object is non-NULL
53+
Py_XBEGIN_CRITICAL_SECTION(d1);
54+
assert_nogil(PyMutex_IsLocked(&d1->ob_mutex));
55+
Py_XEND_CRITICAL_SECTION();
56+
57+
// No-op
58+
Py_XBEGIN_CRITICAL_SECTION(NULL);
59+
Py_XEND_CRITICAL_SECTION();
60+
5261
Py_DECREF(d2);
5362
Py_DECREF(d1);
5463
Py_RETURN_NONE;

0 commit comments

Comments
 (0)