Skip to content

Commit

Permalink
pythongh-115432: Add critical section variant that handles a NULL object
Browse files Browse the repository at this point in the history
This adds `Py_BEGIN_CRITICAL_SECTION_OPT` and
`Py_END_CRITICAL_SECTION_OPT`, 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`.
  • Loading branch information
colesbury committed Feb 13, 2024
1 parent 681778c commit 7b4e4e7
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 0 deletions.
29 changes: 29 additions & 0 deletions Include/internal/pycore_critical_section.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ extern "C" {
_PyCriticalSection_End(&_cs); \
}

# define Py_BEGIN_CRITICAL_SECTION_OPT(op) \
{ \
_PyCriticalSection _cs_opt = {0}; \
_PyCriticalSection_BeginOpt(&_cs_opt, _PyObject_CAST(op))

# define Py_END_CRITICAL_SECTION_OPT() \
_PyCriticalSection_EndOpt(&_cs_opt); \
}

# define Py_BEGIN_CRITICAL_SECTION2(a, b) \
{ \
_PyCriticalSection2 _cs2; \
Expand Down Expand Up @@ -131,6 +140,8 @@ extern "C" {
// The critical section APIs are no-ops with the GIL.
# define Py_BEGIN_CRITICAL_SECTION(op)
# define Py_END_CRITICAL_SECTION()
# define Py_BEGIN_CRITICAL_SECTION_OPT(op)
# define Py_END_CRITICAL_SECTION_OPT()
# define Py_BEGIN_CRITICAL_SECTION2(a, b)
# define Py_END_CRITICAL_SECTION2()
# define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex)
Expand Down Expand Up @@ -187,6 +198,16 @@ _PyCriticalSection_Begin(_PyCriticalSection *c, PyMutex *m)
}
}

static inline void
_PyCriticalSection_BeginOpt(_PyCriticalSection *c, PyObject *op)
{
#ifdef Py_GIL_DISABLED
if (op != NULL) {
_PyCriticalSection_Begin(c, &_PyObject_CAST(op)->ob_mutex);
}
#endif
}

// Removes the top-most critical section from the thread's stack of critical
// sections. If the new top-most critical section is inactive, then it is
// resumed.
Expand All @@ -209,6 +230,14 @@ _PyCriticalSection_End(_PyCriticalSection *c)
_PyCriticalSection_Pop(c);
}

static inline void
_PyCriticalSection_EndOpt(_PyCriticalSection *c)
{
if (c->mutex) {
_PyCriticalSection_End(c);
}
}

static inline void
_PyCriticalSection2_Begin(_PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
{
Expand Down
10 changes: 10 additions & 0 deletions Modules/_testinternalcapi/test_critical_sections.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ test_critical_sections(PyObject *self, PyObject *Py_UNUSED(args))
Py_END_CRITICAL_SECTION2();
assert_nogil(!PyMutex_IsLocked(&d2->ob_mutex));

// Optional variant behaves the same if the object is non-NULL
Py_BEGIN_CRITICAL_SECTION_OPT(d1);
assert_nogil(PyMutex_IsLocked(&d1->ob_mutex));
Py_END_CRITICAL_SECTION_OPT();

// No-op
PyObject *null_object = NULL;
Py_BEGIN_CRITICAL_SECTION_OPT(null_object);
Py_END_CRITICAL_SECTION_OPT();

Py_DECREF(d2);
Py_DECREF(d1);
Py_RETURN_NONE;
Expand Down

0 comments on commit 7b4e4e7

Please sign in to comment.