]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-115432: Add critical section variant that handles a NULL object (#115433)
authorSam Gross <colesbury@gmail.com>
Thu, 15 Feb 2024 13:37:54 +0000 (08:37 -0500)
committerGitHub <noreply@github.com>
Thu, 15 Feb 2024 13:37:54 +0000 (08:37 -0500)
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`.

Include/internal/pycore_critical_section.h
Modules/_testinternalcapi/test_critical_sections.c

index 38ed8cd69804ba9bcd0470b5beba25f01cf46987..30820a24f5bb64c9e1bf0c4a59b426ff6184c47b 100644 (file)
@@ -96,6 +96,15 @@ extern "C" {
         _PyCriticalSection_End(&_cs);                                   \
     }
 
+# define Py_XBEGIN_CRITICAL_SECTION(op)                                 \
+    {                                                                   \
+        _PyCriticalSection _cs_opt = {0};                               \
+        _PyCriticalSection_XBegin(&_cs_opt, _PyObject_CAST(op))
+
+# define Py_XEND_CRITICAL_SECTION()                                     \
+        _PyCriticalSection_XEnd(&_cs_opt);                              \
+    }
+
 # define Py_BEGIN_CRITICAL_SECTION2(a, b)                               \
     {                                                                   \
         _PyCriticalSection2 _cs2;                                       \
@@ -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_XBEGIN_CRITICAL_SECTION(op)
+# define Py_XEND_CRITICAL_SECTION()
 # define Py_BEGIN_CRITICAL_SECTION2(a, b)
 # define Py_END_CRITICAL_SECTION2()
 # define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex)
@@ -187,6 +198,16 @@ _PyCriticalSection_Begin(_PyCriticalSection *c, PyMutex *m)
     }
 }
 
+static inline void
+_PyCriticalSection_XBegin(_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.
@@ -209,6 +230,14 @@ _PyCriticalSection_End(_PyCriticalSection *c)
     _PyCriticalSection_Pop(c);
 }
 
+static inline void
+_PyCriticalSection_XEnd(_PyCriticalSection *c)
+{
+    if (c->mutex) {
+        _PyCriticalSection_End(c);
+    }
+}
+
 static inline void
 _PyCriticalSection2_Begin(_PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
 {
index 1f7e311558b27cb3fb8753fd52e950d3135df5ff..94da0468fcf149a72d1e03cef09ecc8503a1efda 100644 (file)
@@ -49,6 +49,15 @@ 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_XBEGIN_CRITICAL_SECTION(d1);
+    assert_nogil(PyMutex_IsLocked(&d1->ob_mutex));
+    Py_XEND_CRITICAL_SECTION();
+
+    // No-op
+    Py_XBEGIN_CRITICAL_SECTION(NULL);
+    Py_XEND_CRITICAL_SECTION();
+
     Py_DECREF(d2);
     Py_DECREF(d1);
     Py_RETURN_NONE;