mirror of
https://github.com/python/cpython.git
synced 2026-02-22 07:00:51 +00:00
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Benedikt Johannes <benedikt.johannes.hofer@gmail.com>
308 lines
9.8 KiB
ReStructuredText
308 lines
9.8 KiB
ReStructuredText
.. highlight:: c
|
|
|
|
.. _synchronization:
|
|
|
|
Synchronization primitives
|
|
==========================
|
|
|
|
The C-API provides a basic mutual exclusion lock.
|
|
|
|
.. c:type:: PyMutex
|
|
|
|
A mutual exclusion lock. The :c:type:`!PyMutex` should be initialized to
|
|
zero to represent the unlocked state. For example::
|
|
|
|
PyMutex mutex = {0};
|
|
|
|
Instances of :c:type:`!PyMutex` should not be copied or moved. Both the
|
|
contents and address of a :c:type:`!PyMutex` are meaningful, and it must
|
|
remain at a fixed, writable location in memory.
|
|
|
|
.. note::
|
|
|
|
A :c:type:`!PyMutex` currently occupies one byte, but the size should be
|
|
considered unstable. The size may change in future Python releases
|
|
without a deprecation period.
|
|
|
|
.. versionadded:: 3.13
|
|
|
|
.. c:function:: void PyMutex_Lock(PyMutex *m)
|
|
|
|
Lock mutex *m*. If another thread has already locked it, the calling
|
|
thread will block until the mutex is unlocked. While blocked, the thread
|
|
will temporarily detach the :term:`thread state <attached thread state>` if one exists.
|
|
|
|
.. versionadded:: 3.13
|
|
|
|
.. c:function:: void PyMutex_Unlock(PyMutex *m)
|
|
|
|
Unlock mutex *m*. The mutex must be locked --- otherwise, the function will
|
|
issue a fatal error.
|
|
|
|
.. versionadded:: 3.13
|
|
|
|
.. c:function:: int PyMutex_IsLocked(PyMutex *m)
|
|
|
|
Returns non-zero if the mutex *m* is currently locked, zero otherwise.
|
|
|
|
.. note::
|
|
|
|
This function is intended for use in assertions and debugging only and
|
|
should not be used to make concurrency control decisions, as the lock
|
|
state may change immediately after the check.
|
|
|
|
.. versionadded:: 3.14
|
|
|
|
.. _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 are intended to be used for custom types implemented
|
|
in C-API extensions. They should generally not be used with built-in types like
|
|
:class:`list` and :class:`dict` because their public C-APIs
|
|
already use critical sections internally, with the notable
|
|
exception of :c:func:`PyDict_Next`, which requires critical section
|
|
to be acquired externally.
|
|
|
|
Critical sections avoid deadlocks by implicitly suspending active critical
|
|
sections, hence, they do not provide exclusive access such as provided by
|
|
traditional locks like :c:type:`PyMutex`. When a critical section is started,
|
|
the per-object lock for the object is acquired. If the code executed inside the
|
|
critical section calls C-API functions then it can suspend the critical section thereby
|
|
releasing the per-object lock, so other threads can acquire the per-object lock
|
|
for the same object.
|
|
|
|
Variants that accept :c:type:`PyMutex` pointers rather than Python objects are also
|
|
available. Use these variants to start a critical section in a situation where
|
|
there is no :c:type:`PyObject` -- for example, when working with a C type that
|
|
does not extend or wrap :c:type:`PyObject` but still needs to call into the C
|
|
API in a manner that might lead to deadlocks.
|
|
|
|
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 potential 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_BEGIN_CRITICAL_SECTION_MUTEX(m)
|
|
|
|
Locks the mutex *m* and begins a critical section.
|
|
|
|
In the free-threaded build, this macro expands to::
|
|
|
|
{
|
|
PyCriticalSection _py_cs;
|
|
PyCriticalSection_BeginMutex(&_py_cs, m)
|
|
|
|
Note that unlike :c:macro:`Py_BEGIN_CRITICAL_SECTION`, there is no cast for
|
|
the argument of the macro - it must be a :c:type:`PyMutex` pointer.
|
|
|
|
On the default build, this macro expands to ``{``.
|
|
|
|
.. versionadded:: 3.14
|
|
|
|
.. 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-object 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;
|
|
PyCriticalSection2_Begin(&_py_cs2, (PyObject*)(a), (PyObject*)(b))
|
|
|
|
In the default build, this macro expands to ``{``.
|
|
|
|
.. versionadded:: 3.13
|
|
|
|
.. c:macro:: Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2)
|
|
|
|
Locks the mutexes *m1* and *m2* and begins a critical section.
|
|
|
|
In the free-threaded build, this macro expands to::
|
|
|
|
{
|
|
PyCriticalSection2 _py_cs2;
|
|
PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)
|
|
|
|
Note that unlike :c:macro:`Py_BEGIN_CRITICAL_SECTION2`, there is no cast for
|
|
the arguments of the macro - they must be :c:type:`PyMutex` pointers.
|
|
|
|
On the default build, this macro expands to ``{``.
|
|
|
|
.. versionadded:: 3.14
|
|
|
|
.. 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::
|
|
|
|
PyCriticalSection2_End(&_py_cs2);
|
|
}
|
|
|
|
In the default build, this macro expands to ``}``.
|
|
|
|
.. versionadded:: 3.13
|
|
|
|
|
|
Legacy locking APIs
|
|
-------------------
|
|
|
|
These APIs are obsolete since Python 3.13 with the introduction of
|
|
:c:type:`PyMutex`.
|
|
|
|
.. versionchanged:: 3.15
|
|
These APIs are now a simple wrapper around ``PyMutex``.
|
|
|
|
|
|
.. c:type:: PyThread_type_lock
|
|
|
|
A pointer to a mutual exclusion lock.
|
|
|
|
|
|
.. c:type:: PyLockStatus
|
|
|
|
The result of acquiring a lock with a timeout.
|
|
|
|
.. c:namespace:: NULL
|
|
|
|
.. c:enumerator:: PY_LOCK_FAILURE
|
|
|
|
Failed to acquire the lock.
|
|
|
|
.. c:enumerator:: PY_LOCK_ACQUIRED
|
|
|
|
The lock was successfully acquired.
|
|
|
|
.. c:enumerator:: PY_LOCK_INTR
|
|
|
|
The lock was interrupted by a signal.
|
|
|
|
|
|
.. c:function:: PyThread_type_lock PyThread_allocate_lock(void)
|
|
|
|
Allocate a new lock.
|
|
|
|
On success, this function returns a lock; on failure, this
|
|
function returns ``0`` without an exception set.
|
|
|
|
The caller does not need to hold an :term:`attached thread state`.
|
|
|
|
.. versionchanged:: 3.15
|
|
This function now always uses :c:type:`PyMutex`. In prior versions, this
|
|
would use a lock provided by the operating system.
|
|
|
|
|
|
.. c:function:: void PyThread_free_lock(PyThread_type_lock lock)
|
|
|
|
Destroy *lock*. The lock should not be held by any thread when calling
|
|
this.
|
|
|
|
The caller does not need to hold an :term:`attached thread state`.
|
|
|
|
|
|
.. c:function:: PyLockStatus PyThread_acquire_lock_timed(PyThread_type_lock lock, long long microseconds, int intr_flag)
|
|
|
|
Acquire *lock* with a timeout.
|
|
|
|
This will wait for *microseconds* microseconds to acquire the lock. If the
|
|
timeout expires, this function returns :c:enumerator:`PY_LOCK_FAILURE`.
|
|
If *microseconds* is ``-1``, this will wait indefinitely until the lock has
|
|
been released.
|
|
|
|
If *intr_flag* is ``1``, acquiring the lock may be interrupted by a signal,
|
|
in which case this function returns :c:enumerator:`PY_LOCK_INTR`. Upon
|
|
interruption, it's generally expected that the caller makes a call to
|
|
:c:func:`Py_MakePendingCalls` to propagate an exception to Python code.
|
|
|
|
If the lock is successfully acquired, this function returns
|
|
:c:enumerator:`PY_LOCK_ACQUIRED`.
|
|
|
|
The caller does not need to hold an :term:`attached thread state`.
|
|
|
|
|
|
.. c:function:: int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
|
|
|
|
Acquire *lock*.
|
|
|
|
If *waitflag* is ``1`` and another thread currently holds the lock, this
|
|
function will wait until the lock can be acquired and will always return
|
|
``1``.
|
|
|
|
If *waitflag* is ``0`` and another thread holds the lock, this function will
|
|
not wait and instead return ``0``. If the lock is not held by any other
|
|
thread, then this function will acquire it and return ``1``.
|
|
|
|
Unlike :c:func:`PyThread_acquire_lock_timed`, acquiring the lock cannot be
|
|
interrupted by a signal.
|
|
|
|
The caller does not need to hold an :term:`attached thread state`.
|
|
|
|
|
|
.. c:function:: int PyThread_release_lock(PyThread_type_lock lock)
|
|
|
|
Release *lock*. If *lock* is not held, then this function issues a
|
|
fatal error.
|
|
|
|
The caller does not need to hold an :term:`attached thread state`.
|