gh-119344: Make critical section API public (#119353)

This makes the following macros public as part of the non-limited C-API for
locking a single object or two objects at once.

* `Py_BEGIN_CRITICAL_SECTION(op)` / `Py_END_CRITICAL_SECTION()`
* `Py_BEGIN_CRITICAL_SECTION2(a, b)` / `Py_END_CRITICAL_SECTION2()`

The supporting functions and structs used by the macros are also exposed for
cases where C macros are not available.
This commit is contained in:
Sam Gross 2024-06-21 15:50:18 -04:00 committed by GitHub
parent 03fa2df927
commit 8f17d69b7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 459 additions and 225 deletions

View file

@ -2202,3 +2202,107 @@ The C-API provides a basic mutual exclusion lock.
issue a fatal error.
.. versionadded:: 3.13
.. _python-critical-section-api:
Python Critical Section API
---------------------------
The critical section API provides a deadlock avoidance layer on top of
per-object locks for :term:`free-threaded <free threading>` CPython. They are
intended to replace reliance on the :term:`global interpreter lock`, and are
no-ops in versions of Python with the global interpreter lock.
Critical sections avoid deadlocks by implicitly suspending active critical
sections and releasing the locks during calls to :c:func:`PyEval_SaveThread`.
When :c:func:`PyEval_RestoreThread` is called, the most recent critical section
is resumed, and its locks reacquired. This means the critical section API
provides weaker guarantees than traditional locks -- they are useful because
their behavior is similar to the :term:`GIL`.
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
given macro expansions. Note that the sizes and contents of the structures may
change in future Python versions.
.. note::
Operations that need to lock two objects at once must use
:c:macro:`Py_BEGIN_CRITICAL_SECTION2`. You *cannot* use nested critical
sections to lock more than one object at once, because the inner critical
section may suspend the outer critical sections. This API does not provide
a way to lock more than two objects at once.
Example usage::
static PyObject *
set_field(MyObject *self, PyObject *value)
{
Py_BEGIN_CRITICAL_SECTION(self);
Py_SETREF(self->field, Py_XNewRef(value));
Py_END_CRITICAL_SECTION();
Py_RETURN_NONE;
}
In the above example, :c:macro:`Py_SETREF` calls :c:macro:`Py_DECREF`, which
can call arbitrary code through an object's deallocation function. The critical
section API avoids potentital deadlocks due to reentrancy and lock ordering
by allowing the runtime to temporarily suspend the critical section if the
code triggered by the finalizer blocks and calls :c:func:`PyEval_SaveThread`.
.. c:macro:: Py_BEGIN_CRITICAL_SECTION(op)
Acquires the per-object lock for the object *op* and begins a
critical section.
In the free-threaded build, this macro expands to::
{
PyCriticalSection _py_cs;
PyCriticalSection_Begin(&_py_cs, (PyObject*)(op))
In the default build, this macro expands to ``{``.
.. versionadded:: 3.13
.. c:macro:: Py_END_CRITICAL_SECTION()
Ends the critical section and releases the per-object lock.
In the free-threaded build, this macro expands to::
PyCriticalSection_End(&_py_cs);
}
In the default build, this macro expands to ``}``.
.. versionadded:: 3.13
.. c:macro:: Py_BEGIN_CRITICAL_SECTION2(a, b)
Acquires the per-objects locks for the objects *a* and *b* and begins a
critical section. The locks are acquired in a consistent order (lowest
address first) to avoid lock ordering deadlocks.
In the free-threaded build, this macro expands to::
{
PyCriticalSection2 _py_cs2;
PyCriticalSection_Begin2(&_py_cs2, (PyObject*)(a), (PyObject*)(b))
In the default build, this macro expands to ``{``.
.. versionadded:: 3.13
.. c:macro:: Py_END_CRITICAL_SECTION2()
Ends the critical section and releases the per-object locks.
In the free-threaded build, this macro expands to::
PyCriticalSection_End2(&_py_cs2);
}
In the default build, this macro expands to ``}``.
.. versionadded:: 3.13