ciabatta/code/os/win/win_mutex.c

139 lines
3.4 KiB
C

// NOTE: debug mutexes will follow the recursive logic but error if they
// actually recurse, this is slower than doing plain logic but it helps
// debug weird mutex errors.
//
// Based on these posts:
// https://preshing.com/20120305/implementing-a-recursive-mutex/
// https://preshing.com/20120226/roll-your-own-lightweight-mutex/
#include "win.h"
#include "threads.h"
void mtx_destroy(mtx_t *mtx) {
CloseHandle(mtx->semaphore);
}
int mtx_init(mtx_t *mtx, int type) {
*mtx = (mtx_t){
.type = type,
.semaphore = CreateSemaphore(NULL, 0, 1, NULL)
};
if (type == mtx_timed) {
// TODO(NeGate): implement timed mutexes
return thrd_error;
}
return thrd_success;
}
int mtx_lock(mtx_t *mtx) {
bool try_recursive = (mtx->type == mtx_recursive);
#ifdef _DEBUG
try_recursive = true;
#endif
if (try_recursive) {
DWORD tid = GetCurrentThreadId();
if (atomic_fetch_add_explicit(&mtx->counter, 1, memory_order_acquire) > 1) {
if (tid != mtx->owner) {
WaitForSingleObject(mtx->semaphore, INFINITE);
} else {
// we recursive and already locked
#ifdef _DEBUG
if (mtx->type != mtx_recursive) {
return thrd_error;
}
#endif
}
}
mtx->owner = tid;
mtx->recursion++;
} else {
if (atomic_fetch_add_explicit(&mtx->counter, 1, memory_order_acquire) > 1) {
WaitForSingleObject(mtx->semaphore, INFINITE);
}
}
return thrd_success;
}
int mtx_timedlock(mtx_t *restrict mtx, const struct timespec *restrict ts) {
return thrd_error;
}
int mtx_trylock(mtx_t *mtx) {
bool try_recursive = (mtx->type == mtx_recursive);
#ifdef _DEBUG
try_recursive = true;
#endif
if (try_recursive) {
DWORD tid = GetCurrentThreadId();
// Do we own this mutex on this thread already?
if (mtx->owner == tid) {
#ifdef _DEBUG
if (mtx->type != mtx_recursive) {
return thrd_error;
}
#endif
atomic_fetch_add(&mtx->counter, 1);
} else {
int expected = 1;
if (!atomic_compare_exchange_strong(&mtx->counter, &expected, 0)) {
return thrd_busy;
}
mtx->owner = tid;
}
mtx->recursion++;
return thrd_success;
} else {
int expected = 1;
if (!atomic_compare_exchange_strong(&mtx->counter, &expected, 0)) {
return thrd_busy;
}
return thrd_success;
}
}
int mtx_unlock(mtx_t *mtx) {
bool try_recursive = (mtx->type == mtx_recursive);
#if _DEBUG
try_recursive = true;
#endif
if (try_recursive) {
DWORD tid = GetCurrentThreadId();
if (tid != mtx->owner) return thrd_error;
unsigned long recur = --mtx->recursion;
if (recur == 0) {
mtx->owner = 0;
}
if (atomic_fetch_sub_explicit(&mtx->counter, 1, memory_order_release) > 0) {
if (recur == 0) ReleaseSemaphore(mtx->semaphore, 1, NULL);
else {
#ifdef _DEBUG
if (mtx->type != mtx_recursive) {
return thrd_error;
}
#endif
}
}
} else {
// release?
if (atomic_fetch_sub_explicit(&mtx->counter, 1, memory_order_release) > 0) {
ReleaseSemaphore(mtx->semaphore, 1, NULL);
}
}
return thrd_success;
}