winpthreads: mutex.c: refactor implementation

For each mutex type supported by the implementation, move logic for each basic
operation to a separate function:

- It reduces amount of runtime checks
- It makes it easier to follow interaction between functions implementing
  basic operations for a specific mutex type

There is now a separate `Winpthreads{Type}Mutex` structure for each mutex type
supported by the implementation.

Structure `WinpthreadsMutex`, which is pointed to by `pthread_mutex_t` objects,
now is an union of mutex-type-specific `Winpthreads{Type}Mutex` structures.

The real allocation size of `WinpthreadsMutex` structure is the same as for
`Winpthreads{Type}Mutex` structure for a specific mutex type; this allows us to
allocate only as much memory as required to implement specific mutex type.

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 5d1e15e..3b3edf2 100644
--- a/mingw-w64-libraries/winpthreads/src/mutex.c
+++ b/mingw-w64-libraries/winpthreads/src/mutex.c
@@ -1,5 +1,5 @@
 /*
-   Copyright (c) 2011, 2014 mingw-w64 project
+   Copyright (c) 2011, 2014, 2026 mingw-w64 project
    Copyright (c) 2015 Intel Corporation
 
    Permission is hereby granted, free of charge, to any person obtaining a
@@ -25,8 +25,8 @@
 #include "config.h"
 #endif
 
-#include <malloc.h>
-#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
 
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -84,28 +84,668 @@
  * - pthread_mutexattr_setrobust()
  */
 
-typedef enum {
-  Unlocked,        /* Not locked. */
-  Locked,          /* Locked but without waiters. */
-  Waiting,         /* Locked, may have waiters. */
-} mutex_state_t;
+/**
+ * Forward declaration; see definition below.
+ */
+typedef union WinpthreadsMutex WinpthreadsMutex;
 
-typedef enum {
-  Normal,
-  Errorcheck,
-  Recursive,
-} mutex_type_t;
+/**
+ * Mutex type-specific "init" routine.
+ */
+typedef int (* FuncMutexInit) (WinpthreadsMutex **, const pthread_mutexattr_t *);
 
-/* The heap-allocated part of a mutex. */
+/**
+ * Mutex type-specific "destroy" routine.
+ */
+typedef void (* FuncMutexDestroy) (WinpthreadsMutex *);
+
+/**
+ * Mutex type-specific "lock" routine.
+ *
+ * If second argument is `NULL`, this function will block indefinitely.
+ */
+typedef int (* FuncMutexLock) (WinpthreadsMutex *, const struct _timespec64 *);
+
+/**
+ * Mutex type-specific "try lock" routine.
+ *
+ * If second argument is `TRUE`, this function will only succeed if mutex is
+ * not owned by any thread, including the calling thread.
+ */
+typedef int (* FuncMutexTryLock) (WinpthreadsMutex *, BOOL);
+
+/**
+ * Mutex type-specific "unlock" routine.
+ */
+typedef int (* FuncMutexUnlock) (WinpthreadsMutex *);
+
+/**
+ * Implementation for specific Mutex type.
+ */
 typedef struct {
-  mutex_state_t state;
-  mutex_type_t type;
-  HANDLE event;       /* Auto-reset event. */
-  unsigned rec_lock;  /* For recursive mutexes, the number of times the
-                         mutex has been locked in excess by the same thread. */
-  volatile DWORD owner;  /* For recursive and error-checking mutexes, the
-                            ID of the owning thread if the mutex is locked. */
-} mutex_impl_t;
+  FuncMutexInit Init;
+  FuncMutexDestroy Destroy;
+  FuncMutexLock Lock;
+  FuncMutexTryLock TryLock;
+  FuncMutexUnlock Unlock;
+} WinpthreadsMutexVtable;
+
+#define THREAD_ID_NO_OWNER ((DWORD) -1)
+
+/**
+ * Mutex lock states.
+ */
+typedef enum {
+  /**
+   * Mutex is unlocked.
+   */
+  Unlocked,
+  /**
+   * Mutex is locked.
+   *
+   * This state inicates that there are no blocked threads waiting for
+   * the mutex to be released.
+   *
+   * While in this state, if any thread blocks waiting for the mutex to be
+   * released, the lock state will change to `LockedWithBlocking`.
+   */
+  Locked,
+  /**
+   * Mutex is locked.
+   *
+   * This state indicates that there can be one or more blocked threads waiting
+   * for the mutex to be released.
+   */
+  LockedWithBlocking,
+} WinpthreadsMutexLockState;
+
+/**
+ * Common base for `Winpthreads{Type}Mutex` structures defined below.
+ */
+typedef struct {
+  const WinpthreadsMutexVtable *Vtable;
+} WinpthreadsMutexBase;
+
+/**
+ * Data specific to Normal Mutex implementation.
+ */
+typedef struct {
+  WinpthreadsMutexBase Base;
+  /**
+   * Auto-reset event.
+   *
+   * Normal Mutexes are implemented using auto-reset events.
+   */
+  HANDLE Event;
+  /**
+   * One of `WinpthreadsMutexLockState` values.
+   */
+  LONG LockState;
+} WinpthreadsNormalMutex;
+
+WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsNormalMutex, Base.Vtable), "");
+
+/**
+ * Data specific to Error Checking Mutex implementation.
+ */
+typedef struct {
+  WinpthreadsMutexBase Base;
+  /**
+   * Auto-reset event.
+   *
+   * Error Checking Mutexes are implemented using auto-reset events.
+   */
+  HANDLE Event;
+  /**
+   * One of `WinpthreadsMutexLockState` values.
+   */
+  LONG LockState;
+  /**
+   * ID of the thread owning the mutex.
+   * If mutex has no owner, this field is set to `THREAD_ID_NO_OWNER`.
+   */
+  DWORD Owner;
+} WinpthreadsErrorCheckMutex;
+
+WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsErrorCheckMutex, Base.Vtable), "");
+
+/**
+ * Data specific to Recursive Mutex implementation.
+ */
+typedef struct {
+  WinpthreadsMutexBase Base;
+  /**
+   * Auto-reset event.
+   *
+   * Recursive Mutexes are implemented using auto-reset events.
+   */
+  HANDLE Event;
+  /**
+   * One of `WinpthreadsMutexLockState` values.
+   */
+  LONG LockState;
+  /**
+   * ID of the thread owning the mutex.
+   * If mutex has no owner, this field is set to `THREAD_ID_NO_OWNER`.
+   */
+  DWORD Owner;
+  /**
+   * Recursive lock count.
+   *
+   * When a thread locks the mutex, this value is incremented by 1.
+   * When a thread unlocks the mutex, this value is decremented by 1.
+   *
+   * A thread owns the mutes as long as lock count is greater than zero;
+   * once lock count reaches zero, the owning thread releases the ownership of
+   * the mutex.
+   */
+  unsigned int LockCount;
+} WinpthreadsRecursiveMutex;
+
+WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsRecursiveMutex, Base.Vtable), "");
+
+/**
+ * Union pointed to by `pthread_mutex_t` objects.
+ */
+union WinpthreadsMutex {
+  WinpthreadsMutexBase       Base;
+  WinpthreadsNormalMutex     NormalMutex;
+  WinpthreadsErrorCheckMutex ErrorCheckMutex;
+  WinpthreadsRecursiveMutex  RecursiveMutex;
+};
+
+WINPTHREADS_STATIC_ASSERT (offsetof (WinpthreadsMutexBase, Vtable) == offsetof (WinpthreadsMutex, Base.Vtable), "");
+
+/*******************************************************************************
+ * Common helper functions.
+ */
+
+/**
+ * Wait until `handle` becomes signaled or timeout specified by `waitUntil`
+ * has expired. If `waitUntil` is `NULL`, this function waits indefinitely until
+ * `handle` becomes signaled.
+ *
+ * Returns zero on success and an error-code on failure.
+ */
+static WINPTHREADS_INLINE int WinpthreadsTimedWait (HANDLE handle, LONG *lockState, const struct _timespec64 *waitUntil) {
+  unsigned __int64 waitStartTime = 0;
+  unsigned __int64 waitEndTime   = 0;
+  unsigned __int64 waitTimeout   = INFINITE;
+
+  if (waitUntil != NULL) {
+    waitStartTime = _pthread_time_in_ms ();
+    waitEndTime   = _pthread_time_in_ms_from_timespec (waitUntil);
+    waitTimeout   = 0;
+
+    if (waitStartTime < waitEndTime) {
+      waitTimeout = waitEndTime - waitStartTime;
+
+      if (waitTimeout > INFINITE) {
+        waitTimeout = INFINITE;
+      }
+    }
+  }
+
+  /**
+   * Setting lock state to `LockedWithBlocking` prevents the fast lock path
+   * (`lockState` cannot be set to `Locked`) and it causes
+   * `Winpthreads*MutexUnlock` functions to signal `handle`.
+   */
+  LONG oldLockState = InterlockedExchange (lockState, LockedWithBlocking);
+
+  while (oldLockState != Unlocked) {
+    switch (_pthread_wait_for_single_object (handle, (DWORD) waitTimeout)) {
+      /**
+       * `handle` was in signaled state (unlocked) or it became signaled
+       * within `waitTimeout`.
+       */
+      case WAIT_OBJECT_0:
+        break;
+      /**
+       * `handle` was not signaled (unlocked) before `waitTimeout` expired.
+       */
+      case WAIT_TIMEOUT:
+        return ETIMEDOUT;
+      default:
+        return EINVAL;
+    }
+
+    /**
+     * There is a small chance that another thread grabs the lock faster
+     * than we do; lock state is updated before wait handle is signaled.
+     */
+    oldLockState = InterlockedExchange (lockState, LockedWithBlocking);
+
+    if (likely (oldLockState == Unlocked)) {
+      break;
+    }
+
+    /**
+     * Update `waitTimeout`, if not `INFINITE`.
+     */
+    if (waitTimeout != INFINITE) {
+      waitStartTime = _pthread_time_in_ms ();
+
+      if (waitStartTime >= waitEndTime) {
+        return ETIMEDOUT;
+      }
+
+      waitTimeout = waitEndTime - waitStartTime;
+    }
+  }
+
+  return 0;
+}
+
+/*******************************************************************************
+ * Normal Mutex (PTHREAD_MUTEX_NORMAL) implementation.
+ *
+ * Normal Mutexes have the following properties:
+ *
+ * - Attempt to relock a mutex that is already owned by the calling thread
+ *   results in a dead lock.
+ * - Attempt to unlock a mutex that is not owned by the calling thread is UB.
+ * - Attempt to unlock an unlocked mutex is UB.
+ *
+ * Historically, in winpthreads, Normal Mutexes ignore ownership,
+ * which results in the following well-defined behavior:
+ *
+ * - Calls to `pthread_mutex_unlock` always succeed; and
+ * - A thread can unlock mutex owned by another thread.
+ */
+
+static int WinpthreadsNormalMutexInit (WinpthreadsMutex **wMutex, const pthread_mutexattr_t *attr) {
+  WinpthreadsNormalMutex *mutex = malloc (sizeof (WinpthreadsNormalMutex));
+
+  /**
+   * The pthread_mutex_init() function shall fail if:
+   *
+   * [ENOMEM]
+   *   Insufficient memory exists to initialize the mutex.
+   */
+  if (mutex == NULL) {
+    return ENOMEM;
+  }
+
+  mutex->Event = CreateEventW (NULL, FALSE, FALSE, NULL);
+
+  /**
+   * The pthread_mutex_init() function shall fail if:
+   *
+   * [EAGAIN]
+   *   The system lacked the necessary resources (other than memory) to
+   *   initialize another mutex.
+   */
+  if (mutex->Event == NULL) {
+    free (mutex);
+    return EAGAIN;
+  }
+
+  mutex->LockState = Unlocked;
+  *wMutex = (WinpthreadsMutex *) mutex;
+
+  return 0;
+  UNREFERENCED_PARAMETER (attr);
+}
+
+static void WinpthreadsNormalMutexDestroy (WinpthreadsMutex *wMutex) {
+  WinpthreadsNormalMutex *mutex = &wMutex->NormalMutex;
+  HANDLE event = InterlockedExchangePointer ((void **) &mutex->Event, NULL);
+
+  if (event != NULL) {
+    CloseHandle (event);
+  }
+
+  free (mutex);
+}
+
+static int WinpthreadsNormalMutexLock (WinpthreadsMutex *wMutex, const struct _timespec64 *waitUntil) {
+  WinpthreadsNormalMutex *mutex = &wMutex->NormalMutex;
+
+  /**
+   * Try the fast path.
+   */
+  if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) == Unlocked) {
+    return 0;
+  }
+
+  return WinpthreadsTimedWait (mutex->Event, &mutex->LockState, waitUntil);
+}
+
+static int WinpthreadsNormalMutexTryLock (WinpthreadsMutex *wMutex, BOOL exclusiveLock) {
+  WinpthreadsNormalMutex *mutex = &wMutex->NormalMutex;
+
+  if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) != Unlocked) {
+    return EBUSY;
+  }
+
+  return 0;
+  UNREFERENCED_PARAMETER (exclusiveLock);
+}
+
+static int WinpthreadsNormalMutexUnlock (WinpthreadsMutex *wMutex) {
+  WinpthreadsNormalMutex *mutex = &wMutex->NormalMutex;
+
+  /**
+   * If `mutex->LockState` is `LockedWithBlocking`, then some other thread is
+   * waiting for `mutex->Event` to become signaled.
+   */
+  if (InterlockedExchange (&mutex->LockState, Unlocked) == LockedWithBlocking) {
+    if (!SetEvent (mutex->Event)) {
+      return EINVAL;
+    }
+  }
+
+  return 0;
+}
+
+static const WinpthreadsMutexVtable WinpthreadsNormalMutexVtable = {
+  .Init    = WinpthreadsNormalMutexInit,
+  .Destroy = WinpthreadsNormalMutexDestroy,
+  .Lock    = WinpthreadsNormalMutexLock,
+  .TryLock = WinpthreadsNormalMutexTryLock,
+  .Unlock  = WinpthreadsNormalMutexUnlock,
+};
+
+/*******************************************************************************
+ * Error Checking Mutex (PTHREAD_MUTEX_ERRORCHECK) implementation.
+ *
+ * Error Checking Mutexes have the following properties:
+ *
+ * - Attempt to relock a mutex that is already owned by the calling thread
+ *   fails with EDEADLK.
+ * - Attempt to unlock a mutex that is not owned by the calling thread fails
+ *   with EPERM.
+ * - Attempt to unlock an unlocked mutex fails with EPERM.
+ */
+
+static int WinpthreadsErrorCheckMutexInit (WinpthreadsMutex **wMutex, const pthread_mutexattr_t *attr) {
+  WinpthreadsErrorCheckMutex *mutex = malloc (sizeof (WinpthreadsErrorCheckMutex));
+
+  /**
+   * The pthread_mutex_init() function shall fail if:
+   *
+   * [ENOMEM]
+   *   Insufficient memory exists to initialize the mutex.
+   */
+  if (mutex == NULL) {
+    return ENOMEM;
+  }
+
+  mutex->Event = CreateEventW (NULL, FALSE, FALSE, NULL);
+
+  /**
+   * The pthread_mutex_init() function shall fail if:
+   *
+   * [EAGAIN]
+   *   The system lacked the necessary resources (other than memory) to
+   *   initialize another mutex.
+   */
+  if (mutex->Event == NULL) {
+    free (mutex);
+    return EAGAIN;
+  }
+
+  mutex->LockState = Unlocked;
+  mutex->Owner     = THREAD_ID_NO_OWNER;
+
+  *wMutex = (WinpthreadsMutex *) mutex;
+
+  return 0;
+  UNREFERENCED_PARAMETER (attr);
+}
+
+static void WinpthreadsErrorCheckMutexDestroy (WinpthreadsMutex *wMutex) {
+  WinpthreadsErrorCheckMutex *mutex = &wMutex->ErrorCheckMutex;
+  HANDLE event = InterlockedExchangePointer ((void **) &mutex->Event, NULL);
+
+  if (event != NULL) {
+    CloseHandle (event);
+  }
+
+  free (mutex);
+}
+
+static int WinpthreadsErrorCheckMutexLock (WinpthreadsMutex *wMutex, const struct _timespec64 *waitUntil) {
+  WinpthreadsErrorCheckMutex *mutex = &wMutex->ErrorCheckMutex;
+
+  DWORD threadId = GetCurrentThreadId ();
+
+  if (mutex->Owner == threadId) {
+    return EDEADLK;
+  }
+
+  /**
+   * Try the fast path.
+   */
+  if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) == Unlocked) {
+    goto done;
+  }
+
+  int error_code = WinpthreadsTimedWait (mutex->Event, &mutex->LockState, waitUntil);
+
+  if (error_code) {
+    return error_code;
+  }
+
+done:
+  mutex->Owner = threadId;
+
+  return 0;
+}
+
+static int WinpthreadsErrorCheckMutexTryLock (WinpthreadsMutex *wMutex, BOOL exclusiveLock) {
+  WinpthreadsErrorCheckMutex *mutex = &wMutex->ErrorCheckMutex;
+
+  if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) != Unlocked) {
+    return EBUSY;
+  }
+
+  mutex->Owner = GetCurrentThreadId ();
+
+  return 0;
+  UNREFERENCED_PARAMETER (exclusiveLock);
+}
+
+static int WinpthreadsErrorCheckMutexUnlock (WinpthreadsMutex *wMutex) {
+  WinpthreadsErrorCheckMutex *mutex = &wMutex->ErrorCheckMutex;
+
+  LONG threadId = (LONG) GetCurrentThreadId ();
+
+  if (InterlockedCompareExchange ((LONG *) &mutex->Owner, (LONG) THREAD_ID_NO_OWNER, threadId) != threadId) {
+    return EPERM;
+  }
+
+  /**
+   * If `mutex->LockState` is `LockedWithBlocking`, then some other thread is
+   * waiting for `mutex->Event` to become signaled.
+   */
+  if (InterlockedExchange (&mutex->LockState, Unlocked) == LockedWithBlocking) {
+    if (!SetEvent (mutex->Event)) {
+      return EINVAL;
+    }
+  }
+
+  return 0;
+}
+
+static const WinpthreadsMutexVtable WinpthreadsErrorCheckMutexVtable = {
+  .Init    = WinpthreadsErrorCheckMutexInit,
+  .Destroy = WinpthreadsErrorCheckMutexDestroy,
+  .Lock    = WinpthreadsErrorCheckMutexLock,
+  .TryLock = WinpthreadsErrorCheckMutexTryLock,
+  .Unlock  = WinpthreadsErrorCheckMutexUnlock,
+};
+
+/*******************************************************************************
+ * Recursive Mutex (PTHREAD_MUTEX_RECURSIVE) implementation.
+ *
+ * Recursive Mutexes have the following properties:
+ *
+ * - Attempt to relock a mutex that is already owned by the calling thread
+ *   succeeds and increases recursive lock count.
+ * - Attempt to unlock a mutex that is not owned by the calling thread fails
+ *   with EPERM.
+ * - Attempt to unlock an unlocked mutex fails with EPERM.
+ */
+
+static int WinpthreadsRecursiveMutexInit (WinpthreadsMutex **wMutex, const pthread_mutexattr_t *attr) {
+  WinpthreadsRecursiveMutex *mutex = malloc (sizeof (WinpthreadsRecursiveMutex));
+
+  /**
+   * The pthread_mutex_init() function shall fail if:
+   *
+   * [ENOMEM]
+   *   Insufficient memory exists to initialize the mutex.
+   */
+  if (mutex == NULL) {
+    return ENOMEM;
+  }
+
+  mutex->Event = CreateEventW (NULL, FALSE, FALSE, NULL);
+
+  /**
+   * The pthread_mutex_init() function shall fail if:
+   *
+   * [EAGAIN]
+   *   The system lacked the necessary resources (other than memory) to
+   *   initialize another mutex.
+   */
+  if (mutex->Event == NULL) {
+    free (mutex);
+    return EAGAIN;
+  }
+
+  mutex->LockState = Unlocked;
+  mutex->Owner     = THREAD_ID_NO_OWNER;
+  mutex->LockCount = 0;
+
+  *wMutex = (WinpthreadsMutex *) mutex;
+
+  return 0;
+  UNREFERENCED_PARAMETER (attr);
+}
+
+static void WinpthreadsRecursiveMutexDestroy (WinpthreadsMutex *wMutex) {
+  WinpthreadsRecursiveMutex *mutex = &wMutex->RecursiveMutex;
+  HANDLE event = InterlockedExchangePointer ((void **) &mutex->Event, NULL);
+
+  if (event != NULL) {
+    CloseHandle (event);
+  }
+
+  free (mutex);
+}
+
+static int WinpthreadsRecursiveMutexLock (WinpthreadsMutex *wMutex, const struct _timespec64 *waitUntil) {
+  WinpthreadsRecursiveMutex *mutex = &wMutex->RecursiveMutex;
+
+  DWORD threadId = GetCurrentThreadId ();
+
+  /**
+   * If calling thread already owns the mutex, simply increment the lock count.
+   */
+  if (mutex->Owner == threadId) {
+    if (unlikely (mutex->LockCount == UINT_MAX)) {
+      return EAGAIN;
+    }
+
+    mutex->LockCount++;
+    return 0;
+  }
+
+  /**
+   * Try the fast path.
+   */
+  if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) == Unlocked) {
+    goto done;
+  }
+
+  int error_code = WinpthreadsTimedWait (mutex->Event, &mutex->LockState, waitUntil);
+
+  if (error_code) {
+    return error_code;
+  }
+
+done:
+  mutex->Owner = threadId;
+  mutex->LockCount = 1;
+
+  return 0;
+}
+
+static int WinpthreadsRecursiveMutexTryLock (WinpthreadsMutex *wMutex, BOOL exclusiveLock) {
+  WinpthreadsRecursiveMutex *mutex = &wMutex->RecursiveMutex;
+
+  DWORD threadId = GetCurrentThreadId ();
+
+  /**
+   * If calling thread already owns the mutex, simply increment the lock count.
+   */
+  if (mutex->Owner == threadId) {
+    if (unlikely (exclusiveLock)) {
+      return EBUSY;
+    }
+
+    if (unlikely (mutex->LockCount == UINT_MAX)) {
+      return EAGAIN;
+    }
+
+    mutex->LockCount++;
+    return 0;
+  }
+
+  if (InterlockedCompareExchange (&mutex->LockState, Locked, Unlocked) != Unlocked) {
+    return EBUSY;
+  }
+
+  mutex->Owner = threadId;
+  mutex->LockCount = 1;
+
+  return 0;
+}
+
+static int WinpthreadsRecursiveMutexUnlock (WinpthreadsMutex *wMutex) {
+  WinpthreadsRecursiveMutex *mutex = &wMutex->RecursiveMutex;
+
+  DWORD threadId = GetCurrentThreadId ();
+
+  if (mutex->Owner != threadId) {
+    return EPERM;
+  }
+
+  mutex->LockCount--;
+
+  if (mutex->LockCount > 0) {
+    return 0;
+  }
+
+  mutex->Owner = THREAD_ID_NO_OWNER;
+
+  /**
+   * If `mutex->LockState` is `LockedWithBlocking`, then some other thread is
+   * waiting for `mutex->Event` to become signaled.
+   */
+  if (InterlockedExchange (&mutex->LockState, Unlocked) == LockedWithBlocking) {
+    if (!SetEvent (mutex->Event)) {
+      return EINVAL;
+    }
+  }
+
+  return 0;
+}
+
+static const WinpthreadsMutexVtable WinpthreadsRecursiveMutexVtable = {
+  .Init    = WinpthreadsRecursiveMutexInit,
+  .Destroy = WinpthreadsRecursiveMutexDestroy,
+  .Lock    = WinpthreadsRecursiveMutexLock,
+  .TryLock = WinpthreadsRecursiveMutexTryLock,
+  .Unlock  = WinpthreadsRecursiveMutexUnlock,
+};
+
+/*******************************************************************************
+ * Implementation for public `pthread_mutex_*` functions.
+ */
 
 /**
  * Evaluates to non-zero if `m` is a static initializer for `pthread_mutex_t`:
@@ -118,34 +758,34 @@
 #define STATIC_MUTEX_INITIALIZER(m) ((uintptr_t)(m) >= (uintptr_t)-3)
 
 /**
- * Obtain pointer to `mutex_impl_t` structure pointed to by `m`.
+ * Obtain pointer to `WinpthreadsMutex` structure pointed to by `m`.
  *
- * If `m` points to statically initialzied `pthread_mutex_t` object,
- * allocate `mutex_impl_t` structure and store its address in `*m`.
+ * If `m` points to statically initialized `pthread_mutex_t` object,
+ * allocate `WinpthreadsMutex` structure and store its address in `*m`.
  *
- * On success, stores pointer to `mutex_impl_t` structure in `*mi`.
+ * On success, stores pointer to `WinpthreadsMutex` structure in `*wMutex`.
  *
  * Returns zero on success and an error-code on failure.
  */
-static WINPTHREADS_INLINE int mutex_impl(pthread_mutex_t *m, mutex_impl_t **mi)
+static WINPTHREADS_INLINE int WinpthreadsMutexGet(pthread_mutex_t *m, WinpthreadsMutex **wMutex)
 {
-  *mi = (mutex_impl_t *)*m;
+  *wMutex = (WinpthreadsMutex *)*m;
 
   /**
    * We need to avoid race condition when more than one thread attempts to use
    * same statically initialized `pthread_mutex_t` object at the same time.
    *
-   * Store newly initialized mutex in `mi`, which is a local variable supplied
-   * by the caller, and only then store it in `m`.
+   * Store newly initialized mutex in `wMutex`, which is a local variable
+   * supplied by the caller, and only then store it in `m`.
    *
    * If some other thread was faster then us, destroy newly created mutex
    * and use mutex pointed to by `m`.
    */
-  if (unlikely (STATIC_MUTEX_INITIALIZER (*mi))) {
+  if (unlikely (STATIC_MUTEX_INITIALIZER (*wMutex))) {
     pthread_mutexattr_t mutexAttr;
     int mutexType;
 
-    mutex_impl_t *volatile initializer = *mi;
+    WinpthreadsMutex *volatile initializer = *wMutex;
 
     switch ((pthread_mutex_t)initializer) {
       case PTHREAD_NORMAL_MUTEX_INITIALIZER:
@@ -168,95 +808,84 @@
     }
 
     pthread_mutexattr_settype (&mutexAttr, mutexType);
-    error_code = pthread_mutex_init ((pthread_mutex_t *)mi, &mutexAttr);
+    error_code = pthread_mutex_init ((pthread_mutex_t *)wMutex, &mutexAttr);
     pthread_mutexattr_destroy (&mutexAttr);
 
     if (error_code) {
       return error_code;
     }
 
-    void *mutex = InterlockedCompareExchangePointer ((void **)m, *mi, initializer);
+    void *mutex = InterlockedCompareExchangePointer ((void **)m, *wMutex, initializer);
 
     /**
      * Some other thread was faster than us.
      */
     if (unlikely (mutex != initializer)) {
-      pthread_mutex_destroy ((pthread_mutex_t *)mi);
-      *mi = mutex;
+      pthread_mutex_destroy ((pthread_mutex_t *)wMutex);
+      *wMutex = mutex;
     }
   }
 
-  if (unlikely (*mi == NULL)) {
+  if (unlikely (*wMutex == NULL)) {
     return EINVAL;
   }
 
   return 0;
 }
 
-/* Lock a mutex. Give up after 'timeout' ms (with ETIMEDOUT),
-   or never if timeout=INFINITE. */
-static WINPTHREADS_INLINE int pthread_mutex_lock_intern(pthread_mutex_t *m, DWORD timeout)
+int pthread_mutex_lock(pthread_mutex_t *m)
 {
-  mutex_impl_t *mi = NULL;
+  WinpthreadsMutex *wMutex = NULL;
 
-  int error_code = mutex_impl (m, &mi);
+  int error_code = WinpthreadsMutexGet (m, &wMutex);
 
   if (error_code) {
     return error_code;
   }
 
-  mutex_state_t old_state = InterlockedExchange((long *)&mi->state, Locked);
-  if (unlikely(old_state != Unlocked)) {
-    /* The mutex is already locked. */
-
-    if (mi->type != Normal) {
-      /* Recursive or Errorcheck */
-      if (mi->owner == GetCurrentThreadId()) {
-        /* FIXME: A recursive mutex should not need two atomic ops when locking
-           recursively.  We could rewrite by doing compare-and-swap instead of
-           test-and-set the first time, but it would lead to more code
-           duplication and add a conditional branch to the critical path. */
-        InterlockedCompareExchange((long *)&mi->state, old_state, Locked);
-        if (mi->type == Recursive) {
-          mi->rec_lock++;
-          return 0;
-        } else {
-          /* type == Errorcheck */
-          return EDEADLK;
-        }
-      }
-    }
-
-    while (InterlockedExchange((long *)&mi->state, Waiting) != Unlocked) {
-      /* For timed locking attempts, it is possible (although unlikely)
-         that we are woken up but someone else grabs the lock before us,
-         and we have to go back to sleep again. In that case, the total
-         wait may be longer than expected. */
-      unsigned r = _pthread_wait_for_single_object(mi->event, timeout);
-      switch (r) {
-      case WAIT_TIMEOUT:
-        return ETIMEDOUT;
-      case WAIT_OBJECT_0:
-        break;
-      default:
-        return EINVAL;
-      }
-    }
-  }
-
-  if (mi->type != Normal)
-    mi->owner = GetCurrentThreadId();
-
-  return 0;
-}
-
-int pthread_mutex_lock(pthread_mutex_t *m)
-{
-  return pthread_mutex_lock_intern (m, INFINITE);
+  return wMutex->Base.Vtable->Lock (wMutex, NULL);
 }
 
 int pthread_mutex_timedlock64(pthread_mutex_t *m, const struct _timespec64 *ts)
 {
+  WinpthreadsMutex *wMutex = NULL;
+
+  int error_code = WinpthreadsMutexGet (m, &wMutex);
+
+  if (error_code) {
+    return error_code;
+  }
+
+  /**
+   * POSIX:
+   *
+   * Under no circumstance shall the function fail with a timeout if the mutex
+   * can be locked immediately. The validity of the abstime parameter need
+   * not be checked if the mutex can be locked immediately.
+   */
+  error_code = wMutex->Base.Vtable->TryLock (wMutex, FALSE);
+
+  switch (error_code) {
+    /**
+     * Some thread owns the mutex.
+     */
+    case EBUSY:
+      break;
+    /**
+     * Recursive lock count limit has been reached.
+     */
+    case EAGAIN:
+    /**
+     * The calling thread owns the mutex now.
+     */
+    case 0:
+    /**
+     * An unexpected error has occurred.
+     */
+    default:
+      return error_code;
+  }
+
   /**
    * The pthread_mutex_timedlock() function shall fail if:
    *
@@ -269,15 +898,7 @@
     return EINVAL;
   }
 
-  unsigned __int64 end = _pthread_time_in_ms_from_timespec (ts);
-  unsigned __int64 now = _pthread_time_in_ms ();
-  unsigned __int64 patience = end > now ? end - now : 0;
-
-  if (patience > 0xffffffff) {
-    patience = INFINITE;
-  }
-
-  return pthread_mutex_lock_intern(m, (DWORD) patience);
+  return wMutex->Base.Vtable->Lock (wMutex, ts);
 }
 
 int pthread_mutex_timedlock32(pthread_mutex_t *m, const struct _timespec32 *ts)
@@ -288,53 +909,28 @@
 
 int pthread_mutex_unlock(pthread_mutex_t *m)
 {
-  mutex_impl_t *mi = NULL;
+  WinpthreadsMutex *wMutex = NULL;
 
-  int error_code = mutex_impl (m, &mi);
+  int error_code = WinpthreadsMutexGet (m, &wMutex);
 
   if (error_code) {
     return error_code;
   }
 
-  if (unlikely(mi->type != Normal)) {
-    if (mi->state == Unlocked)
-      return EPERM;
-    if (mi->owner != GetCurrentThreadId())
-      return EPERM;
-    if (mi->rec_lock > 0) {
-      mi->rec_lock--;
-      return 0;
-    }
-    mi->owner = (DWORD)-1;
-  }
-  if (unlikely(InterlockedExchange((long *)&mi->state, Unlocked) == Waiting)) {
-    if (!SetEvent(mi->event))
-      return EINVAL;
-  }
-  return 0;
+  return wMutex->Base.Vtable->Unlock (wMutex);
 }
 
 int pthread_mutex_trylock(pthread_mutex_t *m)
 {
-  mutex_impl_t *mi = NULL;
+  WinpthreadsMutex *wMutex = NULL;
 
-  int error_code = mutex_impl (m, &mi);
+  int error_code = WinpthreadsMutexGet (m, &wMutex);
 
   if (error_code) {
     return error_code;
   }
 
-  if (InterlockedCompareExchange((long *)&mi->state, Locked, Unlocked) == Unlocked) {
-    if (mi->type != Normal)
-      mi->owner = GetCurrentThreadId();
-    return 0;
-  } else {
-    if (mi->type == Recursive && mi->owner == GetCurrentThreadId()) {
-      mi->rec_lock++;
-      return 0;
-    }
-    return EBUSY;
-  }
+  return wMutex->Base.Vtable->TryLock (wMutex, FALSE);
 }
 
 int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a)
@@ -364,51 +960,33 @@
     }
   }
 
-  mutex_impl_t *mi = calloc (1, sizeof (mutex_impl_t));
-
-  /**
-   * The pthread_mutex_init() function shall fail if:
-   *
-   * [ENOMEM]
-   *   Insufficient memory exists to initialize the mutex.
-   */
-  if (mi == NULL) {
-    return ENOMEM;
-  }
-
-  mi->event = CreateEventW (NULL, FALSE, FALSE, NULL);
-
-  /**
-   * The pthread_mutex_init() function shall fail if:
-   *
-   * [EAGAIN]
-   *   The system lacked the necessary resources (other than memory) to
-   *   initialize another mutex.
-   */
-  if (mi->event == NULL) {
-    free (mi);
-    return EAGAIN;
-  }
+  const WinpthreadsMutexVtable *wMutexVtable = NULL;
 
   switch (type) {
-    case PTHREAD_MUTEX_RECURSIVE:
-      mi->type = Recursive;
+    case PTHREAD_MUTEX_NORMAL:
+      wMutexVtable = &WinpthreadsNormalMutexVtable;
       break;
     case PTHREAD_MUTEX_ERRORCHECK:
-      mi->type = Errorcheck;
+      wMutexVtable = &WinpthreadsErrorCheckMutexVtable;
       break;
-    case PTHREAD_MUTEX_NORMAL:
-      mi->type = Normal;
+    case PTHREAD_MUTEX_RECURSIVE:
+      wMutexVtable = &WinpthreadsRecursiveMutexVtable;
       break;
     default:
       UNREACHABLE ();
   }
 
-  mi->owner    = (DWORD)-1;
-  mi->state    = Unlocked;
-  mi->rec_lock = 0;
+  WinpthreadsMutex *wMutex = NULL;
 
-  *m = (pthread_mutex_t)mi;
+  int error_code = wMutexVtable->Init (&wMutex, a);
+
+  if (error_code) {
+    return error_code;
+  }
+
+  wMutex->Base.Vtable = wMutexVtable;
+  MemoryBarrier ();
+  *m = (pthread_mutex_t)wMutex;
 
   return 0;
 }
@@ -426,9 +1004,9 @@
     return EINVAL;
   }
 
-  mutex_impl_t *mi = (mutex_impl_t *)*m;
+  WinpthreadsMutex *wMutex = (WinpthreadsMutex *)*m;
 
-  if (unlikely (mi == NULL)) {
+  if (unlikely (wMutex == NULL)) {
     return EINVAL;
   }
 
@@ -436,32 +1014,24 @@
    * If `m` points to a static initializer, attempt to immediately invalidate
    * it in order to reduce window for other functions to attempt using it.
    */
-  if (unlikely (STATIC_MUTEX_INITIALIZER (mi))) {
-    mutex_impl_t *mutex = InterlockedCompareExchangePointer ((void **)m, NULL, mi);
+  if (unlikely (STATIC_MUTEX_INITIALIZER (wMutex))) {
+    WinpthreadsMutex *mutex = InterlockedCompareExchangePointer ((void **)m, NULL, wMutex);
 
-    if (likely (mutex == mi) || unlikely (mutex == NULL)) {
+    if (likely (mutex == wMutex) || unlikely (mutex == NULL)) {
       return 0;
     }
 
-    mi = mutex;
+    wMutex = mutex;
   }
 
-  /**
-   * POSIX:
-   *
-   * If an implementation detects that the value specified by the mutex
-   * argument to pthread_mutex_destroy() refers to a locked mutex or a mutex
-   * that is referenced, it is recommended that the function should fail and
-   * report an [EBUSY] error.
-   */
-  if (InterlockedCompareExchange ((LONG *)&mi->state, Locked, Unlocked) != Unlocked) {
-    return EBUSY;
+  int error_code = wMutex->Base.Vtable->TryLock (wMutex, TRUE);
+
+  if (error_code) {
+    return error_code;
   }
 
   InterlockedExchangePointer ((void **)m, NULL);
-
-  CloseHandle (mi->event);
-  free (mi);
+  wMutex->Base.Vtable->Destroy (wMutex);
 
   return 0;
 }