Synchronization primitives

The C-API provides a basic mutual exclusion lock.

typePyMutex

A mutual exclusion lock. ThePyMutex should be initialized tozero to represent the unlocked state. For example:

PyMutexmutex={0};

Instances ofPyMutex should not be copied or moved. Both thecontents and address of aPyMutex are meaningful, and it mustremain at a fixed, writable location in memory.

Note

APyMutex currently 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 unlikePy_BEGIN_CRITICAL_SECTION, there is no cast forthe argument of the macro - it must be aPyMutex pointer.

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 unlikePy_BEGIN_CRITICAL_SECTION2, there is no cast forthe arguments of the macro - they must bePyMutex pointers.

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.

PyThread_type_lockPyThread_allocate_lock(void)
Part of theStable ABI.

Allocate a new lock.

On success, this function returns a lock; on failure, thisfunction returns0 without an exception set.

The caller does not need to hold anattached thread state.

Changed in version 3.15:This function now always usesPyMutex. 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 returnsPY_LOCK_FAILURE.Ifmicroseconds is-1, this will wait indefinitely until the lock hasbeen released.

Ifintr_flag is1, 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 returnsPY_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 is1 and another thread currently holds the lock, thisfunction will wait until the lock can be acquired and will always return1.

Ifwaitflag is0 and 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.

UnlikePyThread_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.