winpthreads: mutex.c: implement robust mutexes Implementation for robust (`PTHREAD_MUTEX_ROBUST`) mutexes is based directly on Windows mutexes. Unlike stalled (`PTHREAD_MUTEX_STALLED`) mutexes, robust mutexes lack fast lock paths and always require call to `WaitForSingleObject`, which may result in a very noticable overhead in certain cases. Add tests for robust mutexes. New tests correspond to those for stalled mutexes; there are two new subtests specific to robust mutexes: - robust-*-state-recoverable - robust-*-state-unrecoverable These tests verify that robust mutexes properly handle cases when a thread which owned the mutex terminated without releasing it. Signed-off-by: Kirill Makurin <maiddaisuki@outlook.com> Co-authored-by: LIU Hao <lh_mouse@126.com> Signed-off-by: LIU Hao <lh_mouse@126.com>
diff --git a/mingw-w64-libraries/winpthreads/src/mutex.c b/mingw-w64-libraries/winpthreads/src/mutex.c index 152c561..eff3ad8 100644 --- a/mingw-w64-libraries/winpthreads/src/mutex.c +++ b/mingw-w64-libraries/winpthreads/src/mutex.c
@@ -82,6 +82,28 @@ */ /** + * 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; @@ -239,6 +261,51 @@ } 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 { @@ -324,6 +391,68 @@ 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 { @@ -331,6 +460,8 @@ WinpthreadsNormalMutex NormalMutex; WinpthreadsErrorCheckMutex ErrorCheckMutex; WinpthreadsRecursiveMutex RecursiveMutex; + WinpthreadsErrorCheckRobustMutex ErrorCheckRobustMutex; + WinpthreadsRecursiveRobustMutex RecursiveRobustMutex; }; WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsMutex, Base.Vtable), ""); @@ -437,6 +568,114 @@ 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. @@ -850,6 +1089,599 @@ }; /******************************************************************************* + * 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. */ @@ -1215,21 +2047,21 @@ switch (wMutexAttr.Attributes.Type) { case PTHREAD_MUTEX_NORMAL: if (wMutexAttr.Attributes.Robust) { - return ENOSYS; + wMutexVtable = &WinpthreadsErrorCheckRobustMutexVtable; } else { wMutexVtable = &WinpthreadsNormalMutexVtable; } break; case PTHREAD_MUTEX_ERRORCHECK: if (wMutexAttr.Attributes.Robust) { - return ENOSYS; + wMutexVtable = &WinpthreadsErrorCheckRobustMutexVtable; } else { wMutexVtable = &WinpthreadsErrorCheckMutexVtable; } break; case PTHREAD_MUTEX_RECURSIVE: if (wMutexAttr.Attributes.Robust) { - return ENOSYS; + wMutexVtable = &WinpthreadsRecursiveRobustMutexVtable; } else { wMutexVtable = &WinpthreadsRecursiveMutexVtable; } @@ -1288,8 +2120,13 @@ int error_code = wMutex->Base.Vtable->TryLock (wMutex, TRUE); - if (error_code) { - return error_code; + switch (error_code) { + case EOWNERDEAD: + case ENOTRECOVERABLE: + case 0: + break; + default: + return error_code; } InterlockedExchangePointer ((void **)m, NULL); @@ -1350,6 +2187,17 @@ 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:
diff --git a/mingw-w64-libraries/winpthreads/tests/Makefile.am b/mingw-w64-libraries/winpthreads/tests/Makefile.am index 57a647d..8a30746 100644 --- a/mingw-w64-libraries/winpthreads/tests/Makefile.am +++ b/mingw-w64-libraries/winpthreads/tests/Makefile.am
@@ -148,6 +148,30 @@ pthread_mutex/recursive-trylock \ pthread_mutex/recursive-trylock-recursive \ pthread_mutex/recursive-unlock \ + pthread_mutex/robust-errorcheck-basic \ + pthread_mutex/robust-errorcheck-lock-recursive \ + pthread_mutex/robust-errorcheck-state-recoverable \ + pthread_mutex/robust-errorcheck-state-unrecoverable \ + pthread_mutex/robust-errorcheck-timedlock \ + pthread_mutex/robust-errorcheck-trylock \ + pthread_mutex/robust-errorcheck-trylock-recursive \ + pthread_mutex/robust-errorcheck-unlock \ + pthread_mutex/robust-normal-basic \ + pthread_mutex/robust-normal-lock-recursive \ + pthread_mutex/robust-normal-state-recoverable \ + pthread_mutex/robust-normal-state-unrecoverable \ + pthread_mutex/robust-normal-timedlock \ + pthread_mutex/robust-normal-trylock \ + pthread_mutex/robust-normal-trylock-recursive \ + pthread_mutex/robust-normal-unlock \ + pthread_mutex/robust-recursive-basic \ + pthread_mutex/robust-recursive-lock-recursive \ + pthread_mutex/robust-recursive-state-recoverable \ + pthread_mutex/robust-recursive-state-unrecoverable \ + pthread_mutex/robust-recursive-timedlock \ + pthread_mutex/robust-recursive-trylock \ + pthread_mutex/robust-recursive-trylock-recursive \ + pthread_mutex/robust-recursive-unlock \ pthread_once/once1 \ pthread_once/once2 \ pthread_once/once3 \
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/attr-robust.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/attr-robust.c index 39974c4..b0473f5 100644 --- a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/attr-robust.c +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/attr-robust.c
@@ -46,7 +46,7 @@ int main (void) { DoTest (PTHREAD_MUTEX_STALLED, PTHREAD_MUTEX_STALLED, 0, 0); - DoTest (PTHREAD_MUTEX_ROBUST, PTHREAD_MUTEX_ROBUST, 0, ENOSYS); + DoTest (PTHREAD_MUTEX_ROBUST, PTHREAD_MUTEX_ROBUST, 0, 0); /** * Try setting an invalid value; default attribute value must be used.
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-basic.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-basic.c new file mode 100644 index 0000000..367a9be --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-basic.c
@@ -0,0 +1,55 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Create robust error checking mutex and test basic assumptions about mutex + * ownership and lifetime. + */ + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_ERRORCHECK) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (mutex != (pthread_mutex_t) 0); + assert (pthread_mutex_lock (&mutex) == 0); + assert (pthread_mutex_lock (&mutex) == EDEADLK); + assert (pthread_mutex_trylock (&mutex) == EBUSY); + assert (pthread_mutex_destroy (&mutex) == EBUSY); + assert (pthread_mutex_consistent (&mutex) == EINVAL); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_unlock (&mutex) == EPERM); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (mutex == (pthread_mutex_t) 0); + assert (pthread_mutex_lock (&mutex) == EINVAL); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-lock-recursive.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-lock-recursive.c new file mode 100644 index 0000000..bd4e2fb --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-lock-recursive.c
@@ -0,0 +1,73 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust error checking mutex L. + * + * Thread A locks L. + * + * Thread A attempts to lock L again; the call to `pthread_mutex_lock` must + * fail with EDEADLK. + * + * Thread A unlocks L. + * + * Thread A attempts to unlock L again; the call to `pthread_mutex_unlock` must + * fail with EPERM. + * + * Thread M destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + + assert (pthread_mutex_lock (mutex) == 0); + assert (pthread_mutex_lock (mutex) == EDEADLK); + assert (pthread_mutex_unlock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == EPERM); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_ERRORCHECK) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-state-recoverable.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-state-recoverable.c new file mode 100644 index 0000000..e09c61d --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-state-recoverable.c
@@ -0,0 +1,134 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust error checking mutex L, + * then it creates threads A, B and C. + * + * A locks L. + * + * While A holds L, B enter wait state in order to lock L. + * + * While B is blocked waiting for L to be released, + * A terminates but does not release L. + * + * B gains ownership for L and is notified that state protected by L + * may be inconsistent; call to `pthread_mutex_lock` must return `EOWNERDEAD`. + * + * After B locks L, C enters wait state in order to lock L. + * + * B calls `pthread_mutex_consistent` to mark that state protected by L is + * consistent and then unlocks L. + * + * C gains ownership for L and then unlocks L. + * + * M waits for A, B and C to return, and then destroys L. + */ + +static LONG Started = FALSE; +static LONG Locked = FALSE; +static LONG Lock = FALSE; +static LONG Blocked = FALSE; + +static void *ThreadA (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Started) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == 0); + InterlockedExchange (&Locked, TRUE); + /** + * Give B time to enter wait state. + */ + Sleep (10); + + return arg; +} + +static void *ThreadB (void *arg) { + pthread_mutex_t *mutex = arg; + + InterlockedExchange (&Started, TRUE); + while (!Locked) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == EOWNERDEAD); + InterlockedExchange (&Lock, TRUE); + while (!Blocked) { + YieldProcessor (); + } + /** + * Give C time to enter wait state. + */ + Sleep (10); + assert (pthread_mutex_lock (mutex) == EDEADLK); + assert (pthread_mutex_consistent (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +static void *ThreadC (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Lock) { + YieldProcessor (); + } + InterlockedExchange (&Blocked, TRUE); + assert (pthread_mutex_lock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t threadA; + pthread_t threadB; + pthread_t threadC; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_ERRORCHECK) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&threadA, NULL, ThreadA, &mutex) == 0); + assert (pthread_create (&threadB, NULL, ThreadB, &mutex) == 0); + assert (pthread_create (&threadC, NULL, ThreadC, &mutex) == 0); + assert (pthread_join (threadA, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadB, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadC, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-state-unrecoverable.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-state-unrecoverable.c new file mode 100644 index 0000000..7ceadc9 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-state-unrecoverable.c
@@ -0,0 +1,137 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust error checking mutex L, + * then it creates threads A, B and C. + * + * A locks L. + * + * While A holds L, B enter wait state in order to lock L. + * + * While B is blocked waiting for L to be released, + * A terminates but does not release L. + * + * B gains ownership for L and is notified that state protected by L + * may be inconsistent; call to `pthread_mutex_lock` must return `EOWNERDEAD`. + * + * After B locks L, C enters wait state in order to lock L. + * + * B unlocks L without calling `pthread_mutex_consistent`, + * which marks state protected by L as unrecoverable. + * + * C fails to gain ownership for L; + * call to `pthread_mutex_lock` must fail with `ENOTRECOVERABLE`. + * + * C attempts to unlock L; + * call to `pthread_mutex_unlock` must fail with `EPERM`. + * + * M waits for A, B and C to return, and then destroys L. + */ + +static LONG Started = FALSE; +static LONG Locked = FALSE; +static LONG Lock = FALSE; +static LONG Blocked = FALSE; + +static void *ThreadA (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Started) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == 0); + InterlockedExchange (&Locked, TRUE); + /** + * Give B time to enter wait state. + */ + Sleep (10); + + return arg; +} + +static void *ThreadB (void *arg) { + pthread_mutex_t *mutex = arg; + + InterlockedExchange (&Started, TRUE); + while (!Locked) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == EOWNERDEAD); + InterlockedExchange (&Lock, TRUE); + while (!Blocked) { + YieldProcessor (); + } + /** + * Give C time to enter wait state. + */ + Sleep (10); + assert (pthread_mutex_lock (mutex) == EDEADLK); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +static void *ThreadC (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Lock) { + YieldProcessor (); + } + InterlockedExchange (&Blocked, TRUE); + assert (pthread_mutex_lock (mutex) == ENOTRECOVERABLE); + assert (pthread_mutex_unlock (mutex) == EPERM); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t threadA; + pthread_t threadB; + pthread_t threadC; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_ERRORCHECK) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&threadA, NULL, ThreadA, &mutex) == 0); + assert (pthread_create (&threadB, NULL, ThreadB, &mutex) == 0); + assert (pthread_create (&threadC, NULL, ThreadC, &mutex) == 0); + assert (pthread_join (threadA, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadB, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadC, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-timedlock.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-timedlock.c new file mode 100644 index 0000000..04a94f3 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-timedlock.c
@@ -0,0 +1,71 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust error checking mutex L and locks it. + * + * Thread A attempts to lock L with one second timeout; the call to + * `pthread_mutex_timedlock` must fail with ETIMEDOUT. + * + * Thread M unlocks and destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + struct timespec abstime = {0, 0}; + + assert (clock_gettime (CLOCK_REALTIME, &abstime) == 0); + abstime.tv_sec += 1; + assert (pthread_mutex_timedlock (mutex, &abstime) == ETIMEDOUT); + + return arg; +} + +int main (void) +{ + struct timespec abstime; + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_ERRORCHECK) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (clock_gettime (CLOCK_REALTIME, &abstime) == 0); + abstime.tv_sec += 1; + assert (pthread_mutex_timedlock (&mutex, &abstime) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-trylock-recursive.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-trylock-recursive.c new file mode 100644 index 0000000..93c64ab --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-trylock-recursive.c
@@ -0,0 +1,73 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust error checking mutex L. + * + * Thread A locks L. + * + * Thread A attempts to lock L again; the call to `pthread_mutex_trylock` must + * fail with EBUSY. + * + * Thread A unlocks L. + * + * Thread A attempts to unlock L again; the call to `pthread_mutex_unlock` must + * fail with EPERM. + * + * Thread M destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + + assert (pthread_mutex_trylock (mutex) == 0); + assert (pthread_mutex_trylock (mutex) == EBUSY); + assert (pthread_mutex_unlock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == EPERM); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_ERRORCHECK) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-trylock.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-trylock.c new file mode 100644 index 0000000..46c5dad --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-trylock.c
@@ -0,0 +1,63 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust error checking mutex L and locks it. + * + * Thread A attempts to lock L; since M owns L, call to `pthread_mutex_trylock` + * must fail with EBUSY. + * + * Thread M unlocks and destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + assert (pthread_mutex_trylock (mutex) == EBUSY); + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_ERRORCHECK) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_mutex_trylock (&mutex) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-unlock.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-unlock.c new file mode 100644 index 0000000..dd4097a --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-errorcheck-unlock.c
@@ -0,0 +1,63 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust error checking mutex L and locks it. + * + * Thread A attempts to unlock L; since M owns L, call to `pthread_mutex_unlock` + * must fail with EPERM. + * + * Thread M unlocks and destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + assert (pthread_mutex_unlock (mutex) == EPERM); + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_ERRORCHECK) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_mutex_lock (&mutex) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-basic.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-basic.c new file mode 100644 index 0000000..ac70d64 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-basic.c
@@ -0,0 +1,65 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Create robust normal mutex and test basic assumptions about mutex + * ownership and lifetime. + */ + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_NORMAL) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (mutex != (pthread_mutex_t) 0); + assert (pthread_mutex_lock (&mutex) == 0); + /** + * According to POSIX, an attempt to recursively lock normal robust mutex + * results in a deadlock. + * + * Since winpthreads implements robust mutexes using Windows mutexes, which + * support recursive locking, robust mutexes cannot deadlock in this case. + * + * POSIX allows `pthread_mutex_lock` to fail with `EDEADLK` when a deadlock + * condition has been detected; this is what we do. + */ + assert (pthread_mutex_lock (&mutex) == EDEADLK); + assert (pthread_mutex_trylock (&mutex) == EBUSY); + assert (pthread_mutex_destroy (&mutex) == EBUSY); + assert (pthread_mutex_consistent (&mutex) == EINVAL); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_unlock (&mutex) == EPERM); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (mutex == (pthread_mutex_t) 0); + assert (pthread_mutex_lock (&mutex) == EINVAL); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-lock-recursive.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-lock-recursive.c new file mode 100644 index 0000000..0df1d0d --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-lock-recursive.c
@@ -0,0 +1,83 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust normal mutex L. + * + * Thread A locks L. + * + * Thread A attempts to lock L again; the call to `pthread_mutex_lock` must + * fail with EDEADLK. + * + * Thread A unlocks L. + * + * Thread A attempts to unlock L again; the call to `pthread_mutex_unlock` must + * fail with EPERM. + * + * Thread M destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + + assert (pthread_mutex_lock (mutex) == 0); + /** + * According to POSIX, an attempt to recursively lock normal robust mutex + * results in a deadlock. + * + * Since winpthreads implements robust mutexes using Windows mutexes, which + * support recursive locking, robust mutexes cannot deadlock in this case. + * + * POSIX allows `pthread_mutex_lock` to fail with `EDEADLK` when a deadlock + * condition has been detected; this is what we do. + */ + assert (pthread_mutex_lock (mutex) == EDEADLK); + assert (pthread_mutex_unlock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == EPERM); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_NORMAL) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-state-recoverable.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-state-recoverable.c new file mode 100644 index 0000000..046c72f --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-state-recoverable.c
@@ -0,0 +1,144 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust normal mutex L, + * then it creates threads A, B and C. + * + * A locks L. + * + * While A holds L, B enter wait state in order to lock L. + * + * While B is blocked waiting for L to be released, + * A terminates but does not release L. + * + * B gains ownership for L and is notified that state protected by L + * may be inconsistent; call to `pthread_mutex_lock` must return `EOWNERDEAD`. + * + * After B locks L, C enters wait state in order to lock L. + * + * B calls `pthread_mutex_consistent` to mark that state protected by L is + * consistent and then unlocks L. + * + * C gains ownership for L and then unlocks L. + * + * M waits for A, B and C to return, and then destroys L. + */ + +static LONG Started = FALSE; +static LONG Locked = FALSE; +static LONG Lock = FALSE; +static LONG Blocked = FALSE; + +static void *ThreadA (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Started) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == 0); + InterlockedExchange (&Locked, TRUE); + /** + * Give B time to enter wait state. + */ + Sleep (10); + + return arg; +} + +static void *ThreadB (void *arg) { + pthread_mutex_t *mutex = arg; + + InterlockedExchange (&Started, TRUE); + while (!Locked) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == EOWNERDEAD); + InterlockedExchange (&Lock, TRUE); + while (!Blocked) { + YieldProcessor (); + } + /** + * Give C time to enter wait state. + */ + Sleep (10); + /** + * According to POSIX, an attempt to recursively lock normal robust mutex + * results in a deadlock. + * + * Since winpthreads implements robust mutexes using Windows mutexes, which + * support recursive locking, robust mutexes cannot deadlock in this case. + * + * POSIX allows `pthread_mutex_lock` to fail with `EDEADLK` when a deadlock + * condition has been detected; this is what we do. + */ + assert (pthread_mutex_lock (mutex) == EDEADLK); + assert (pthread_mutex_consistent (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +static void *ThreadC (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Lock) { + YieldProcessor (); + } + InterlockedExchange (&Blocked, TRUE); + assert (pthread_mutex_lock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t threadA; + pthread_t threadB; + pthread_t threadC; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_NORMAL) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&threadA, NULL, ThreadA, &mutex) == 0); + assert (pthread_create (&threadB, NULL, ThreadB, &mutex) == 0); + assert (pthread_create (&threadC, NULL, ThreadC, &mutex) == 0); + assert (pthread_join (threadA, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadB, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadC, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-state-unrecoverable.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-state-unrecoverable.c new file mode 100644 index 0000000..12deb1b --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-state-unrecoverable.c
@@ -0,0 +1,147 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust normal mutex L, + * then it creates threads A, B and C. + * + * A locks L. + * + * While A holds L, B enter wait state in order to lock L. + * + * While B is blocked waiting for L to be released, + * A terminates but does not release L. + * + * B gains ownership for L and is notified that state protected by L + * may be inconsistent; call to `pthread_mutex_lock` must return `EOWNERDEAD`. + * + * After B locks L, C enters wait state in order to lock L. + * + * B unlocks L without calling `pthread_mutex_consistent`, + * which marks state protected by L as unrecoverable. + * + * C fails to gain ownership for L; + * call to `pthread_mutex_lock` must fail with `ENOTRECOVERABLE`. + * + * C attempts to unlock L; + * call to `pthread_mutex_unlock` must fail with `EPERM`. + * + * M waits for A, B and C to return, and then destroys L. + */ + +static LONG Started = FALSE; +static LONG Locked = FALSE; +static LONG Lock = FALSE; +static LONG Blocked = FALSE; + +static void *ThreadA (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Started) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == 0); + InterlockedExchange (&Locked, TRUE); + /** + * Give B time to enter wait state. + */ + Sleep (10); + + return arg; +} + +static void *ThreadB (void *arg) { + pthread_mutex_t *mutex = arg; + + InterlockedExchange (&Started, TRUE); + while (!Locked) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == EOWNERDEAD); + InterlockedExchange (&Lock, TRUE); + while (!Blocked) { + YieldProcessor (); + } + /** + * Give C time to enter wait state. + */ + Sleep (10); + /** + * According to POSIX, an attempt to recursively lock normal robust mutex + * results in a deadlock. + * + * Since winpthreads implements robust mutexes using Windows mutexes, which + * support recursive locking, robust mutexes cannot deadlock in this case. + * + * POSIX allows `pthread_mutex_lock` to fail with `EDEADLK` when a deadlock + * condition has been detected; this is what we do. + */ + assert (pthread_mutex_lock (mutex) == EDEADLK); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +static void *ThreadC (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Lock) { + YieldProcessor (); + } + InterlockedExchange (&Blocked, TRUE); + assert (pthread_mutex_lock (mutex) == ENOTRECOVERABLE); + assert (pthread_mutex_unlock (mutex) == EPERM); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t threadA; + pthread_t threadB; + pthread_t threadC; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_NORMAL) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&threadA, NULL, ThreadA, &mutex) == 0); + assert (pthread_create (&threadB, NULL, ThreadB, &mutex) == 0); + assert (pthread_create (&threadC, NULL, ThreadC, &mutex) == 0); + assert (pthread_join (threadA, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadB, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadC, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-timedlock.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-timedlock.c new file mode 100644 index 0000000..a87bacb --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-timedlock.c
@@ -0,0 +1,71 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust normal mutex L and locks it. + * + * Thread A attempts to lock L with one second timeout; the call to + * `pthread_mutex_timedlock` must fail with ETIMEDOUT. + * + * Thread M unlocks and destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + struct timespec abstime = {0, 0}; + + assert (clock_gettime (CLOCK_REALTIME, &abstime) == 0); + abstime.tv_sec += 1; + assert (pthread_mutex_timedlock (mutex, &abstime) == ETIMEDOUT); + + return arg; +} + +int main (void) +{ + struct timespec abstime; + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_NORMAL) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (clock_gettime (CLOCK_REALTIME, &abstime) == 0); + abstime.tv_sec += 1; + assert (pthread_mutex_timedlock (&mutex, &abstime) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-trylock-recursive.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-trylock-recursive.c new file mode 100644 index 0000000..f235599 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-trylock-recursive.c
@@ -0,0 +1,73 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust normal mutex L. + * + * Thread A locks L. + * + * Thread A attempts to lock L again; the call to `pthread_mutex_trylock` must + * fail with EBUSY. + * + * Thread A unlocks L. + * + * Thread A attempts to unlock L again; the call to `pthread_mutex_unlock` must + * fail with EPERM. + * + * Thread M destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + + assert (pthread_mutex_trylock (mutex) == 0); + assert (pthread_mutex_trylock (mutex) == EBUSY); + assert (pthread_mutex_unlock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == EPERM); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_NORMAL) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-trylock.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-trylock.c new file mode 100644 index 0000000..e8ee00a --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-trylock.c
@@ -0,0 +1,63 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust normal mutex L and locks it. + * + * Thread A attempts to lock L; since M owns L, call to `pthread_mutex_trylock` + * must fail with EBUSY. + * + * Thread M unlocks and destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + assert (pthread_mutex_trylock (mutex) == EBUSY); + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_NORMAL) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_mutex_trylock (&mutex) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-unlock.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-unlock.c new file mode 100644 index 0000000..60080e0 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-normal-unlock.c
@@ -0,0 +1,63 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust normal mutex L and locks it. + * + * Thread A attempts to unlock L; since M owns L, call to `pthread_mutex_unlock` + * must fail with EPERM. + * + * Thread M unlocks and destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + assert (pthread_mutex_unlock (mutex) == EPERM); + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_NORMAL) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_mutex_lock (&mutex) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-basic.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-basic.c new file mode 100644 index 0000000..14a045e --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-basic.c
@@ -0,0 +1,57 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Create robust recursive mutex and test basic assumptions about mutex + * ownership and lifetime. + */ + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (mutex != (pthread_mutex_t) 0); + assert (pthread_mutex_lock (&mutex) == 0); + assert (pthread_mutex_lock (&mutex) == 0); + assert (pthread_mutex_trylock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == EBUSY); + assert (pthread_mutex_consistent (&mutex) == EINVAL); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_unlock (&mutex) == EPERM); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (mutex == (pthread_mutex_t) 0); + assert (pthread_mutex_lock (&mutex) == EINVAL); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-lock-recursive.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-lock-recursive.c new file mode 100644 index 0000000..d0a81f0 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-lock-recursive.c
@@ -0,0 +1,73 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust recursive mutex L. + * + * Thread A locks L. + * + * Thread A attempts to lock L again; the call to `pthread_mutex_lock` must + * succeed. + * + * Thread A unlocks L. + * + * Thread A attempts to unlock L again; the call to `pthread_mutex_unlock` must + * succeed. + * + * Thread M destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + + assert (pthread_mutex_lock (mutex) == 0); + assert (pthread_mutex_lock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-state-recoverable.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-state-recoverable.c new file mode 100644 index 0000000..730fad6 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-state-recoverable.c
@@ -0,0 +1,143 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust recursive mutex L, + * then it creates threads A, B and C. + * + * A locks L. + * + * While A holds L, B enter wait state in order to lock L. + * + * While B is blocked waiting for L to be released, + * A terminates but does not release L. + * + * B gains ownership for L and is notified that state protected by L + * may be inconsistent; call to `pthread_mutex_lock` must return `EOWNERDEAD`. + * + * After B locks L, C enters wait state in order to lock L. + * + * B recursively locks L. + * + * B calls `pthread_mutex_consistent` to mark that state protected by L is + * consistent and then unlocks L. + * + * C gains ownership for L and then unlocks L. + * + * M waits for A, B and C to return, and then destroys L. + */ + +static LONG Started = FALSE; +static LONG Locked = FALSE; +static LONG Lock = FALSE; +static LONG Blocked = FALSE; + +static void *ThreadA (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Started) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == 0); + InterlockedExchange (&Locked, TRUE); + /** + * Give B time to enter wait state. + */ + Sleep (10); + + return arg; +} + +static void *ThreadB (void *arg) { + pthread_mutex_t *mutex = arg; + + InterlockedExchange (&Started, TRUE); + while (!Locked) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == EOWNERDEAD); + InterlockedExchange (&Lock, TRUE); + while (!Blocked) { + YieldProcessor (); + } + /** + * Give C time to enter wait state. + */ + Sleep (10); + assert (pthread_mutex_lock (mutex) == 0); + assert (pthread_mutex_consistent (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == 0); + /** + * C must be blocked. + */ + Sleep (10); + assert (Blocked); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +static void *ThreadC (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Lock) { + YieldProcessor (); + } + InterlockedExchange (&Blocked, TRUE); + assert (pthread_mutex_lock (mutex) == 0); + InterlockedExchange (&Blocked, FALSE); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t threadA; + pthread_t threadB; + pthread_t threadC; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&threadA, NULL, ThreadA, &mutex) == 0); + assert (pthread_create (&threadB, NULL, ThreadB, &mutex) == 0); + assert (pthread_create (&threadC, NULL, ThreadC, &mutex) == 0); + assert (pthread_join (threadA, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadB, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadC, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-state-unrecoverable.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-state-unrecoverable.c new file mode 100644 index 0000000..6fd5093 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-state-unrecoverable.c
@@ -0,0 +1,149 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust recursive mutex L, + * then it creates threads A, B and C. + * + * A locks L. + * + * While A holds L, B enter wait state in order to lock L. + * + * While B is blocked waiting for L to be released, + * A terminates but does not release L. + * + * B gains ownership for L and is notified that state protected by L + * may be inconsistent; call to `pthread_mutex_lock` must return `EOWNERDEAD`. + * + * After B locks L, C enters wait state in order to lock L. + * + * B recursively locks L. + * + * B unlocks L without calling `pthread_mutex_consistent`; the first call to + * `pthread_mutex_unlock` must simply decrement recursive lock count. + * + * B unlocks L without calling `pthread_mutex_consistent`; the second call to + * `pthread_mutex_unlock` must mark state protected by L as unrecoverable. + * + * C fails to gain ownership for L; + * call to `pthread_mutex_lock` must fail with `ENOTRECOVERABLE`. + * + * C attempts to unlock L; + * call to `pthread_mutex_unlock` must fail with `EPERM`. + * + * M waits for A, B and C to return, and then destroys L. + */ + +static LONG Started = FALSE; +static LONG Locked = FALSE; +static LONG Lock = FALSE; +static LONG Blocked = FALSE; + +static void *ThreadA (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Started) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == 0); + InterlockedExchange (&Locked, TRUE); + /** + * Give B time to enter wait state. + */ + Sleep (10); + + return arg; +} + +static void *ThreadB (void *arg) { + pthread_mutex_t *mutex = arg; + + InterlockedExchange (&Started, TRUE); + while (!Locked) { + YieldProcessor (); + } + assert (pthread_mutex_lock (mutex) == EOWNERDEAD); + InterlockedExchange (&Lock, TRUE); + while (!Blocked) { + YieldProcessor (); + } + /** + * Give C time to enter wait state. + */ + Sleep (10); + assert (pthread_mutex_lock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == 0); + /** + * C must be blocked. + */ + Sleep (10); + assert (Blocked); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +static void *ThreadC (void *arg) { + pthread_mutex_t *mutex = arg; + + while (!Lock) { + YieldProcessor (); + } + InterlockedExchange (&Blocked, TRUE); + assert (pthread_mutex_lock (mutex) == ENOTRECOVERABLE); + InterlockedExchange (&Blocked, FALSE); + assert (pthread_mutex_unlock (mutex) == EPERM); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t threadA; + pthread_t threadB; + pthread_t threadC; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&threadA, NULL, ThreadA, &mutex) == 0); + assert (pthread_create (&threadB, NULL, ThreadB, &mutex) == 0); + assert (pthread_create (&threadC, NULL, ThreadC, &mutex) == 0); + assert (pthread_join (threadA, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadB, &result) == 0); + assert (result == &mutex); + assert (pthread_join (threadC, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-timedlock.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-timedlock.c new file mode 100644 index 0000000..a932ff1 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-timedlock.c
@@ -0,0 +1,71 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust recursive mutex L and locks it. + * + * Thread A attempts to lock L with one second timeout; the call to + * `pthread_mutex_timedlock` must fail with ETIMEDOUT. + * + * Thread M unlocks and destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + struct timespec abstime = {0, 0}; + + assert (clock_gettime (CLOCK_REALTIME, &abstime) == 0); + abstime.tv_sec += 1; + assert (pthread_mutex_timedlock (mutex, &abstime) == ETIMEDOUT); + + return arg; +} + +int main (void) +{ + struct timespec abstime; + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (clock_gettime (CLOCK_REALTIME, &abstime) == 0); + abstime.tv_sec += 1; + assert (pthread_mutex_timedlock (&mutex, &abstime) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-trylock-recursive.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-trylock-recursive.c new file mode 100644 index 0000000..e06726e --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-trylock-recursive.c
@@ -0,0 +1,73 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust recursive mutex L. + * + * Thread A locks L. + * + * Thread A attempts to lock L again; the call to `pthread_mutex_trylock` must + * succeed. + * + * Thread A unlocks L. + * + * Thread A attempts to unlock L again; the call to `pthread_mutex_unlock` must + * succeed. + * + * Thread M destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + + assert (pthread_mutex_trylock (mutex) == 0); + assert (pthread_mutex_trylock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == 0); + assert (pthread_mutex_unlock (mutex) == 0); + + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-trylock.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-trylock.c new file mode 100644 index 0000000..fb7cb23 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-trylock.c
@@ -0,0 +1,63 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust recursive mutex L and locks it. + * + * Thread A attempts to lock L; since M owns L, call to `pthread_mutex_trylock` + * must fail with EBUSY. + * + * Thread M unlocks and destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + assert (pthread_mutex_trylock (mutex) == EBUSY); + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_mutex_trylock (&mutex) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}
diff --git a/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-unlock.c b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-unlock.c new file mode 100644 index 0000000..e694642 --- /dev/null +++ b/mingw-w64-libraries/winpthreads/tests/pthread_mutex/robust-recursive-unlock.c
@@ -0,0 +1,63 @@ +/* + Copyright (c) 2026 mingw-w64 project + + 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. +*/ + +#include "test.h" + +/** + * Test Summary: + * + * Main thread M creates robust recursive mutex L and locks it. + * + * Thread A attempts to unlock L; since M owns L, call to `pthread_mutex_unlock` + * must fail with EPERM. + * + * Thread M unlocks and destroys L. + */ + +static void *ThreadA (void *arg) +{ + pthread_mutex_t *mutex = arg; + assert (pthread_mutex_unlock (mutex) == EPERM); + return arg; +} + +int main (void) +{ + pthread_mutexattr_t mutexAttr; + pthread_mutex_t mutex; + pthread_t thread; + void *result; + + assert (pthread_mutexattr_init (&mutexAttr) == 0); + assert (pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE) == 0); + assert (pthread_mutexattr_setrobust (&mutexAttr, PTHREAD_MUTEX_ROBUST) == 0); + assert (pthread_mutex_init (&mutex, &mutexAttr) == 0); + assert (pthread_mutex_lock (&mutex) == 0); + assert (pthread_create (&thread, NULL, ThreadA, &mutex) == 0); + assert (pthread_join (thread, &result) == 0); + assert (result == &mutex); + assert (pthread_mutex_unlock (&mutex) == 0); + assert (pthread_mutex_destroy (&mutex) == 0); + assert (pthread_mutexattr_destroy (&mutexAttr) == 0); + + return 0; +}