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;
+}