| /* |
| Copyright (c) 2011, 2014, 2026 mingw-w64 project |
| Copyright (c) 2015 Intel Corporation |
| |
| Permission is hereby granted, free of charge, to any person obtaining a |
| copy of this software and associated documentation files (the "Software"), |
| to deal in the Software without restriction, including without limitation |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| and/or sell copies of the Software, and to permit persons to whom the |
| Software is furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in |
| all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <limits.h> |
| #include <stdlib.h> |
| |
| #define WIN32_LEAN_AND_MEAN |
| #include <windows.h> |
| |
| #define WINPTHREAD_MUTEX_DECL WINPTHREAD_API |
| |
| /* public header files */ |
| #include "pthread.h" |
| /* internal header files */ |
| #include "misc.h" |
| |
| /** |
| * Reference: |
| * |
| * pthread_mutex_init(), pthread_mutex_destroy(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_destroy.html> |
| * |
| * pthread_mutex_lock(), pthread_mutex_trylock(), pthread_mutex_unlock(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_lock.html> |
| * |
| * pthread_mutex_timedlock(), pthread_mutex_clocklock(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_clocklock.html> |
| * |
| * pthread_mutex_getprioceiling(), pthread_mutex_setprioceiling(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_getprioceiling.html> |
| * |
| * pthread_mutex_consistent(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_consistent.html> |
| * |
| * pthread_mutexattr_init(), pthread_mutexattr_destroy(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_destroy.html> |
| * |
| * pthread_mutexattr_gettype(), pthread_mutexattr_settype(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_gettype.html> |
| * |
| * pthread_mutexattr_getpshared(), pthread_mutexattr_setpshared(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_getpshared.html> |
| * |
| * pthread_mutexattr_getprotocol(), pthread_mutexattr_setprotocol(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_getprotocol.html> |
| * |
| * pthread_mutexattr_getprioceiling(), pthread_mutexattr_setprioceiling(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_getprioceiling.html> |
| * |
| * pthread_mutexattr_getrobust(), pthread_mutexattr_setrobust(): |
| * <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_getrobust.html> |
| * |
| * The following functions are not implemented: |
| * |
| * - pthread_mutex_clocklock() |
| * - pthread_mutex_getprioceiling() |
| * - pthread_mutex_setprioceiling() |
| */ |
| |
| /** |
| * Stalled (PTHREAD_MUTEX_STALLED) vs. Robust (PTHREAD_MUTEX_ROBUST) mutexes. |
| * |
| * Stalled mutexes are implemented using auto-reset events with fast paths: |
| * |
| * - `pthread_mutex_trylock` always uses the fast test-and-set path. |
| * |
| * - `pthread_mutex_lock` and `pthread_mutex_timedlock` will use the fast path |
| * if mutex is unlocked, and only enter wait state if required. |
| * |
| * Unlike stalled mutexes, robust mutexes are required to detect condition |
| * when the thread that owned a mutex terminated without releasing it. |
| * |
| * Function `WaitForSingleObject`, when called on a Windows mutex, reports this |
| * exact condition by returning `WAIT_ABANDONED`. As such, robust mutexes are |
| * implemented using Windows mutexes. |
| * |
| * This has an implication that fast paths are not suitable for robust mutexes, |
| * as they always require a call to `WaitForSingleObject` to check if the mutex |
| * was abandoned. The syscall overhead may be very noticable in certain cases. |
| */ |
| |
| /** |
| * Forward declaration; see definition below. |
| */ |
| typedef union WinpthreadsMutex WinpthreadsMutex; |
| |
| /** |
| * Forward declaration; see definition below. |
| */ |
| typedef union WinpthreadsMutexAttributes WinpthreadsMutexAttributes; |
| |
| /** |
| * Mutex type-specific "init" routine. |
| */ |
| typedef int (* FuncMutexInit) (WinpthreadsMutex **, const WinpthreadsMutexAttributes *); |
| |
| /** |
| * Mutex type-specific "destroy" routine. |
| */ |
| typedef void (* FuncMutexDestroy) (WinpthreadsMutex *); |
| |
| /** |
| * Mutex type-specific "lock" routine. |
| * |
| * If second argument is `NULL`, this function will block indefinitely. |
| */ |
| typedef int (* FuncMutexLock) (WinpthreadsMutex *, const struct _timespec64 *); |
| |
| /** |
| * Mutex type-specific "try lock" routine. |
| * |
| * If second argument is `TRUE`, this function will only succeed if mutex is |
| * not owned by any thread, including the calling thread. |
| */ |
| typedef int (* FuncMutexTryLock) (WinpthreadsMutex *, BOOL); |
| |
| /** |
| * Mutex type-specific "unlock" routine. |
| */ |
| typedef int (* FuncMutexUnlock) (WinpthreadsMutex *); |
| |
| /** |
| * Mutex type-specific "mark protected state as consistent" routine. |
| */ |
| typedef int (* FuncMutexSetConsistentState) (WinpthreadsMutex *); |
| |
| /** |
| * Implementation for specific Mutex type. |
| */ |
| typedef struct { |
| FuncMutexInit Init; |
| FuncMutexDestroy Destroy; |
| FuncMutexLock Lock; |
| FuncMutexTryLock TryLock; |
| FuncMutexUnlock Unlock; |
| FuncMutexSetConsistentState SetConsistentState; |
| } WinpthreadsMutexVtable; |
| |
| /** |
| * Union stored in `pthread_mutexattr_t` objects. |
| */ |
| union WinpthreadsMutexAttributes { |
| pthread_mutexattr_t Value; |
| struct { |
| /** |
| * Can be one of the following values: |
| * |
| * - PTHREAD_MUTEX_NORMAL |
| * - PTHREAD_MUTEX_ERRORCHECK |
| * - PTHREAD_MUTEX_RECURSIVE |
| */ |
| int Type : 4; |
| /** |
| * Zero for `PTHREAD_PROCESS_PRIVATE` and non-zero for |
| * `PTHREAD_PROCESS_SHARED`. |
| * |
| * Currently, winpthreads only supports `PTHREAD_PROCESS_PRIVATE`. |
| */ |
| int Shared : 1; |
| /** |
| * Zero for `PTHREAD_MUTEX_STALLED` and non-zero for |
| * `PTHREAD_MUTEX_ROBUST`. |
| */ |
| int Robust : 1; |
| int Reserved : 2; |
| /** |
| * Can be one of the following values: |
| * |
| * - PTHREAD_PRIO_NONE |
| * - PTHREAD_PRIO_INHERIT |
| * - PTHREAD_PRIO_PROTECT |
| * |
| * Currently, winpthreads does not implement POSIX realtime extensions. |
| * As such, the only supported value is `PTHREAD_PRIO_NONE`. |
| */ |
| int Protocol : 8; |
| /** |
| * Can be any valid `THREAD_PRIORITY_*` value. |
| * |
| * Only has effect when `Protocol` is `PTHREAD_PRIO_INHERIT` or |
| * `PTHREAD_PRIO_PROTECT`, which are not currently supported. |
| */ |
| int PriorityCeiling : 8; |
| #define WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC 0x67 |
| /** |
| * Must be `WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC`. |
| * |
| * If needed, these bits may be used for future extensions. |
| */ |
| int Magic : 8; |
| } Attributes; |
| }; |
| |
| WINPTHREADS_STATIC_ASSERT (sizeof ((WinpthreadsMutexAttributes) {}.Attributes) == sizeof (pthread_mutexattr_t), ""); |
| |
| /** |
| * Expands into an expression of type `WinpthreadsMutexAttributes` |
| * corresponding to the default mutex attributes. |
| */ |
| #define WINPTHREADS_MUTEX_ATTRIBUTES_DEFAULT ((WinpthreadsMutexAttributes) { \ |
| .Attributes.Type = PTHREAD_MUTEX_NORMAL, \ |
| .Attributes.Shared = PTHREAD_PROCESS_PRIVATE, \ |
| .Attributes.Robust = PTHREAD_MUTEX_STALLED, \ |
| .Attributes.Reserved = 0, \ |
| .Attributes.Protocol = PTHREAD_PRIO_NONE, \ |
| .Attributes.PriorityCeiling = THREAD_PRIORITY_NORMAL, \ |
| .Attributes.Magic = WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC \ |
| }) |
| |
| #define THREAD_ID_NO_OWNER ((DWORD) -1) |
| |
| /** |
| * Mutex lock states. |
| */ |
| typedef enum { |
| /** |
| * Mutex is unlocked. |
| */ |
| Unlocked, |
| /** |
| * Mutex is locked. |
| * |
| * This state inicates that there are no blocked threads waiting for |
| * the mutex to be released. |
| * |
| * While in this state, if any thread blocks waiting for the mutex to be |
| * released, the lock state will change to `LockedWithBlocking`. |
| */ |
| Locked, |
| /** |
| * Mutex is locked. |
| * |
| * This state indicates that there can be one or more blocked threads waiting |
| * for the mutex to be released. |
| */ |
| LockedWithBlocking, |
| } WinpthreadsMutexLockState; |
| |
| /** |
| * Mutex consistency states. |
| */ |
| typedef enum { |
| /** |
| * State protected by the mutex is consistent. |
| * |
| * If the thread that owns a mutex terminates without unlocking it, |
| * the next thread which tries to lock the mutex will gain the ownership, |
| * while the state protected by the mutex will be marked as inconsistent. |
| */ |
| Consistent, |
| /** |
| * State protected by the mutex is inconsistent. |
| * |
| * This state is set by `pthread_mutex_unlock` when it detects that mutex |
| * it tries to unlock was abandoned; the mutex becomes unlocked. |
| * |
| * The next thread to gain ownership of the mutex will upgrade this state |
| * to `Inconsistent`. |
| */ |
| OwnerDied, |
| /** |
| * State protected by the mutex is inconsistent. |
| * |
| * After state protected by the mutex is marked as inconsistent, |
| * the thread that now owns the mutex has two options: |
| * |
| * 1. Call `pthread_mutex_consistent`; this will mark state protected by |
| * the mutex as consistent, and make mutex usable once again. |
| * |
| * 2. Call `pthread_mutex_unlock`; this will mark state protected by |
| * the mutex as unrecoverable, and the only permitted operation on such |
| * a mutex is to pass it to `pthread_mutex_destroy`. |
| */ |
| Inconsistent, |
| /** |
| * State protected by the mutex is unrecoverable. |
| * |
| * Once state protected by the mutex is marked as unrecoverable, the mutex |
| * becomes unusable and the only permitted operation is to destroy it. |
| */ |
| Unrecoverable, |
| } WinpthreadsMutexConsistencyState; |
| |
| /** |
| * Common base for `Winpthreads{Type}Mutex` structures defined below. |
| */ |
| typedef struct { |
| const WinpthreadsMutexVtable *Vtable; |
| } WinpthreadsMutexBase; |
| |
| /** |
| * Data specific to Normal Mutex implementation. |
| */ |
| typedef struct { |
| WinpthreadsMutexBase Base; |
| /** |
| * Auto-reset event. |
| * |
| * Normal Mutexes are implemented using auto-reset events. |
| */ |
| HANDLE Event; |
| /** |
| * One of `WinpthreadsMutexLockState` values. |
| */ |
| LONG LockState; |
| } WinpthreadsNormalMutex; |
| |
| WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsNormalMutex, Base.Vtable), ""); |
| |
| /** |
| * Data specific to Error Checking Mutex implementation. |
| */ |
| typedef struct { |
| WinpthreadsMutexBase Base; |
| /** |
| * Auto-reset event. |
| * |
| * Error Checking Mutexes are implemented using auto-reset events. |
| */ |
| HANDLE Event; |
| /** |
| * One of `WinpthreadsMutexLockState` values. |
| */ |
| LONG LockState; |
| /** |
| * ID of the thread owning the mutex. |
| * If mutex has no owner, this field is set to `THREAD_ID_NO_OWNER`. |
| */ |
| DWORD Owner; |
| } WinpthreadsErrorCheckMutex; |
| |
| WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsErrorCheckMutex, Base.Vtable), ""); |
| |
| /** |
| * Data specific to Recursive Mutex implementation. |
| */ |
| typedef struct { |
| WinpthreadsMutexBase Base; |
| /** |
| * Auto-reset event. |
| * |
| * Recursive Mutexes are implemented using auto-reset events. |
| */ |
| HANDLE Event; |
| /** |
| * One of `WinpthreadsMutexLockState` values. |
| */ |
| LONG LockState; |
| /** |
| * ID of the thread owning the mutex. |
| * If mutex has no owner, this field is set to `THREAD_ID_NO_OWNER`. |
| */ |
| DWORD Owner; |
| /** |
| * Recursive lock count. |
| * |
| * When a thread locks the mutex, this value is incremented by 1. |
| * When a thread unlocks the mutex, this value is decremented by 1. |
| * |
| * A thread owns the mutes as long as lock count is greater than zero; |
| * once lock count reaches zero, the owning thread releases the ownership of |
| * the mutex. |
| */ |
| unsigned int LockCount; |
| } WinpthreadsRecursiveMutex; |
| |
| WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsRecursiveMutex, Base.Vtable), ""); |
| |
| /** |
| * Data specific to Error Checking Robust Mutex implementation. |
| */ |
| typedef struct { |
| WinpthreadsMutexBase Base; |
| /** |
| * Mutex. |
| * |
| * Error Checking Robust Mutexes are implemented using mutexes. |
| */ |
| HANDLE Mutex; |
| /** |
| * One of `WinpthreadsMutexConsistencyState` values. |
| */ |
| LONG State; |
| /** |
| * ID of the thread owning the mutex. |
| * If mutex has no owner, this field is set to `THREAD_ID_NO_OWNER`. |
| */ |
| DWORD Owner; |
| } WinpthreadsErrorCheckRobustMutex; |
| |
| WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsErrorCheckRobustMutex, Base.Vtable), ""); |
| |
| /** |
| * Data specific to Recursive Robust Mutex implementation. |
| */ |
| typedef struct { |
| WinpthreadsMutexBase Base; |
| /** |
| * Mutex. |
| * |
| * Recursive Robust Mutexes are implemented using mutexes. |
| */ |
| HANDLE Mutex; |
| /** |
| * One of `WinpthreadsMutexConsistencyState` values. |
| */ |
| LONG State; |
| /** |
| * ID of the thread owning the mutex. |
| * If mutex has no owner, this field is set to `THREAD_ID_NO_OWNER`. |
| */ |
| DWORD Owner; |
| /** |
| * Recursive lock count. |
| * |
| * When a thread locks the mutex, this value is incremented by 1. |
| * When a thread unlocks the mutex, this value is decremented by 1. |
| * |
| * A thread owns the mutes as long as lock count is greater than zero; |
| * once lock count reaches zero, the owning thread releases the ownership of |
| * the mutex. |
| * |
| * This lock count is kept in sync with lock count for `Mutex` managed by |
| * the operating system. |
| */ |
| unsigned int LockCount; |
| } WinpthreadsRecursiveRobustMutex; |
| |
| WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsRecursiveRobustMutex, Base.Vtable), ""); |
| |
| /** |
| * Union pointed to by `pthread_mutex_t` objects. |
| */ |
| union WinpthreadsMutex { |
| WinpthreadsMutexBase Base; |
| WinpthreadsNormalMutex NormalMutex; |
| WinpthreadsErrorCheckMutex ErrorCheckMutex; |
| WinpthreadsRecursiveMutex RecursiveMutex; |
| WinpthreadsErrorCheckRobustMutex ErrorCheckRobustMutex; |
| WinpthreadsRecursiveRobustMutex RecursiveRobustMutex; |
| }; |
| |
| WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsMutex, Base.Vtable), ""); |
| |
| /******************************************************************************* |
| * Common helper functions. |
| */ |
| |
| /** |
| * Common wait logic for stalled mutexes. |
| * |
| * Wait until `event` becomes signaled or timeout specified by `waitUntil` |
| * has expired. |
| * |
| * If `waitUntil` is `NULL`, this function waits indefinitely until `event` |
| * becomes signaled. |
| * |
| * Returns zero on success and an error-code on failure. |
| */ |
| static WINPTHREADS_INLINE int WinpthreadsStalledMutexTimedLock (HANDLE event, LONG *lockState, const struct _timespec64 *waitUntil) { |
| unsigned __int64 waitStartTime = 0; |
| unsigned __int64 waitEndTime = 0; |
| unsigned __int64 waitTimeout = INFINITE; |
| |
| if (waitUntil != NULL) { |
| waitStartTime = _pthread_time_in_ms (); |
| waitEndTime = _pthread_time_in_ms_from_timespec (waitUntil); |
| waitTimeout = 0; |
| |
| if (waitStartTime < waitEndTime) { |
| waitTimeout = waitEndTime - waitStartTime; |
| |
| if (waitTimeout > INFINITE) { |
| waitTimeout = INFINITE; |
| } |
| } |
| } |
| |
| /** |
| * Setting lock state to `LockedWithBlocking` prevents the fast lock path |
| * (`lockState` cannot be set to `Locked`) and it causes |
| * `Winpthreads*MutexUnlock` functions to signal `event`. |
| */ |
| LONG oldLockState = InterlockedExchange (lockState, LockedWithBlocking); |
| |
| while (oldLockState != Unlocked) { |
| switch (_pthread_wait_for_single_object (event, (DWORD) waitTimeout)) { |
| /** |
| * `event` was in signaled state (unlocked) or it became signaled |
| * within `waitTimeout`. |
| */ |
| case WAIT_OBJECT_0: |
| break; |
| /** |
| * `event` was not signaled (unlocked) before `waitTimeout` expired. |
| */ |
| case WAIT_TIMEOUT: |
| return ETIMEDOUT; |
| default: |
| return EINVAL; |
| } |
| |
| /** |
| * There is a small chance that another thread grabs the lock faster |
| * than we do; lock state is updated before event is signaled. |
| */ |
| oldLockState = InterlockedExchange (lockState, LockedWithBlocking); |
| |
| if (likely (oldLockState == Unlocked)) { |
| break; |
| } |
| |
| /** |
| * Update `waitTimeout`, if not `INFINITE`. |
| */ |
| if (waitTimeout != INFINITE) { |
| waitStartTime = _pthread_time_in_ms (); |
| |
| if (waitStartTime >= waitEndTime) { |
| return ETIMEDOUT; |
| } |
| |
| waitTimeout = waitEndTime - waitStartTime; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Common implementation for `pthread_mutex_consistent` for stalled mutexes. |
| * |
| * Always fails with `EINVAL`. |
| */ |
| static int WinpthreadsStalledMutexSetConsistentState (WinpthreadsMutex *wMutex) { |
| /** |
| * The pthread_mutex_consistent() function shall fail if: |
| * |
| * [EINVAL] |
| * The mutex object referenced by mutex is not robust or does not protect |
| * an inconsistent state. |
| */ |
| return EINVAL; |
| UNREFERENCED_PARAMETER (wMutex); |
| } |
| |
| /** |
| * Common wait logic for robust mutexes. |
| * |
| * Wait until `mutex` becomes signaled or timeout specified by `waitUntil` |
| * has expired. |
| * |
| * If `waitUntil` is `NULL` and `withTimeout` is `FALSE`, this function waits |
| * indefinitely until `mutex` becomes signaled. |
| * |
| * If `waitUntil` is `NULL` and `withTimeout` is `TRUE`, this function checks |
| * whether `mutex` is in signaled state and then returns immedeately. |
| * |
| * Returns zero on success and an error-code on failure. |
| */ |
| static WINPTHREADS_INLINE int WinpthreadsRobustMutexTimedLock (HANDLE mutex, LONG *state, BOOL withTimeout, const struct _timespec64 *waitUntil) { |
| unsigned __int64 waitStartTime = 0; |
| unsigned __int64 waitEndTime = 0; |
| unsigned __int64 waitTimeout = INFINITE; |
| |
| if (withTimeout || waitUntil != NULL) { |
| waitTimeout = 0; |
| } |
| |
| if (waitUntil != NULL) { |
| waitStartTime = _pthread_time_in_ms (); |
| waitEndTime = _pthread_time_in_ms_from_timespec (waitUntil); |
| |
| if (waitStartTime < waitEndTime) { |
| waitTimeout = waitEndTime - waitStartTime; |
| |
| if (waitTimeout > INFINITE) { |
| waitTimeout = INFINITE; |
| } |
| } |
| } |
| |
| DWORD errorCode; |
| int error_code; |
| |
| switch (_pthread_wait_for_single_object (mutex, (DWORD) waitTimeout)) { |
| /** |
| * `mutex` was in signaled state (unlocked) or it became signaled before |
| * `waitTimeout` expired. |
| */ |
| case WAIT_OBJECT_0: |
| error_code = 0; |
| break; |
| /** |
| * Previous owner of `mutex` terminated without releasing it; |
| * the calling thread owns the mutex now. |
| */ |
| case WAIT_ABANDONED: |
| /** |
| * Set `state` to `Inconsistent`. |
| * |
| * There is a non-zero possibility that `state` is `Unrecoverable`, |
| * but the previous owner of `mutex` terminated before releasing it |
| * following the unlikely path below. |
| * |
| * In such case, `state` must remain `Unrecoverable`. |
| */ |
| if (likely (*state != Unrecoverable)) { |
| *state = Inconsistent; |
| } |
| error_code = EOWNERDEAD; |
| break; |
| /** |
| * `mutex` was not signaled (unlocked) before `waitTimeout` expired. |
| */ |
| case WAIT_TIMEOUT: |
| error_code = waitUntil == NULL ? EBUSY : ETIMEDOUT; |
| break; |
| case WAIT_FAILED: |
| errorCode = GetLastError (); |
| |
| /** |
| * Recursive lock count has been exceeded. |
| */ |
| if (errorCode == ERROR_MUTANT_LIMIT_EXCEEDED) { |
| error_code = EAGAIN; |
| break; |
| } |
| |
| /* FALLTHROUGH */ |
| default: |
| error_code = EINVAL; |
| break; |
| } |
| |
| if (unlikely (*state == Unrecoverable)) { |
| switch (error_code) { |
| case EOWNERDEAD: |
| case 0: |
| /** |
| * If state protected by the mutex was marked as unrecoverable, |
| * release all other threads waiting on `mutex` one by one. |
| */ |
| ReleaseMutex (mutex); |
| /* FALLTHROUGH */ |
| case ETIMEDOUT: |
| case EBUSY: |
| error_code = ENOTRECOVERABLE; |
| } |
| } |
| |
| return error_code; |
| } |
| |
| /******************************************************************************* |
| * Normal (PTHREAD_MUTEX_NORMAL) Stalled (PTHREAD_MUTEX_STALLED) Mutex |
| * implementation. |
| * |
| * Normal Mutexes have the following properties: |
| * |
| * - Attempt to relock a mutex that is already owned by the calling thread |
| * results in a dead lock. |
| * - Attempt to unlock a mutex that is not owned by the calling thread is UB. |
| * - Attempt to unlock an unlocked mutex is UB. |
| * |
| * Historically, in winpthreads, Normal Mutexes ignore ownership, |
| * which results in the following well-defined behavior: |
| * |
| * - Calls to `pthread_mutex_unlock` always succeed; and |
| * - A thread can unlock mutex owned by another thread. |
| */ |
| |
| static int WinpthreadsNormalMutexInit (WinpthreadsMutex **wMutex, const WinpthreadsMutexAttributes *wMutexAttr) { |
| WinpthreadsNormalMutex *mutex = malloc (sizeof (WinpthreadsNormalMutex)); |
| |
| /** |
| * The pthread_mutex_init() function shall fail if: |
| * |
| * [ENOMEM] |
| * Insufficient memory exists to initialize the mutex. |
| */ |
| if (mutex == NULL) { |
| return ENOMEM; |
| } |
| |
| mutex->Event = CreateEventW (NULL, FALSE, FALSE, NULL); |
| |
| /** |
| * The pthread_mutex_init() function shall fail if: |
| * |
| * [EAGAIN] |
| * The system lacked the necessary resources (other than memory) to |
| * initialize another mutex. |
| */ |
| if (mutex->Event == NULL) { |
| free (mutex); |
| return EAGAIN; |
| } |
| |
| mutex->LockState = Unlocked; |
| *wMutex = (WinpthreadsMutex *) mutex; |
| |
| return 0; |
| UNREFERENCED_PARAMETER (wMutexAttr); |
| } |
| |
| static void WinpthreadsNormalMutexDestroy (WinpthreadsMutex *wMutex) { |
| WinpthreadsNormalMutex *mutex = &wMutex->NormalMutex; |
| HANDLE event = InterlockedExchangePointer ((void **) &mutex->Event, NULL); |
| |
| if (event != NULL) { |
| CloseHandle (event); |
| } |
| |
| free (mutex); |
| } |
| |
| static int WinpthreadsNormalMutexLock (WinpthreadsMutex *wMutex, const struct _timespec64 *waitUntil) { |
| WinpthreadsNormalMutex *mutex = &wMutex->NormalMutex; |
| |
| /** |
| * Try the fast path. |
| */ |
| if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) == Unlocked) { |
| return 0; |
| } |
| |
| return WinpthreadsStalledMutexTimedLock (mutex->Event, &mutex->LockState, waitUntil); |
| } |
| |
| static int WinpthreadsNormalMutexTryLock (WinpthreadsMutex *wMutex, BOOL exclusiveLock) { |
| WinpthreadsNormalMutex *mutex = &wMutex->NormalMutex; |
| |
| if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) != Unlocked) { |
| return EBUSY; |
| } |
| |
| return 0; |
| UNREFERENCED_PARAMETER (exclusiveLock); |
| } |
| |
| static int WinpthreadsNormalMutexUnlock (WinpthreadsMutex *wMutex) { |
| WinpthreadsNormalMutex *mutex = &wMutex->NormalMutex; |
| |
| /** |
| * If `mutex->LockState` is `LockedWithBlocking`, then some other thread is |
| * waiting for `mutex->Event` to become signaled. |
| */ |
| if (InterlockedExchange (&mutex->LockState, Unlocked) == LockedWithBlocking) { |
| if (!SetEvent (mutex->Event)) { |
| return EINVAL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static const WinpthreadsMutexVtable WinpthreadsNormalMutexVtable = { |
| .Init = WinpthreadsNormalMutexInit, |
| .Destroy = WinpthreadsNormalMutexDestroy, |
| .Lock = WinpthreadsNormalMutexLock, |
| .TryLock = WinpthreadsNormalMutexTryLock, |
| .Unlock = WinpthreadsNormalMutexUnlock, |
| .SetConsistentState = WinpthreadsStalledMutexSetConsistentState, |
| }; |
| |
| /******************************************************************************* |
| * Error Checking (PTHREAD_MUTEX_ERRORCHECK) Stalled (PTHREAD_MUTEX_STALLED) |
| * Mutex implementation. |
| * |
| * Error Checking Mutexes have the following properties: |
| * |
| * - Attempt to relock a mutex that is already owned by the calling thread |
| * fails with EDEADLK. |
| * - Attempt to unlock a mutex that is not owned by the calling thread fails |
| * with EPERM. |
| * - Attempt to unlock an unlocked mutex fails with EPERM. |
| */ |
| |
| static int WinpthreadsErrorCheckMutexInit (WinpthreadsMutex **wMutex, const WinpthreadsMutexAttributes *wMutexAttr) { |
| WinpthreadsErrorCheckMutex *mutex = malloc (sizeof (WinpthreadsErrorCheckMutex)); |
| |
| /** |
| * The pthread_mutex_init() function shall fail if: |
| * |
| * [ENOMEM] |
| * Insufficient memory exists to initialize the mutex. |
| */ |
| if (mutex == NULL) { |
| return ENOMEM; |
| } |
| |
| mutex->Event = CreateEventW (NULL, FALSE, FALSE, NULL); |
| |
| /** |
| * The pthread_mutex_init() function shall fail if: |
| * |
| * [EAGAIN] |
| * The system lacked the necessary resources (other than memory) to |
| * initialize another mutex. |
| */ |
| if (mutex->Event == NULL) { |
| free (mutex); |
| return EAGAIN; |
| } |
| |
| mutex->LockState = Unlocked; |
| mutex->Owner = THREAD_ID_NO_OWNER; |
| |
| *wMutex = (WinpthreadsMutex *) mutex; |
| |
| return 0; |
| UNREFERENCED_PARAMETER (wMutexAttr); |
| } |
| |
| static void WinpthreadsErrorCheckMutexDestroy (WinpthreadsMutex *wMutex) { |
| WinpthreadsErrorCheckMutex *mutex = &wMutex->ErrorCheckMutex; |
| HANDLE event = InterlockedExchangePointer ((void **) &mutex->Event, NULL); |
| |
| if (event != NULL) { |
| CloseHandle (event); |
| } |
| |
| free (mutex); |
| } |
| |
| static int WinpthreadsErrorCheckMutexLock (WinpthreadsMutex *wMutex, const struct _timespec64 *waitUntil) { |
| WinpthreadsErrorCheckMutex *mutex = &wMutex->ErrorCheckMutex; |
| |
| DWORD threadId = GetCurrentThreadId (); |
| |
| if (mutex->Owner == threadId) { |
| return EDEADLK; |
| } |
| |
| /** |
| * Try the fast path. |
| */ |
| if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) == Unlocked) { |
| goto done; |
| } |
| |
| int error_code = WinpthreadsStalledMutexTimedLock (mutex->Event, &mutex->LockState, waitUntil); |
| |
| if (error_code) { |
| return error_code; |
| } |
| |
| done: |
| mutex->Owner = threadId; |
| |
| return 0; |
| } |
| |
| static int WinpthreadsErrorCheckMutexTryLock (WinpthreadsMutex *wMutex, BOOL exclusiveLock) { |
| WinpthreadsErrorCheckMutex *mutex = &wMutex->ErrorCheckMutex; |
| |
| if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) != Unlocked) { |
| return EBUSY; |
| } |
| |
| mutex->Owner = GetCurrentThreadId (); |
| |
| return 0; |
| UNREFERENCED_PARAMETER (exclusiveLock); |
| } |
| |
| static int WinpthreadsErrorCheckMutexUnlock (WinpthreadsMutex *wMutex) { |
| WinpthreadsErrorCheckMutex *mutex = &wMutex->ErrorCheckMutex; |
| |
| LONG threadId = (LONG) GetCurrentThreadId (); |
| |
| if (InterlockedCompareExchange ((LONG *) &mutex->Owner, (LONG) THREAD_ID_NO_OWNER, threadId) != threadId) { |
| return EPERM; |
| } |
| |
| /** |
| * If `mutex->LockState` is `LockedWithBlocking`, then some other thread is |
| * waiting for `mutex->Event` to become signaled. |
| */ |
| if (InterlockedExchange (&mutex->LockState, Unlocked) == LockedWithBlocking) { |
| if (!SetEvent (mutex->Event)) { |
| return EINVAL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static const WinpthreadsMutexVtable WinpthreadsErrorCheckMutexVtable = { |
| .Init = WinpthreadsErrorCheckMutexInit, |
| .Destroy = WinpthreadsErrorCheckMutexDestroy, |
| .Lock = WinpthreadsErrorCheckMutexLock, |
| .TryLock = WinpthreadsErrorCheckMutexTryLock, |
| .Unlock = WinpthreadsErrorCheckMutexUnlock, |
| .SetConsistentState = WinpthreadsStalledMutexSetConsistentState, |
| }; |
| |
| /******************************************************************************* |
| * Recursive (PTHREAD_MUTEX_RECURSIVE) Stalled (PTHREAD_MUTEX_STALLED) Mutex |
| * implementation. |
| * |
| * Recursive Mutexes have the following properties: |
| * |
| * - Attempt to relock a mutex that is already owned by the calling thread |
| * succeeds and increases recursive lock count. |
| * - Attempt to unlock a mutex that is not owned by the calling thread fails |
| * with EPERM. |
| * - Attempt to unlock an unlocked mutex fails with EPERM. |
| */ |
| |
| static int WinpthreadsRecursiveMutexInit (WinpthreadsMutex **wMutex, const WinpthreadsMutexAttributes *wMutexAttr) { |
| WinpthreadsRecursiveMutex *mutex = malloc (sizeof (WinpthreadsRecursiveMutex)); |
| |
| /** |
| * The pthread_mutex_init() function shall fail if: |
| * |
| * [ENOMEM] |
| * Insufficient memory exists to initialize the mutex. |
| */ |
| if (mutex == NULL) { |
| return ENOMEM; |
| } |
| |
| mutex->Event = CreateEventW (NULL, FALSE, FALSE, NULL); |
| |
| /** |
| * The pthread_mutex_init() function shall fail if: |
| * |
| * [EAGAIN] |
| * The system lacked the necessary resources (other than memory) to |
| * initialize another mutex. |
| */ |
| if (mutex->Event == NULL) { |
| free (mutex); |
| return EAGAIN; |
| } |
| |
| mutex->LockState = Unlocked; |
| mutex->Owner = THREAD_ID_NO_OWNER; |
| mutex->LockCount = 0; |
| |
| *wMutex = (WinpthreadsMutex *) mutex; |
| |
| return 0; |
| UNREFERENCED_PARAMETER (wMutexAttr); |
| } |
| |
| static void WinpthreadsRecursiveMutexDestroy (WinpthreadsMutex *wMutex) { |
| WinpthreadsRecursiveMutex *mutex = &wMutex->RecursiveMutex; |
| HANDLE event = InterlockedExchangePointer ((void **) &mutex->Event, NULL); |
| |
| if (event != NULL) { |
| CloseHandle (event); |
| } |
| |
| free (mutex); |
| } |
| |
| static int WinpthreadsRecursiveMutexLock (WinpthreadsMutex *wMutex, const struct _timespec64 *waitUntil) { |
| WinpthreadsRecursiveMutex *mutex = &wMutex->RecursiveMutex; |
| |
| DWORD threadId = GetCurrentThreadId (); |
| |
| /** |
| * If calling thread already owns the mutex, simply increment the lock count. |
| */ |
| if (mutex->Owner == threadId) { |
| if (unlikely (mutex->LockCount == UINT_MAX)) { |
| return EAGAIN; |
| } |
| |
| mutex->LockCount++; |
| return 0; |
| } |
| |
| /** |
| * Try the fast path. |
| */ |
| if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) == Unlocked) { |
| goto done; |
| } |
| |
| int error_code = WinpthreadsStalledMutexTimedLock (mutex->Event, &mutex->LockState, waitUntil); |
| |
| if (error_code) { |
| return error_code; |
| } |
| |
| done: |
| mutex->Owner = threadId; |
| mutex->LockCount = 1; |
| |
| return 0; |
| } |
| |
| static int WinpthreadsRecursiveMutexTryLock (WinpthreadsMutex *wMutex, BOOL exclusiveLock) { |
| WinpthreadsRecursiveMutex *mutex = &wMutex->RecursiveMutex; |
| |
| DWORD threadId = GetCurrentThreadId (); |
| |
| /** |
| * If calling thread already owns the mutex, simply increment the lock count. |
| */ |
| if (mutex->Owner == threadId) { |
| if (unlikely (exclusiveLock)) { |
| return EBUSY; |
| } |
| |
| if (unlikely (mutex->LockCount == UINT_MAX)) { |
| return EAGAIN; |
| } |
| |
| mutex->LockCount++; |
| return 0; |
| } |
| |
| if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) != Unlocked) { |
| return EBUSY; |
| } |
| |
| mutex->Owner = threadId; |
| mutex->LockCount = 1; |
| |
| return 0; |
| } |
| |
| static int WinpthreadsRecursiveMutexUnlock (WinpthreadsMutex *wMutex) { |
| WinpthreadsRecursiveMutex *mutex = &wMutex->RecursiveMutex; |
| |
| DWORD threadId = GetCurrentThreadId (); |
| |
| if (mutex->Owner != threadId) { |
| return EPERM; |
| } |
| |
| mutex->LockCount--; |
| |
| if (mutex->LockCount > 0) { |
| return 0; |
| } |
| |
| mutex->Owner = THREAD_ID_NO_OWNER; |
| |
| /** |
| * If `mutex->LockState` is `LockedWithBlocking`, then some other thread is |
| * waiting for `mutex->Event` to become signaled. |
| */ |
| if (InterlockedExchange (&mutex->LockState, Unlocked) == LockedWithBlocking) { |
| if (!SetEvent (mutex->Event)) { |
| return EINVAL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static const WinpthreadsMutexVtable WinpthreadsRecursiveMutexVtable = { |
| .Init = WinpthreadsRecursiveMutexInit, |
| .Destroy = WinpthreadsRecursiveMutexDestroy, |
| .Lock = WinpthreadsRecursiveMutexLock, |
| .TryLock = WinpthreadsRecursiveMutexTryLock, |
| .Unlock = WinpthreadsRecursiveMutexUnlock, |
| .SetConsistentState = WinpthreadsStalledMutexSetConsistentState, |
| }; |
| |
| /******************************************************************************* |
| * Normal (PTHREAD_MUTEX_NORMAL) Robust (PTHREAD_MUTEX_ROBUST) Mutex |
| * implementation. |
| * |
| * Normal Robust Mutexes have the following properties: |
| * |
| * - Attempt to relock a mutex that is already owned by the calling thread |
| * results in a dead lock. |
| * - Attempt to unlock a mutex that is not owned by the calling thread fails |
| * with EPERM. |
| * - Attempt to unlock an unlocked mutex fails with EPERM. |
| * |
| * An attempt to obtain recursive lock on a Normal Robust Mutex mutex must |
| * result in a dead lock. However, calling `WaitForSingleObject` on a mutex |
| * owned by the calling thread succeeds and simply increments the lock count. |
| * |
| * POSIX allows `pthread_mutex_lock` to fail with `EDEADLK` when a dead lock |
| * condition has been detected; this is what we do. |
| * |
| * However, this behavior completely matches properties of Error Checking |
| * Robust Mutexes. For this reason, we use the same implementation for both. |
| */ |
| |
| /******************************************************************************* |
| * Error Checking (PTHREAD_MUTEX_ERRORCHECK) Robust (PTHREAD_MUTEX_ROBUST) |
| * Mutex implementation. |
| * |
| * Error Checking Robust Mutexes have the following properties: |
| * |
| * - Attempt to relock a mutex that is already owned by the calling thread |
| * fails with EDEADLK. |
| * - Attempt to unlock a mutex that is not owned by the calling thread fails |
| * with EPERM. |
| * - Attempt to unlock an unlocked mutex fails with EPERM. |
| */ |
| |
| static int WinpthreadsErrorCheckRobustMutexInit (WinpthreadsMutex **wMutex, const WinpthreadsMutexAttributes *wMutexAttr) { |
| WinpthreadsErrorCheckRobustMutex *mutex = malloc (sizeof (WinpthreadsErrorCheckRobustMutex)); |
| |
| /** |
| * The pthread_mutex_init() function shall fail if: |
| * |
| * [ENOMEM] |
| * Insufficient memory exists to initialize the mutex. |
| */ |
| if (mutex == NULL) { |
| return ENOMEM; |
| } |
| |
| mutex->Mutex = CreateMutexW (NULL, FALSE, NULL); |
| |
| /** |
| * The pthread_mutex_init() function shall fail if: |
| * |
| * [EAGAIN] |
| * The system lacked the necessary resources (other than memory) to |
| * initialize another mutex. |
| */ |
| if (mutex->Mutex == NULL) { |
| free (mutex); |
| return EAGAIN; |
| } |
| |
| mutex->State = Consistent; |
| mutex->Owner = THREAD_ID_NO_OWNER; |
| |
| *wMutex = (WinpthreadsMutex *) mutex; |
| |
| return 0; |
| UNREFERENCED_PARAMETER (wMutexAttr); |
| } |
| |
| static void WinpthreadsErrorCheckRobustMutexDestroy (WinpthreadsMutex *wMutex) { |
| WinpthreadsErrorCheckRobustMutex *mutex = &wMutex->ErrorCheckRobustMutex; |
| HANDLE mutexHandle = InterlockedExchangePointer ((void **) &mutex->Mutex, NULL); |
| |
| if (mutexHandle != NULL) { |
| CloseHandle (mutexHandle); |
| } |
| |
| free (mutex); |
| } |
| |
| static int WinpthreadsErrorCheckRobustMutexLock (WinpthreadsMutex *wMutex, const struct _timespec64 *waitUntil) { |
| WinpthreadsErrorCheckRobustMutex *mutex = &wMutex->ErrorCheckRobustMutex; |
| |
| /** |
| * The pthread_mutex_lock() function shall fail if: |
| * |
| * [ENOTRECOVERABLE] |
| * The state protected by the mutex is not recoverable. |
| */ |
| if (unlikely (mutex->State == Unrecoverable)) { |
| return ENOTRECOVERABLE; |
| } |
| |
| int error_code = WinpthreadsRobustMutexTimedLock (mutex->Mutex, &mutex->State, FALSE, waitUntil); |
| |
| /** |
| * Recursive lock count has been exceeded. |
| * Normally, this must never happen. |
| */ |
| if (unlikely (error_code == EAGAIN)) { |
| return EDEADLK; |
| } |
| |
| if (error_code == 0 || error_code == EOWNERDEAD) { |
| DWORD threadId = GetCurrentThreadId (); |
| |
| if (likely (error_code == 0)) { |
| /** |
| * Avoid recursive locking. |
| */ |
| if (unlikely (mutex->Owner == threadId)) { |
| ReleaseMutex (mutex->Mutex); |
| return EDEADLK; |
| } |
| |
| if (unlikely (mutex->State == OwnerDied)) { |
| mutex->State = Inconsistent; |
| error_code = EOWNERDEAD; |
| } |
| } |
| |
| mutex->Owner = threadId; |
| } |
| |
| return error_code; |
| } |
| |
| static int WinpthreadsErrorCheckRobustMutexTryLock (WinpthreadsMutex *wMutex, BOOL exclusiveLock) { |
| WinpthreadsErrorCheckRobustMutex *mutex = &wMutex->ErrorCheckRobustMutex; |
| |
| /** |
| * The pthread_mutex_trylock() function shall fail if: |
| * |
| * [ENOTRECOVERABLE] |
| * The state protected by the mutex is not recoverable. |
| */ |
| if (unlikely (mutex->State == Unrecoverable)) { |
| return ENOTRECOVERABLE; |
| } |
| |
| int error_code = WinpthreadsRobustMutexTimedLock (mutex->Mutex, &mutex->State, TRUE, NULL); |
| |
| /** |
| * Recursive lock count has been exceeded. |
| * Normally, this must never happen. |
| */ |
| if (unlikely (error_code == EAGAIN)) { |
| return EBUSY; |
| } |
| |
| if (error_code == 0 || error_code == EOWNERDEAD) { |
| DWORD threadId = GetCurrentThreadId (); |
| |
| if (likely (error_code == 0)) { |
| /** |
| * Avoid recursive locking. |
| */ |
| if (unlikely (mutex->Owner == threadId)) { |
| ReleaseMutex (mutex->Mutex); |
| return EBUSY; |
| } |
| |
| if (unlikely (mutex->State == OwnerDied)) { |
| mutex->State = Inconsistent; |
| error_code = EOWNERDEAD; |
| } |
| } |
| |
| mutex->Owner = threadId; |
| } |
| |
| return error_code; |
| UNREFERENCED_PARAMETER (exclusiveLock); |
| } |
| |
| static int WinpthreadsErrorCheckRobustMutexUnlock (WinpthreadsMutex *wMutex) { |
| WinpthreadsErrorCheckRobustMutex *mutex = &wMutex->ErrorCheckRobustMutex; |
| |
| DWORD threadId = GetCurrentThreadId (); |
| |
| /** |
| * It is possible, though very unlikely, that the previous owner of `mutex` |
| * had the same `threadId` and terminated without releasing the mutex. |
| */ |
| if (mutex->Owner != threadId) { |
| return EPERM; |
| } |
| |
| /** |
| * Attempt to obtain recursive lock. |
| * |
| * We do this in order to validate ownership of the `mutex`; in case if |
| * previous owner with the same `threadId` terminated without releasing it, |
| * we will be notified about it with `EOWNERDEAD` error. |
| */ |
| int error_code = WinpthreadsRobustMutexTimedLock (mutex->Mutex, &mutex->State, TRUE, NULL); |
| |
| switch (error_code) { |
| /** |
| * We locked the mutex. |
| */ |
| case 0: |
| ReleaseMutex (mutex->Mutex); |
| |
| /** |
| * Check if lock is recursive. |
| * |
| * This may occur if previous owner of the mutex with the same `threadId` |
| * abandoned it, and then another thread locked and unlocked it. |
| */ |
| if (unlikely (mutex->Owner != threadId)) { |
| return EPERM; |
| } |
| |
| break; |
| /** |
| * Recursive lock count has been exceeded; we own the mutex. |
| */ |
| case EAGAIN: |
| break; |
| /** |
| * An unlikely case when previous thread with the same `threadId` |
| * terminated while owning the mutex. |
| * |
| * Set `mutex->State` to `OwnerDied` and release the mutex. |
| * The next thread to gain ownership of the mutex will return `EOWNERDEAD`. |
| */ |
| case EOWNERDEAD: |
| mutex->Owner = THREAD_ID_NO_OWNER; |
| mutex->State = OwnerDied; |
| ReleaseMutex (mutex->Mutex); |
| return EPERM; |
| /** |
| * Some other thread owns the mutex. |
| * |
| * This may occur if previous owner of the mutex with the same `threadId` |
| * abandoned it, and then another thread locked it just before us. |
| */ |
| case EBUSY: |
| /** |
| * This may occur if previous owner of the mutex with the same `threadId` |
| * abandoned it, then another thread locked and unlocked it without |
| * calling `pthread_mutex_consistent`. |
| */ |
| case ENOTRECOVERABLE: |
| return EPERM; |
| default: |
| return error_code; |
| } |
| |
| mutex->Owner = THREAD_ID_NO_OWNER; |
| |
| /** |
| * Set `mutex->State` to `Unrecoverable`, which will signal unblocked threads |
| * that state protected by the mutex is unrecoverable. |
| */ |
| if (mutex->State == Inconsistent) { |
| mutex->State = Unrecoverable; |
| } |
| |
| if (!ReleaseMutex (mutex->Mutex)) { |
| return EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int WinpthreadsErrorCheckRobustMutexSetConsistentState (WinpthreadsMutex *wMutex) { |
| WinpthreadsErrorCheckRobustMutex *mutex = &wMutex->ErrorCheckRobustMutex; |
| |
| /** |
| * The pthread_mutex_consistent() function shall fail if: |
| * |
| * [EINVAL] |
| * The mutex object referenced by mutex is not robust or does not protect |
| * an inconsistent state. |
| */ |
| if (mutex->State != Inconsistent) { |
| return EINVAL; |
| } |
| |
| mutex->State = Consistent; |
| return 0; |
| } |
| |
| static const WinpthreadsMutexVtable WinpthreadsErrorCheckRobustMutexVtable = { |
| .Init = WinpthreadsErrorCheckRobustMutexInit, |
| .Destroy = WinpthreadsErrorCheckRobustMutexDestroy, |
| .Lock = WinpthreadsErrorCheckRobustMutexLock, |
| .TryLock = WinpthreadsErrorCheckRobustMutexTryLock, |
| .Unlock = WinpthreadsErrorCheckRobustMutexUnlock, |
| .SetConsistentState = WinpthreadsErrorCheckRobustMutexSetConsistentState, |
| }; |
| |
| /******************************************************************************* |
| * Recursive (PTHREAD_MUTEX_RECURSIVE) Robust (PTHREAD_MUTEX_ROBUST) |
| * Mutex implementation. |
| * |
| * Recursive Robust Mutexes have the following properties: |
| * |
| * - Attempt to relock a mutex that is already owned by the calling thread |
| * succeeds and increases recursive lock count. |
| * - Attempt to unlock a mutex that is not owned by the calling thread fails |
| * with EPERM. |
| * - Attempt to unlock an unlocked mutex fails with EPERM. |
| */ |
| |
| static int WinpthreadsRecursiveRobustMutexInit (WinpthreadsMutex **wMutex, const WinpthreadsMutexAttributes *wMutexAttr) { |
| WinpthreadsRecursiveRobustMutex *mutex = malloc (sizeof (WinpthreadsRecursiveRobustMutex)); |
| |
| /** |
| * The pthread_mutex_init() function shall fail if: |
| * |
| * [ENOMEM] |
| * Insufficient memory exists to initialize the mutex. |
| */ |
| if (mutex == NULL) { |
| return ENOMEM; |
| } |
| |
| mutex->Mutex = CreateMutexW (NULL, FALSE, NULL); |
| |
| /** |
| * The pthread_mutex_init() function shall fail if: |
| * |
| * [EAGAIN] |
| * The system lacked the necessary resources (other than memory) to |
| * initialize another mutex. |
| */ |
| if (mutex->Mutex == NULL) { |
| free (mutex); |
| return EAGAIN; |
| } |
| |
| mutex->State = Consistent; |
| mutex->Owner = THREAD_ID_NO_OWNER; |
| mutex->LockCount = 0; |
| |
| *wMutex = (WinpthreadsMutex *) mutex; |
| |
| return 0; |
| UNREFERENCED_PARAMETER (wMutexAttr); |
| } |
| |
| static void WinpthreadsRecursiveRobustMutexDestroy (WinpthreadsMutex *wMutex) { |
| WinpthreadsRecursiveRobustMutex *mutex = &wMutex->RecursiveRobustMutex; |
| HANDLE mutexHandle = InterlockedExchangePointer ((void **) &mutex->Mutex, NULL); |
| |
| if (mutexHandle != NULL) { |
| CloseHandle (mutexHandle); |
| } |
| |
| free (mutex); |
| } |
| |
| static int WinpthreadsRecursiveRobustMutexLock (WinpthreadsMutex *wMutex, const struct _timespec64 *waitUntil) { |
| WinpthreadsRecursiveRobustMutex *mutex = &wMutex->RecursiveRobustMutex; |
| |
| /** |
| * The pthread_mutex_lock() function shall fail if: |
| * |
| * [ENOTRECOVERABLE] |
| * The state protected by the mutex is not recoverable. |
| */ |
| if (unlikely (mutex->State == Unrecoverable)) { |
| return ENOTRECOVERABLE; |
| } |
| |
| int error_code = WinpthreadsRobustMutexTimedLock (mutex->Mutex, &mutex->State, FALSE, waitUntil); |
| |
| if (error_code == 0 || error_code == EOWNERDEAD) { |
| DWORD threadId = GetCurrentThreadId (); |
| |
| if (unlikely (error_code == EOWNERDEAD)) { |
| mutex->Owner = threadId; |
| mutex->LockCount = 1; |
| return EOWNERDEAD; |
| } |
| |
| if (unlikely (mutex->State == OwnerDied)) { |
| mutex->State = Inconsistent; |
| mutex->Owner = threadId; |
| mutex->LockCount = 1; |
| return EOWNERDEAD; |
| } |
| |
| if (mutex->LockCount == 0) { |
| mutex->Owner = threadId; |
| } else { |
| if (unlikely (mutex->LockCount == UINT_MAX)) { |
| ReleaseMutex (mutex->Mutex); |
| return EAGAIN; |
| } |
| } |
| |
| mutex->LockCount++; |
| } |
| |
| return error_code; |
| } |
| |
| static int WinpthreadsRecursiveRobustMutexTryLock (WinpthreadsMutex *wMutex, BOOL exclusiveLock) { |
| WinpthreadsRecursiveRobustMutex *mutex = &wMutex->RecursiveRobustMutex; |
| |
| /** |
| * The pthread_mutex_trylock() function shall fail if: |
| * |
| * [ENOTRECOVERABLE] |
| * The state protected by the mutex is not recoverable. |
| */ |
| if (unlikely (mutex->State == Unrecoverable)) { |
| return ENOTRECOVERABLE; |
| } |
| |
| int error_code = WinpthreadsRobustMutexTimedLock (mutex->Mutex, &mutex->State, TRUE, NULL); |
| |
| if (error_code == 0 || error_code == EOWNERDEAD) { |
| DWORD threadId = GetCurrentThreadId (); |
| |
| if (unlikely (error_code == EOWNERDEAD)) { |
| mutex->Owner = threadId; |
| mutex->LockCount = 1; |
| return EOWNERDEAD; |
| } |
| |
| if (unlikely (mutex->State == OwnerDied)) { |
| mutex->State = Inconsistent; |
| mutex->Owner = threadId; |
| mutex->LockCount = 1; |
| return EOWNERDEAD; |
| } |
| |
| if (mutex->LockCount == 0) { |
| mutex->Owner = threadId; |
| } else { |
| if (unlikely (exclusiveLock)) { |
| ReleaseMutex (mutex->Mutex); |
| return EBUSY; |
| } |
| |
| if (unlikely (mutex->LockCount == UINT_MAX)) { |
| ReleaseMutex (mutex->Mutex); |
| return EAGAIN; |
| } |
| } |
| |
| mutex->LockCount++; |
| } |
| |
| return error_code; |
| } |
| |
| static int WinpthreadsRecursiveRobustMutexUnlock (WinpthreadsMutex *wMutex) { |
| WinpthreadsRecursiveRobustMutex *mutex = &wMutex->RecursiveRobustMutex; |
| |
| DWORD threadId = GetCurrentThreadId (); |
| |
| /** |
| * It is possible, though very unlikely, that the previous owner of `mutex` |
| * had the same `threadId` and terminated without releasing the mutex. |
| */ |
| if (mutex->Owner != threadId) { |
| return EPERM; |
| } |
| |
| /** |
| * Attempt to obtain recursive lock. |
| * |
| * We do this in order to validate ownership of the `mutex`; in case if |
| * previous owner with the same `threadId` terminated without releasing it, |
| * we will be notified about it with `EOWNERDEAD` error. |
| */ |
| int error_code = WinpthreadsRobustMutexTimedLock (mutex->Mutex, &mutex->State, TRUE, NULL); |
| |
| switch (error_code) { |
| /** |
| * We locked the mutex. |
| */ |
| case 0: |
| ReleaseMutex (mutex->Mutex); |
| |
| /** |
| * Check if lock is recursive. |
| * |
| * This may occur if previous owner of the mutex with the same `threadId` |
| * abandoned it, and then another thread locked and unlocked it. |
| */ |
| if (unlikely (mutex->Owner != threadId)) { |
| return EPERM; |
| } |
| |
| break; |
| /** |
| * Recursive lock count has been exceeded; we own the mutex. |
| */ |
| case EAGAIN: |
| break; |
| /** |
| * An unlikely case when previous thread with the same `threadId` |
| * terminated while owning the mutex. |
| * |
| * Set `mutex->State` to `OwnerDied` and release the mutex. |
| * The next thread to gain ownership of the mutex will return `EOWNERDEAD`. |
| */ |
| case EOWNERDEAD: |
| mutex->Owner = THREAD_ID_NO_OWNER; |
| mutex->State = OwnerDied; |
| ReleaseMutex (mutex->Mutex); |
| return EPERM; |
| /** |
| * Some other thread owns the mutex. |
| * |
| * This may occur if previous owner of the mutex with the same `threadId` |
| * abandoned it, and then another thread locked it just before us. |
| */ |
| case EBUSY: |
| /** |
| * This may occur if previous owner of the mutex with the same `threadId` |
| * abandoned it, then another thread locked and unlocked it without |
| * calling `pthread_mutex_consistent`. |
| */ |
| case ENOTRECOVERABLE: |
| return EPERM; |
| default: |
| return error_code; |
| } |
| |
| mutex->LockCount--; |
| |
| /** |
| * When a mutex protecting an inconsistent state is unlocked without prior |
| * call to `pthread_mutex_consistent`, the state protected by the mutex |
| * must be marked as unrecoverable. |
| * |
| * POSIX, however, does specify behavior when the mutex protecting an |
| * inconsistent state is locked recursively. |
| * |
| * For recursive mutexes, we treat "unlocking" as releasing ownership |
| * of the mutex. This means that state will be marked as unrecoverable only |
| * when the owning thread releases ownership of the mutex. |
| */ |
| if (mutex->LockCount > 0) { |
| ReleaseMutex (mutex->Mutex); |
| return 0; |
| } |
| |
| mutex->Owner = THREAD_ID_NO_OWNER; |
| |
| /** |
| * Set `mutex->State` to `Unrecoverable`, which will signal released threads |
| * that state protected by the mutex is unrecoverable. |
| */ |
| if (mutex->State == Inconsistent) { |
| mutex->State = Unrecoverable; |
| } |
| |
| if (!ReleaseMutex (mutex->Mutex)) { |
| return EPERM; |
| } |
| |
| return 0; |
| } |
| |
| static int WinpthreadsRecursiveRobustMutexSetConsistentState (WinpthreadsMutex *wMutex) { |
| WinpthreadsRecursiveRobustMutex *mutex = &wMutex->RecursiveRobustMutex; |
| |
| /** |
| * The pthread_mutex_consistent() function shall fail if: |
| * |
| * [EINVAL] |
| * The mutex object referenced by mutex is not robust or does not protect |
| * an inconsistent state. |
| */ |
| if (mutex->State != Inconsistent) { |
| return EINVAL; |
| } |
| |
| mutex->State = Consistent; |
| return 0; |
| } |
| |
| static const WinpthreadsMutexVtable WinpthreadsRecursiveRobustMutexVtable = { |
| .Init = WinpthreadsRecursiveRobustMutexInit, |
| .Destroy = WinpthreadsRecursiveRobustMutexDestroy, |
| .Lock = WinpthreadsRecursiveRobustMutexLock, |
| .TryLock = WinpthreadsRecursiveRobustMutexTryLock, |
| .Unlock = WinpthreadsRecursiveRobustMutexUnlock, |
| .SetConsistentState = WinpthreadsRecursiveRobustMutexSetConsistentState, |
| }; |
| |
| /******************************************************************************* |
| * Implementation for public `pthread_mutexattr_*` functions. |
| */ |
| |
| int pthread_mutexattr_init(pthread_mutexattr_t *a) |
| { |
| *a = WINPTHREADS_MUTEX_ATTRIBUTES_DEFAULT.Value; |
| return 0; |
| } |
| |
| int pthread_mutexattr_destroy(pthread_mutexattr_t *a) |
| { |
| if (a == NULL) { |
| return EINVAL; |
| } |
| |
| memset (a, 0, sizeof (pthread_mutexattr_t)); |
| return 0; |
| } |
| |
| int pthread_mutexattr_settype(pthread_mutexattr_t *a, int value) |
| { |
| if (a == NULL) { |
| return EINVAL; |
| } |
| |
| WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a; |
| |
| if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) { |
| return EINVAL; |
| } |
| |
| switch (value) { |
| case PTHREAD_MUTEX_NORMAL: |
| case PTHREAD_MUTEX_ERRORCHECK: |
| case PTHREAD_MUTEX_RECURSIVE: |
| wMutexAttr->Attributes.Type = value; |
| return 0; |
| } |
| |
| return EINVAL; |
| } |
| |
| int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *value) |
| { |
| if (a == NULL || value == NULL) { |
| return EINVAL; |
| } |
| |
| const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a; |
| |
| if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) { |
| return EINVAL; |
| } |
| |
| switch (wMutexAttr->Attributes.Type) { |
| case PTHREAD_MUTEX_NORMAL: |
| case PTHREAD_MUTEX_ERRORCHECK: |
| case PTHREAD_MUTEX_RECURSIVE: |
| *value = wMutexAttr->Attributes.Type; |
| return 0; |
| } |
| |
| return EINVAL; |
| } |
| |
| int pthread_mutexattr_setpshared(pthread_mutexattr_t *a, int value) |
| { |
| if (a == NULL) { |
| return EINVAL; |
| } |
| |
| WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a; |
| |
| if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) { |
| return EINVAL; |
| } |
| |
| switch (value) { |
| case PTHREAD_PROCESS_PRIVATE: |
| case PTHREAD_PROCESS_SHARED: |
| wMutexAttr->Attributes.Shared = value; |
| return 0; |
| } |
| |
| return EINVAL; |
| } |
| |
| int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *value) |
| { |
| if (a == NULL || value == NULL) { |
| return EINVAL; |
| } |
| |
| const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a; |
| |
| if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) { |
| return EINVAL; |
| } |
| |
| *value = !!wMutexAttr->Attributes.Shared; |
| return 0; |
| } |
| |
| int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int value) |
| { |
| if (a == NULL) { |
| return EINVAL; |
| } |
| |
| WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a; |
| |
| if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) { |
| return EINVAL; |
| } |
| |
| switch (value) { |
| case PTHREAD_PRIO_NONE: |
| wMutexAttr->Attributes.Protocol = value; |
| return 0; |
| /** |
| * POSIX realtime extensions are not implemented. |
| * |
| * The pthread_mutexattr_setprotocol() function shall fail if: |
| * |
| * [ENOTSUP] |
| * The value specified by protocol is an unsupported value. |
| */ |
| case PTHREAD_PRIO_INHERIT: |
| case PTHREAD_PRIO_PROTECT: |
| return ENOTSUP; |
| } |
| |
| return EINVAL; |
| } |
| |
| int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *value) |
| { |
| if (a == NULL || value == NULL) { |
| return EINVAL; |
| } |
| |
| const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a; |
| |
| if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) { |
| return EINVAL; |
| } |
| |
| switch (wMutexAttr->Attributes.Protocol) { |
| case PTHREAD_PRIO_NONE: |
| case PTHREAD_PRIO_INHERIT: |
| case PTHREAD_PRIO_PROTECT: |
| *value = wMutexAttr->Attributes.Protocol; |
| return 0; |
| } |
| |
| return EINVAL; |
| } |
| |
| int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int value) |
| { |
| if (a == NULL) { |
| return EINVAL; |
| } |
| |
| WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a; |
| |
| if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) { |
| return EINVAL; |
| } |
| |
| if (value >= THREAD_PRIORITY_IDLE && value <= THREAD_PRIORITY_TIME_CRITICAL) { |
| wMutexAttr->Attributes.PriorityCeiling = value; |
| return 0; |
| } |
| |
| return EINVAL; |
| } |
| |
| int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int *value) |
| { |
| if (a == NULL || value == NULL) { |
| return EINVAL; |
| } |
| |
| const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a; |
| |
| if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) { |
| return EINVAL; |
| } |
| |
| int priorityCeiling = wMutexAttr->Attributes.PriorityCeiling; |
| |
| if (priorityCeiling >= THREAD_PRIORITY_IDLE && priorityCeiling <= THREAD_PRIORITY_TIME_CRITICAL) { |
| *value = priorityCeiling; |
| return 0; |
| } |
| |
| return EINVAL; |
| } |
| |
| int pthread_mutexattr_setrobust(pthread_mutexattr_t *a, int value) |
| { |
| if (a == NULL) { |
| return EINVAL; |
| } |
| |
| WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a; |
| |
| if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) { |
| return EINVAL; |
| } |
| |
| switch (value) { |
| case PTHREAD_MUTEX_STALLED: |
| case PTHREAD_MUTEX_ROBUST: |
| wMutexAttr->Attributes.Robust = value; |
| return 0; |
| } |
| |
| return EINVAL; |
| } |
| |
| int pthread_mutexattr_getrobust(const pthread_mutexattr_t *a, int *value) |
| { |
| if (a == NULL || value == NULL) { |
| return EINVAL; |
| } |
| |
| const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a; |
| |
| if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) { |
| return EINVAL; |
| } |
| |
| *value = !!wMutexAttr->Attributes.Robust; |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * Implementation for public `pthread_mutex_*` functions. |
| */ |
| |
| /** |
| * Evaluates to non-zero if `m` is a static initializer for `pthread_mutex_t`: |
| * |
| * PTHREAD_DEFAULT_MUTEX_INITIALIZER: -1 |
| * PTHREAD_NORMAL_MUTEX_INITIALIZER: -1 |
| * PTHREAD_ERRORCHECK_MUTEX_INITIALIZER: -2 |
| * PTHREAD_RECURSIVE_MUTEX_INITIALIZER: -3 |
| */ |
| #define STATIC_MUTEX_INITIALIZER(m) ((uintptr_t)(m) >= (uintptr_t)-3) |
| |
| /** |
| * Obtain pointer to `WinpthreadsMutex` structure pointed to by `m`. |
| * |
| * If `m` points to statically initialized `pthread_mutex_t` object, |
| * allocate `WinpthreadsMutex` structure and store its address in `*m`. |
| * |
| * On success, stores pointer to `WinpthreadsMutex` structure in `*wMutex`. |
| * |
| * Returns zero on success and an error-code on failure. |
| */ |
| static WINPTHREADS_INLINE int WinpthreadsMutexGet(pthread_mutex_t *m, WinpthreadsMutex **wMutex) |
| { |
| *wMutex = (WinpthreadsMutex *)*m; |
| |
| /** |
| * We need to avoid race condition when more than one thread attempts to use |
| * same statically initialized `pthread_mutex_t` object at the same time. |
| * |
| * Store newly initialized mutex in `wMutex`, which is a local variable |
| * supplied by the caller, and only then store it in `m`. |
| * |
| * If some other thread was faster then us, destroy newly created mutex |
| * and use mutex pointed to by `m`. |
| */ |
| if (unlikely (STATIC_MUTEX_INITIALIZER (*wMutex))) { |
| WinpthreadsMutexAttributes wMutexAttr = WINPTHREADS_MUTEX_ATTRIBUTES_DEFAULT; |
| WinpthreadsMutex *volatile initializer = *wMutex; |
| |
| switch ((pthread_mutex_t)initializer) { |
| case PTHREAD_NORMAL_MUTEX_INITIALIZER: |
| wMutexAttr.Attributes.Type = PTHREAD_MUTEX_NORMAL; |
| break; |
| case PTHREAD_ERRORCHECK_MUTEX_INITIALIZER: |
| wMutexAttr.Attributes.Type = PTHREAD_MUTEX_ERRORCHECK; |
| break; |
| case PTHREAD_RECURSIVE_MUTEX_INITIALIZER: |
| wMutexAttr.Attributes.Type = PTHREAD_MUTEX_RECURSIVE; |
| break; |
| default: |
| UNREACHABLE (); |
| } |
| |
| int error_code = pthread_mutex_init ((pthread_mutex_t *)wMutex, &wMutexAttr.Value); |
| |
| if (error_code) { |
| return error_code; |
| } |
| |
| void *mutex = InterlockedCompareExchangePointer ((void **)m, *wMutex, initializer); |
| |
| /** |
| * Some other thread was faster than us. |
| */ |
| if (unlikely (mutex != initializer)) { |
| pthread_mutex_destroy ((pthread_mutex_t *)wMutex); |
| *wMutex = mutex; |
| } |
| } |
| |
| if (unlikely (*wMutex == NULL)) { |
| return EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a) |
| { |
| WinpthreadsMutexAttributes wMutexAttr = WINPTHREADS_MUTEX_ATTRIBUTES_DEFAULT; |
| |
| if (a != NULL) { |
| int value; |
| |
| wMutexAttr.Value = *a; |
| |
| /** |
| * POSIX: |
| * |
| * If an implementation detects that the value specified by the attr |
| * argument to pthread_mutex_init() does not refer to an initialized mutex |
| * attributes object, it is recommended that the function should fail and |
| * report an [EINVAL] error. |
| */ |
| if (pthread_mutexattr_gettype (&wMutexAttr.Value, &value) != 0) { |
| return EINVAL; |
| } |
| |
| if (pthread_mutexattr_getpshared (&wMutexAttr.Value, &value) != 0) { |
| return EINVAL; |
| } |
| |
| if (pthread_mutexattr_getrobust (&wMutexAttr.Value, &value) != 0) { |
| return EINVAL; |
| } |
| |
| if (pthread_mutexattr_getprotocol (&wMutexAttr.Value, &value) != 0) { |
| return EINVAL; |
| } |
| |
| if (pthread_mutexattr_getprioceiling (&wMutexAttr.Value, &value) != 0) { |
| return EINVAL; |
| } |
| |
| if (wMutexAttr.Attributes.Shared) { |
| return ENOSYS; |
| } |
| } |
| |
| const WinpthreadsMutexVtable *wMutexVtable = NULL; |
| |
| switch (wMutexAttr.Attributes.Type) { |
| case PTHREAD_MUTEX_NORMAL: |
| if (wMutexAttr.Attributes.Robust) { |
| wMutexVtable = &WinpthreadsErrorCheckRobustMutexVtable; |
| } else { |
| wMutexVtable = &WinpthreadsNormalMutexVtable; |
| } |
| break; |
| case PTHREAD_MUTEX_ERRORCHECK: |
| if (wMutexAttr.Attributes.Robust) { |
| wMutexVtable = &WinpthreadsErrorCheckRobustMutexVtable; |
| } else { |
| wMutexVtable = &WinpthreadsErrorCheckMutexVtable; |
| } |
| break; |
| case PTHREAD_MUTEX_RECURSIVE: |
| if (wMutexAttr.Attributes.Robust) { |
| wMutexVtable = &WinpthreadsRecursiveRobustMutexVtable; |
| } else { |
| wMutexVtable = &WinpthreadsRecursiveMutexVtable; |
| } |
| break; |
| default: |
| UNREACHABLE (); |
| } |
| |
| WinpthreadsMutex *wMutex = NULL; |
| |
| int error_code = wMutexVtable->Init (&wMutex, &wMutexAttr); |
| |
| if (error_code) { |
| return error_code; |
| } |
| |
| wMutex->Base.Vtable = wMutexVtable; |
| MemoryBarrier (); |
| *m = (pthread_mutex_t)wMutex; |
| |
| return 0; |
| } |
| |
| int pthread_mutex_destroy(pthread_mutex_t *m) |
| { |
| /** |
| * POSIX: |
| * |
| * If an implementation detects that the value specified by the mutex argument |
| * to pthread_mutex_destroy() does not refer to an initialized mutex, it is |
| * recommended that the function should fail and report an [EINVAL] error. |
| */ |
| if (unlikely (m == NULL)) { |
| return EINVAL; |
| } |
| |
| WinpthreadsMutex *wMutex = (WinpthreadsMutex *)*m; |
| |
| if (unlikely (wMutex == NULL)) { |
| return EINVAL; |
| } |
| |
| /** |
| * If `m` points to a static initializer, attempt to immediately invalidate |
| * it in order to reduce window for other functions to attempt using it. |
| */ |
| if (unlikely (STATIC_MUTEX_INITIALIZER (wMutex))) { |
| WinpthreadsMutex *mutex = InterlockedCompareExchangePointer ((void **)m, NULL, wMutex); |
| |
| if (likely (mutex == wMutex) || unlikely (mutex == NULL)) { |
| return 0; |
| } |
| |
| wMutex = mutex; |
| } |
| |
| int error_code = wMutex->Base.Vtable->TryLock (wMutex, TRUE); |
| |
| switch (error_code) { |
| case EOWNERDEAD: |
| case ENOTRECOVERABLE: |
| case 0: |
| break; |
| default: |
| return error_code; |
| } |
| |
| InterlockedExchangePointer ((void **)m, NULL); |
| wMutex->Base.Vtable->Destroy (wMutex); |
| |
| return 0; |
| } |
| |
| int pthread_mutex_lock(pthread_mutex_t *m) |
| { |
| WinpthreadsMutex *wMutex = NULL; |
| |
| int error_code = WinpthreadsMutexGet (m, &wMutex); |
| |
| if (error_code) { |
| return error_code; |
| } |
| |
| return wMutex->Base.Vtable->Lock (wMutex, NULL); |
| } |
| |
| int pthread_mutex_trylock(pthread_mutex_t *m) |
| { |
| WinpthreadsMutex *wMutex = NULL; |
| |
| int error_code = WinpthreadsMutexGet (m, &wMutex); |
| |
| if (error_code) { |
| return error_code; |
| } |
| |
| return wMutex->Base.Vtable->TryLock (wMutex, FALSE); |
| } |
| |
| int pthread_mutex_timedlock64(pthread_mutex_t *m, const struct _timespec64 *ts) |
| { |
| WinpthreadsMutex *wMutex = NULL; |
| |
| int error_code = WinpthreadsMutexGet (m, &wMutex); |
| |
| if (error_code) { |
| return error_code; |
| } |
| |
| /** |
| * POSIX: |
| * |
| * Under no circumstance shall the function fail with a timeout if the mutex |
| * can be locked immediately. The validity of the abstime parameter need |
| * not be checked if the mutex can be locked immediately. |
| */ |
| error_code = wMutex->Base.Vtable->TryLock (wMutex, FALSE); |
| |
| switch (error_code) { |
| /** |
| * Some thread owns the mutex. |
| */ |
| case EBUSY: |
| break; |
| /** |
| * `wMutex` is a robust mutex and its previous owner terminated without |
| * releasing it; the calling thread owns the mutex now, but state it |
| * protects is marked as inconsistent. |
| */ |
| case EOWNERDEAD: |
| /** |
| * `wMutex` is a robust mutex and state it protects was marked as |
| * unrecoverable. |
| */ |
| case ENOTRECOVERABLE: |
| /** |
| * Recursive lock count limit has been reached. |
| */ |
| case EAGAIN: |
| /** |
| * The calling thread owns the mutex now. |
| */ |
| case 0: |
| /** |
| * An unexpected error has occurred. |
| */ |
| default: |
| return error_code; |
| } |
| |
| /** |
| * The pthread_mutex_timedlock() function shall fail if: |
| * |
| * [EINVAL] |
| * The process or thread would have blocked, and the abstime parameter |
| * specified a nanoseconds field value less than zero or greater than |
| * or equal to 1000 million. |
| */ |
| if (ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000) { |
| return EINVAL; |
| } |
| |
| return wMutex->Base.Vtable->Lock (wMutex, ts); |
| } |
| |
| int pthread_mutex_timedlock32(pthread_mutex_t *m, const struct _timespec32 *ts) |
| { |
| struct _timespec64 ts64 = {.tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec}; |
| return pthread_mutex_timedlock64 (m, &ts64); |
| } |
| |
| int pthread_mutex_unlock(pthread_mutex_t *m) |
| { |
| WinpthreadsMutex *wMutex = NULL; |
| |
| int error_code = WinpthreadsMutexGet (m, &wMutex); |
| |
| if (error_code) { |
| return error_code; |
| } |
| |
| return wMutex->Base.Vtable->Unlock (wMutex); |
| } |
| |
| int pthread_mutex_consistent(pthread_mutex_t *m) |
| { |
| WinpthreadsMutex *wMutex = NULL; |
| |
| int error_code = WinpthreadsMutexGet (m, &wMutex); |
| |
| if (error_code) { |
| return error_code; |
| } |
| |
| return wMutex->Base.Vtable->SetConsistentState (wMutex); |
| } |