| // Lightweight locks and other synchronization mechanisms. | |
| // | |
| // These implementations are based on WebKit's WTF::Lock. See | |
| // https://webkit.org/blog/6161/locking-in-webkit/ for a description of the | |
| // design. | |
| #ifndef Py_INTERNAL_LOCK_H | |
| #define Py_INTERNAL_LOCK_H | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| #ifndef Py_BUILD_CORE | |
| # error "this header requires Py_BUILD_CORE define" | |
| #endif | |
| //_Py_UNLOCKED is defined as 0 and _Py_LOCKED as 1 in Include/cpython/pylock.h | |
| #define _Py_HAS_PARKED 2 | |
| #define _Py_ONCE_INITIALIZED 4 | |
| static inline int | |
| PyMutex_LockFast(PyMutex *m) | |
| { | |
| uint8_t expected = _Py_UNLOCKED; | |
| uint8_t *lock_bits = &m->_bits; | |
| return _Py_atomic_compare_exchange_uint8(lock_bits, &expected, _Py_LOCKED); | |
| } | |
| // Re-initializes the mutex after a fork to the unlocked state. | |
| static inline void | |
| _PyMutex_at_fork_reinit(PyMutex *m) | |
| { | |
| memset(m, 0, sizeof(*m)); | |
| } | |
| typedef enum _PyLockFlags { | |
| // Do not detach/release the GIL when waiting on the lock. | |
| // | |
| // Note that code executed while holding a mutex with this flag must | |
| // not detach, reach a safepoint or initiate a stop-the-world pause. | |
| // Otherwise, a non-detaching waiter may remain waiting for this mutex and | |
| // prevent the pause from completing. | |
| _Py_LOCK_DONT_DETACH = 0, | |
| // Detach/release the GIL while waiting on the lock. | |
| _PY_LOCK_DETACH = 1, | |
| // Handle signals if interrupted while waiting on the lock. | |
| _PY_LOCK_HANDLE_SIGNALS = 2, | |
| // Fail if interrupted by a signal while waiting on the lock. | |
| _PY_FAIL_IF_INTERRUPTED = 4, | |
| // Locking & unlocking this lock requires attached thread state. | |
| // If locking returns PY_LOCK_FAILURE, a Python exception *may* be raised. | |
| // (Intended for use with _PY_LOCK_HANDLE_SIGNALS and _PY_LOCK_DETACH.) | |
| _PY_LOCK_PYTHONLOCK = 8, | |
| } _PyLockFlags; | |
| // Lock a mutex with an optional timeout and additional options. See | |
| // _PyLockFlags for details. | |
| extern PyAPI_FUNC(PyLockStatus) | |
| _PyMutex_LockTimed(PyMutex *m, PyTime_t timeout_ns, _PyLockFlags flags); | |
| // Lock a mutex with additional options. See _PyLockFlags for details. | |
| static inline void | |
| PyMutex_LockFlags(PyMutex *m, _PyLockFlags flags) | |
| { | |
| uint8_t expected = _Py_UNLOCKED; | |
| if (!_Py_atomic_compare_exchange_uint8(&m->_bits, &expected, _Py_LOCKED)) { | |
| _PyMutex_LockTimed(m, -1, flags); | |
| } | |
| } | |
| // Unlock a mutex, returns -1 if the mutex is not locked (used for improved | |
| // error messages) otherwise returns 0. | |
| extern int _PyMutex_TryUnlock(PyMutex *m); | |
| // Yield the processor to other threads (e.g., sched_yield). | |
| extern void _Py_yield(void); | |
| // PyEvent is a one-time event notification | |
| typedef struct { | |
| uint8_t v; | |
| } PyEvent; | |
| // Check if the event is set without blocking. Returns 1 if the event is set or | |
| // 0 otherwise. | |
| PyAPI_FUNC(int) _PyEvent_IsSet(PyEvent *evt); | |
| // Set the event and notify any waiting threads. | |
| // Export for '_testinternalcapi' shared extension | |
| PyAPI_FUNC(void) _PyEvent_Notify(PyEvent *evt); | |
| // Wait for the event to be set. If the event is already set, then this returns | |
| // immediately. | |
| PyAPI_FUNC(void) PyEvent_Wait(PyEvent *evt); | |
| // Wait for the event to be set, or until the timeout expires. If the event is | |
| // already set, then this returns immediately. Returns 1 if the event was set, | |
| // and 0 if the timeout expired or thread was interrupted. If `detach` is | |
| // true, then the thread will detach/release the GIL while waiting. | |
| PyAPI_FUNC(int) | |
| PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns, int detach); | |
| // _PyRawMutex implements a word-sized mutex that that does not depend on the | |
| // parking lot API, and therefore can be used in the parking lot | |
| // implementation. | |
| // | |
| // The mutex uses a packed representation: the least significant bit is used to | |
| // indicate whether the mutex is locked or not. The remaining bits are either | |
| // zero or a pointer to a `struct raw_mutex_entry` (see lock.c). | |
| typedef struct { | |
| uintptr_t v; | |
| } _PyRawMutex; | |
| // Slow paths for lock/unlock | |
| extern void _PyRawMutex_LockSlow(_PyRawMutex *m); | |
| extern void _PyRawMutex_UnlockSlow(_PyRawMutex *m); | |
| static inline void | |
| _PyRawMutex_Lock(_PyRawMutex *m) | |
| { | |
| uintptr_t unlocked = _Py_UNLOCKED; | |
| if (_Py_atomic_compare_exchange_uintptr(&m->v, &unlocked, _Py_LOCKED)) { | |
| return; | |
| } | |
| _PyRawMutex_LockSlow(m); | |
| } | |
| static inline void | |
| _PyRawMutex_Unlock(_PyRawMutex *m) | |
| { | |
| uintptr_t locked = _Py_LOCKED; | |
| if (_Py_atomic_compare_exchange_uintptr(&m->v, &locked, _Py_UNLOCKED)) { | |
| return; | |
| } | |
| _PyRawMutex_UnlockSlow(m); | |
| } | |
| // Type signature for one-time initialization functions. The function should | |
| // return 0 on success and -1 on failure. | |
| typedef int _Py_once_fn_t(void *arg); | |
| // (private) slow path for one time initialization | |
| PyAPI_FUNC(int) | |
| _PyOnceFlag_CallOnceSlow(_PyOnceFlag *flag, _Py_once_fn_t *fn, void *arg); | |
| // Calls `fn` once using `flag`. The `arg` is passed to the call to `fn`. | |
| // | |
| // Returns 0 on success and -1 on failure. | |
| // | |
| // If `fn` returns 0 (success), then subsequent calls immediately return 0. | |
| // If `fn` returns -1 (failure), then subsequent calls will retry the call. | |
| static inline int | |
| _PyOnceFlag_CallOnce(_PyOnceFlag *flag, _Py_once_fn_t *fn, void *arg) | |
| { | |
| if (_Py_atomic_load_uint8(&flag->v) == _Py_ONCE_INITIALIZED) { | |
| return 0; | |
| } | |
| return _PyOnceFlag_CallOnceSlow(flag, fn, arg); | |
| } | |
| // A recursive mutex. The mutex should zero-initialized. | |
| typedef struct { | |
| PyMutex mutex; | |
| unsigned long long thread; // i.e., PyThread_get_thread_ident_ex() | |
| size_t level; | |
| } _PyRecursiveMutex; | |
| PyAPI_FUNC(int) _PyRecursiveMutex_IsLockedByCurrentThread(_PyRecursiveMutex *m); | |
| PyAPI_FUNC(void) _PyRecursiveMutex_Lock(_PyRecursiveMutex *m); | |
| extern PyLockStatus _PyRecursiveMutex_LockTimed(_PyRecursiveMutex *m, PyTime_t timeout, _PyLockFlags flags); | |
| PyAPI_FUNC(void) _PyRecursiveMutex_Unlock(_PyRecursiveMutex *m); | |
| extern int _PyRecursiveMutex_TryUnlock(_PyRecursiveMutex *m); | |
| #ifdef HAVE_FORK | |
| extern void _PyRecursiveMutex_at_fork_reinit(_PyRecursiveMutex *m, int owned); | |
| #endif | |
| // A readers-writer (RW) lock. The lock supports multiple concurrent readers or | |
| // a single writer. The lock is write-preferring: if a writer is waiting while | |
| // the lock is read-locked then, new readers will be blocked. This avoids | |
| // starvation of writers. | |
| // | |
| // In C++, the equivalent synchronization primitive is std::shared_mutex | |
| // with shared ("read") and exclusive ("write") locking. | |
| // | |
| // The two least significant bits are used to indicate if the lock is | |
| // write-locked and if there are parked threads (either readers or writers) | |
| // waiting to acquire the lock. The remaining bits are used to indicate the | |
| // number of readers holding the lock. | |
| // | |
| // 0b000..00000: unlocked | |
| // 0bnnn..nnn00: nnn..nnn readers holding the lock | |
| // 0bnnn..nnn10: nnn..nnn readers holding the lock and a writer is waiting | |
| // 0b00000..010: unlocked with awoken writer about to acquire lock | |
| // 0b00000..001: write-locked | |
| // 0b00000..011: write-locked and readers or other writers are waiting | |
| // | |
| // Note that reader_count must be zero if the lock is held by a writer, and | |
| // vice versa. The lock can only be held by readers or a writer, but not both. | |
| // | |
| // The design is optimized for simplicity of the implementation. The lock is | |
| // not fair: if fairness is desired, use an additional PyMutex to serialize | |
| // writers. The lock is also not reentrant. | |
| typedef struct { | |
| uintptr_t bits; | |
| } _PyRWMutex; | |
| // Read lock (i.e., shared lock) | |
| PyAPI_FUNC(void) _PyRWMutex_RLock(_PyRWMutex *rwmutex); | |
| PyAPI_FUNC(void) _PyRWMutex_RUnlock(_PyRWMutex *rwmutex); | |
| // Write lock (i.e., exclusive lock) | |
| PyAPI_FUNC(void) _PyRWMutex_Lock(_PyRWMutex *rwmutex); | |
| PyAPI_FUNC(void) _PyRWMutex_Unlock(_PyRWMutex *rwmutex); | |
| #ifdef __cplusplus | |
| } | |
| #endif | |
| #endif /* !Py_INTERNAL_LOCK_H */ |