mirror of
https://github.com/python/cpython.git
synced 2025-11-03 03:22:27 +00:00
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:
parent
03fa2df927
commit
8f17d69b7b
15 changed files with 459 additions and 225 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue