Synchronization primitives¶
The C-API provides a basic mutual exclusion lock.
- typePyMutex¶
A mutual exclusion lock. The
PyMutexshould be initialized tozero to represent the unlocked state. For example:PyMutexmutex={0};
Instances of
PyMutexshould not be copied or moved. Both thecontents and address of aPyMutexare meaningful, and it mustremain at a fixed, writable location in memory.Note
A
PyMutexcurrently occupies one byte, but the size should beconsidered unstable. The size may change in future Python releaseswithout a deprecation period.Added in version 3.13.
- voidPyMutex_Lock(PyMutex*m)¶
Lock mutexm. If another thread has already locked it, the callingthread will block until the mutex is unlocked. While blocked, the threadwill temporarily detach thethread state if one exists.
Added in version 3.13.
- voidPyMutex_Unlock(PyMutex*m)¶
Unlock mutexm. The mutex must be locked — otherwise, the function willissue a fatal error.
Added in version 3.13.
- intPyMutex_IsLocked(PyMutex*m)¶
Returns non-zero if the mutexm is currently locked, zero otherwise.
Note
This function is intended for use in assertions and debugging only andshould not be used to make concurrency control decisions, as the lockstate may change immediately after the check.
Added in version 3.14.
Python critical section API¶
The critical section API provides a deadlock avoidance layer on top ofper-object locks forfree-threaded CPython. They areintended to replace reliance on theglobal interpreter lock, and areno-ops in versions of Python with the global interpreter lock.
Critical sections are intended to be used for custom types implementedin C-API extensions. They should generally not be used with built-in types likelist anddict because their public C-APIsalready use critical sections internally, with the notableexception ofPyDict_Next(), which requires critical sectionto be acquired externally.
Critical sections avoid deadlocks by implicitly suspending active criticalsections, hence, they do not provide exclusive access such as provided bytraditional locks likePyMutex. When a critical section is started,the per-object lock for the object is acquired. If the code executed inside thecritical section calls C-API functions then it can suspend the critical section therebyreleasing the per-object lock, so other threads can acquire the per-object lockfor the same object.
Variants that acceptPyMutex pointers rather than Python objects are alsoavailable. Use these variants to start a critical section in a situation wherethere is noPyObject – for example, when working with a C type thatdoes not extend or wrapPyObject but still needs to call into the CAPI in a manner that might lead to deadlocks.
The functions and structs used by the macros are exposed for caseswhere C macros are not available. They should only be used as in thegiven macro expansions. Note that the sizes and contents of the structures maychange in future Python versions.
Note
Operations that need to lock two objects at once must usePy_BEGIN_CRITICAL_SECTION2. Youcannot use nested criticalsections to lock more than one object at once, because the inner criticalsection may suspend the outer critical sections. This API does not providea way to lock more than two objects at once.
Example usage:
staticPyObject*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,Py_SETREF callsPy_DECREF, whichcan call arbitrary code through an object’s deallocation function. The criticalsection API avoids potential deadlocks due to reentrancy and lock orderingby allowing the runtime to temporarily suspend the critical section if thecode triggered by the finalizer blocks and callsPyEval_SaveThread().
- Py_BEGIN_CRITICAL_SECTION(op)¶
Acquires the per-object lock for the objectop and begins acritical 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
{.Added in version 3.13.
- Py_BEGIN_CRITICAL_SECTION_MUTEX(m)¶
Locks the mutexm 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
Py_BEGIN_CRITICAL_SECTION, there is no cast forthe argument of the macro - it must be aPyMutexpointer.On the default build, this macro expands to
{.Added in version 3.14.
- 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
}.Added in version 3.13.
- Py_BEGIN_CRITICAL_SECTION2(a,b)¶
Acquires the per-object locks for the objectsa andb and begins acritical section. The locks are acquired in a consistent order (lowestaddress 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
{.Added in version 3.13.
- Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1,m2)¶
Locks the mutexesm1 andm2 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
Py_BEGIN_CRITICAL_SECTION2, there is no cast forthe arguments of the macro - they must bePyMutexpointers.On the default build, this macro expands to
{.Added in version 3.14.
- 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
}.Added in version 3.13.
Legacy locking APIs¶
These APIs are obsolete since Python 3.13 with the introduction ofPyMutex.
Changed in version 3.15:These APIs are now a simple wrapper aroundPyMutex.
- typePyThread_type_lock¶
A pointer to a mutual exclusion lock.
- typePyLockStatus¶
The result of acquiring a lock with a timeout.
- enumeratorPY_LOCK_FAILURE¶
Failed to acquire the lock.
- enumeratorPY_LOCK_ACQUIRED¶
The lock was successfully acquired.
- enumeratorPY_LOCK_INTR¶
The lock was interrupted by a signal.
- enumeratorPY_LOCK_FAILURE¶
- PyThread_type_lockPyThread_allocate_lock(void)¶
- Part of theStable ABI.
Allocate a new lock.
On success, this function returns a lock; on failure, thisfunction returns
0without an exception set.The caller does not need to hold anattached thread state.
Changed in version 3.15:This function now always uses
PyMutex. In prior versions, thiswould use a lock provided by the operating system.
- voidPyThread_free_lock(PyThread_type_locklock)¶
- Part of theStable ABI.
Destroylock. The lock should not be held by any thread when callingthis.
The caller does not need to hold anattached thread state.
- PyLockStatusPyThread_acquire_lock_timed(PyThread_type_locklock,longlongmicroseconds,intintr_flag)¶
- Part of theStable ABI.
Acquirelock with a timeout.
This will wait formicroseconds microseconds to acquire the lock. If thetimeout expires, this function returns
PY_LOCK_FAILURE.Ifmicroseconds is-1, this will wait indefinitely until the lock hasbeen released.Ifintr_flag is
1, acquiring the lock may be interrupted by a signal,in which case this function returnsPY_LOCK_INTR. Uponinterruption, it’s generally expected that the caller makes a call toPy_MakePendingCalls()to propagate an exception to Python code.If the lock is successfully acquired, this function returns
PY_LOCK_ACQUIRED.The caller does not need to hold anattached thread state.
- intPyThread_acquire_lock(PyThread_type_locklock,intwaitflag)¶
- Part of theStable ABI.
Acquirelock.
Ifwaitflag is
1and another thread currently holds the lock, thisfunction will wait until the lock can be acquired and will always return1.Ifwaitflag is
0and another thread holds the lock, this function willnot wait and instead return0. If the lock is not held by any otherthread, then this function will acquire it and return1.Unlike
PyThread_acquire_lock_timed(), acquiring the lock cannot beinterrupted by a signal.The caller does not need to hold anattached thread state.
- intPyThread_release_lock(PyThread_type_locklock)¶
- Part of theStable ABI.
Releaselock. Iflock is not held, then this function issues afatal error.
The caller does not need to hold anattached thread state.