blob: 93b854f98e4a56211475caec9509ef99373c245c [file]
/*
Copyright (c) 2011, 2014, 2026 mingw-w64 project
Copyright (c) 2015 Intel Corporation
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <limits.h>
#include <stdlib.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define WINPTHREAD_MUTEX_DECL WINPTHREAD_API
/* public header files */
#include "pthread.h"
/* internal header files */
#include "misc.h"
/**
* Reference:
*
* pthread_mutex_init(), pthread_mutex_destroy():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_destroy.html>
*
* pthread_mutex_lock(), pthread_mutex_trylock(), pthread_mutex_unlock():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_lock.html>
*
* pthread_mutex_timedlock(), pthread_mutex_clocklock():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_clocklock.html>
*
* pthread_mutex_getprioceiling(), pthread_mutex_setprioceiling():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_getprioceiling.html>
*
* pthread_mutex_consistent():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutex_consistent.html>
*
* pthread_mutexattr_init(), pthread_mutexattr_destroy():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_destroy.html>
*
* pthread_mutexattr_gettype(), pthread_mutexattr_settype():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_gettype.html>
*
* pthread_mutexattr_getpshared(), pthread_mutexattr_setpshared():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_getpshared.html>
*
* pthread_mutexattr_getprotocol(), pthread_mutexattr_setprotocol():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_getprotocol.html>
*
* pthread_mutexattr_getprioceiling(), pthread_mutexattr_setprioceiling():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_getprioceiling.html>
*
* pthread_mutexattr_getrobust(), pthread_mutexattr_setrobust():
* <https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/pthread_mutexattr_getrobust.html>
*
* The following functions are not implemented:
*
* - pthread_mutex_clocklock()
* - pthread_mutex_getprioceiling()
* - pthread_mutex_setprioceiling()
* - pthread_mutex_consistent()
* - pthread_mutexattr_getrobust()
* - pthread_mutexattr_setrobust()
*/
/**
* Forward declaration; see definition below.
*/
typedef union WinpthreadsMutex WinpthreadsMutex;
/**
* Forward declaration; see definition below.
*/
typedef union WinpthreadsMutexAttributes WinpthreadsMutexAttributes;
/**
* Mutex type-specific "init" routine.
*/
typedef int (* FuncMutexInit) (WinpthreadsMutex **, const pthread_mutexattr_t *);
/**
* 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 {
FuncMutexInit Init;
FuncMutexDestroy Destroy;
FuncMutexLock Lock;
FuncMutexTryLock TryLock;
FuncMutexUnlock Unlock;
} WinpthreadsMutexVtable;
/**
* Union stored in `pthread_mutexattr_t` objects.
*/
union WinpthreadsMutexAttributes {
pthread_mutexattr_t Value;
struct {
/**
* Can be one of the following values:
*
* - PTHREAD_MUTEX_NORMAL
* - PTHREAD_MUTEX_ERRORCHECK
* - PTHREAD_MUTEX_RECURSIVE
*/
int Type : 4;
/**
* Zero for `PTHREAD_PROCESS_PRIVATE` and non-zero for
* `PTHREAD_PROCESS_SHARED`.
*
* Currently, winpthreads only supports `PTHREAD_PROCESS_PRIVATE`.
*/
int Shared : 1;
int Reserved : 3;
/**
* Can be one of the following values:
*
* - PTHREAD_PRIO_NONE
* - PTHREAD_PRIO_INHERIT
* - PTHREAD_PRIO_PROTECT
*
* Currently, winpthreads does not implement POSIX realtime extensions.
* As such, the only supported value is `PTHREAD_PRIO_NONE`.
*/
int Protocol : 8;
/**
* Can be any valid `THREAD_PRIORITY_*` value.
*
* Only has effect when `Protocol` is `PTHREAD_PRIO_INHERIT` or
* `PTHREAD_PRIO_PROTECT`, which are not currently supported.
*/
int PriorityCeiling : 8;
#define WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC 0x67
/**
* Must be `WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC`.
*
* If needed, these bits may be used for future extensions.
*/
int Magic : 8;
} Attributes;
};
WINPTHREADS_STATIC_ASSERT (sizeof ((WinpthreadsMutexAttributes) {}.Attributes) == sizeof (pthread_mutexattr_t), "");
/**
* Expands into an expression of type `WinpthreadsMutexAttributes`
* corresponding to the default mutex attributes.
*/
#define WINPTHREADS_MUTEX_ATTRIBUTES_DEFAULT ((WinpthreadsMutexAttributes) { \
.Attributes.Type = PTHREAD_MUTEX_NORMAL, \
.Attributes.Shared = PTHREAD_PROCESS_PRIVATE, \
.Attributes.Reserved = 0, \
.Attributes.Protocol = PTHREAD_PRIO_NONE, \
.Attributes.PriorityCeiling = THREAD_PRIORITY_NORMAL, \
.Attributes.Magic = WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC \
})
#define THREAD_ID_NO_OWNER ((DWORD) -1)
/**
* Mutex lock states.
*/
typedef enum {
/**
* Mutex is unlocked.
*/
Unlocked,
/**
* Mutex is locked.
*
* This state inicates that there are no blocked threads waiting for
* the mutex to be released.
*
* While in this state, if any thread blocks waiting for the mutex to be
* released, the lock state will change to `LockedWithBlocking`.
*/
Locked,
/**
* Mutex is locked.
*
* This state indicates that there can be one or more blocked threads waiting
* for the mutex to be released.
*/
LockedWithBlocking,
} WinpthreadsMutexLockState;
/**
* 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`:
*
* PTHREAD_DEFAULT_MUTEX_INITIALIZER: -1
* PTHREAD_NORMAL_MUTEX_INITIALIZER: -1
* PTHREAD_ERRORCHECK_MUTEX_INITIALIZER: -2
* PTHREAD_RECURSIVE_MUTEX_INITIALIZER: -3
*/
#define STATIC_MUTEX_INITIALIZER(m) ((uintptr_t)(m) >= (uintptr_t)-3)
/**
* Obtain pointer to `WinpthreadsMutex` structure pointed to by `m`.
*
* If `m` points to statically initialized `pthread_mutex_t` object,
* allocate `WinpthreadsMutex` structure and store its address in `*m`.
*
* On success, stores pointer to `WinpthreadsMutex` structure in `*wMutex`.
*
* Returns zero on success and an error-code on failure.
*/
static WINPTHREADS_INLINE int WinpthreadsMutexGet(pthread_mutex_t *m, WinpthreadsMutex **wMutex)
{
*wMutex = (WinpthreadsMutex *)*m;
/**
* We need to avoid race condition when more than one thread attempts to use
* same statically initialized `pthread_mutex_t` object at the same time.
*
* Store newly initialized mutex in `wMutex`, which is a local variable
* supplied by the caller, and only then store it in `m`.
*
* If some other thread was faster then us, destroy newly created mutex
* and use mutex pointed to by `m`.
*/
if (unlikely (STATIC_MUTEX_INITIALIZER (*wMutex))) {
pthread_mutexattr_t mutexAttr;
int mutexType;
WinpthreadsMutex *volatile initializer = *wMutex;
switch ((pthread_mutex_t)initializer) {
case PTHREAD_NORMAL_MUTEX_INITIALIZER:
mutexType = PTHREAD_MUTEX_NORMAL;
break;
case PTHREAD_ERRORCHECK_MUTEX_INITIALIZER:
mutexType = PTHREAD_MUTEX_ERRORCHECK;
break;
case PTHREAD_RECURSIVE_MUTEX_INITIALIZER:
mutexType = PTHREAD_MUTEX_RECURSIVE;
break;
default:
UNREACHABLE ();
}
int error_code = pthread_mutexattr_init (&mutexAttr);
if (error_code) {
return error_code;
}
pthread_mutexattr_settype (&mutexAttr, mutexType);
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, *wMutex, initializer);
/**
* Some other thread was faster than us.
*/
if (unlikely (mutex != initializer)) {
pthread_mutex_destroy ((pthread_mutex_t *)wMutex);
*wMutex = mutex;
}
}
if (unlikely (*wMutex == NULL)) {
return EINVAL;
}
return 0;
}
int pthread_mutex_lock(pthread_mutex_t *m)
{
WinpthreadsMutex *wMutex = NULL;
int error_code = WinpthreadsMutexGet (m, &wMutex);
if (error_code) {
return error_code;
}
return wMutex->Base.Vtable->Lock (wMutex, NULL);
}
int pthread_mutex_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:
*
* [EINVAL]
* The process or thread would have blocked, and the abstime parameter
* specified a nanoseconds field value less than zero or greater than
* or equal to 1000 million.
*/
if (ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000) {
return EINVAL;
}
return wMutex->Base.Vtable->Lock (wMutex, ts);
}
int pthread_mutex_timedlock32(pthread_mutex_t *m, const struct _timespec32 *ts)
{
struct _timespec64 ts64 = {.tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec};
return pthread_mutex_timedlock64 (m, &ts64);
}
int pthread_mutex_unlock(pthread_mutex_t *m)
{
WinpthreadsMutex *wMutex = NULL;
int error_code = WinpthreadsMutexGet (m, &wMutex);
if (error_code) {
return error_code;
}
return wMutex->Base.Vtable->Unlock (wMutex);
}
int pthread_mutex_trylock(pthread_mutex_t *m)
{
WinpthreadsMutex *wMutex = NULL;
int error_code = WinpthreadsMutexGet (m, &wMutex);
if (error_code) {
return error_code;
}
return wMutex->Base.Vtable->TryLock (wMutex, FALSE);
}
int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a)
{
int pshared = PTHREAD_PROCESS_PRIVATE;
int type = PTHREAD_MUTEX_DEFAULT;
if (a != NULL) {
/**
* POSIX:
*
* If an implementation detects that the value specified by the attr
* argument to pthread_mutex_init() does not refer to an initialized mutex
* attributes object, it is recommended that the function should fail and
* report an [EINVAL] error.
*/
if (pthread_mutexattr_getpshared (a, &pshared) != 0) {
return EINVAL;
}
if (pthread_mutexattr_gettype (a, &type) != 0) {
return EINVAL;
}
if (pshared == PTHREAD_PROCESS_SHARED) {
return ENOSYS;
}
}
const WinpthreadsMutexVtable *wMutexVtable = NULL;
switch (type) {
case PTHREAD_MUTEX_NORMAL:
wMutexVtable = &WinpthreadsNormalMutexVtable;
break;
case PTHREAD_MUTEX_ERRORCHECK:
wMutexVtable = &WinpthreadsErrorCheckMutexVtable;
break;
case PTHREAD_MUTEX_RECURSIVE:
wMutexVtable = &WinpthreadsRecursiveMutexVtable;
break;
default:
UNREACHABLE ();
}
WinpthreadsMutex *wMutex = NULL;
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;
}
int pthread_mutex_destroy(pthread_mutex_t *m)
{
/**
* POSIX:
*
* If an implementation detects that the value specified by the mutex argument
* to pthread_mutex_destroy() does not refer to an initialized mutex, it is
* recommended that the function should fail and report an [EINVAL] error.
*/
if (unlikely (m == NULL)) {
return EINVAL;
}
WinpthreadsMutex *wMutex = (WinpthreadsMutex *)*m;
if (unlikely (wMutex == NULL)) {
return EINVAL;
}
/**
* If `m` points to a static initializer, attempt to immediately invalidate
* it in order to reduce window for other functions to attempt using it.
*/
if (unlikely (STATIC_MUTEX_INITIALIZER (wMutex))) {
WinpthreadsMutex *mutex = InterlockedCompareExchangePointer ((void **)m, NULL, wMutex);
if (likely (mutex == wMutex) || unlikely (mutex == NULL)) {
return 0;
}
wMutex = mutex;
}
int error_code = wMutex->Base.Vtable->TryLock (wMutex, TRUE);
if (error_code) {
return error_code;
}
InterlockedExchangePointer ((void **)m, NULL);
wMutex->Base.Vtable->Destroy (wMutex);
return 0;
}
int pthread_mutexattr_init(pthread_mutexattr_t *a)
{
*a = WINPTHREADS_MUTEX_ATTRIBUTES_DEFAULT.Value;
return 0;
}
int pthread_mutexattr_destroy(pthread_mutexattr_t *a)
{
if (a == NULL) {
return EINVAL;
}
memset (a, 0, sizeof (pthread_mutexattr_t));
return 0;
}
int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *value)
{
if (a == NULL || value == NULL) {
return EINVAL;
}
const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a;
if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
return EINVAL;
}
switch (wMutexAttr->Attributes.Type) {
case PTHREAD_MUTEX_NORMAL:
case PTHREAD_MUTEX_ERRORCHECK:
case PTHREAD_MUTEX_RECURSIVE:
*value = wMutexAttr->Attributes.Type;
return 0;
}
return EINVAL;
}
int pthread_mutexattr_settype(pthread_mutexattr_t *a, int value)
{
if (a == NULL) {
return EINVAL;
}
WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a;
if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
return EINVAL;
}
switch (value) {
case PTHREAD_MUTEX_NORMAL:
case PTHREAD_MUTEX_ERRORCHECK:
case PTHREAD_MUTEX_RECURSIVE:
wMutexAttr->Attributes.Type = value;
return 0;
}
return EINVAL;
}
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *value)
{
if (a == NULL || value == NULL) {
return EINVAL;
}
const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a;
if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
return EINVAL;
}
*value = !!wMutexAttr->Attributes.Shared;
return 0;
}
int pthread_mutexattr_setpshared(pthread_mutexattr_t *a, int value)
{
if (a == NULL) {
return EINVAL;
}
WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a;
if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
return EINVAL;
}
switch (value) {
case PTHREAD_PROCESS_PRIVATE:
case PTHREAD_PROCESS_SHARED:
wMutexAttr->Attributes.Shared = value;
return 0;
}
return EINVAL;
}
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *value)
{
if (a == NULL || value == NULL) {
return EINVAL;
}
const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a;
if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
return EINVAL;
}
switch (wMutexAttr->Attributes.Protocol) {
case PTHREAD_PRIO_NONE:
case PTHREAD_PRIO_INHERIT:
case PTHREAD_PRIO_PROTECT:
*value = wMutexAttr->Attributes.Protocol;
return 0;
}
return EINVAL;
}
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int value)
{
if (a == NULL) {
return EINVAL;
}
WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a;
if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
return EINVAL;
}
switch (value) {
case PTHREAD_PRIO_NONE:
wMutexAttr->Attributes.Protocol = value;
return 0;
/**
* POSIX realtime extensions are not implemented.
*
* The pthread_mutexattr_setprotocol() function shall fail if:
*
* [ENOTSUP]
* The value specified by protocol is an unsupported value.
*/
case PTHREAD_PRIO_INHERIT:
case PTHREAD_PRIO_PROTECT:
return ENOTSUP;
}
return EINVAL;
}
int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int *value)
{
if (a == NULL || value == NULL) {
return EINVAL;
}
const WinpthreadsMutexAttributes *wMutexAttr = (const WinpthreadsMutexAttributes *) a;
if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
return EINVAL;
}
int priorityCeiling = wMutexAttr->Attributes.PriorityCeiling;
if (priorityCeiling >= THREAD_PRIORITY_IDLE && priorityCeiling <= THREAD_PRIORITY_TIME_CRITICAL) {
*value = priorityCeiling;
return 0;
}
return EINVAL;
}
int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int value)
{
if (a == NULL) {
return EINVAL;
}
WinpthreadsMutexAttributes *wMutexAttr = (WinpthreadsMutexAttributes *) a;
if (wMutexAttr->Attributes.Magic != WINPTHREADS_MUTEX_ATTRIBUTES_MAGIC) {
return EINVAL;
}
if (value >= THREAD_PRIORITY_IDLE && value <= THREAD_PRIORITY_TIME_CRITICAL) {
wMutexAttr->Attributes.PriorityCeiling = value;
return 0;
}
return EINVAL;
}