ciabatta/src/os_win/thread.c

214 lines
5.3 KiB
C
Raw Normal View History

2022-07-24 15:57:12 +00:00
2022-06-14 22:46:14 +00:00
// 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/
DWORD _tls_index = 0;
2023-02-15 09:54:58 +00:00
typedef struct UserClosure {
thrd_start_t func;
void* arg;
2023-02-15 09:54:58 +00:00
} UserClosure;
2023-02-15 09:54:58 +00:00
static DWORD _thread_call_user(void* arg) {
UserClosure info = *((UserClosure*) arg);
int result = info.func(info.arg);
// TODO(NeGate): setup TSS dtors here
return (DWORD) result;
}
thrd_t thrd_current(void) {
return (thrd_t){ GetCurrentThread() };
}
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
2023-02-15 09:54:58 +00:00
UserClosure* info = malloc(sizeof(UserClosure));
if (info == NULL) {
return thrd_nomem;
}
info->func = func;
info->arg = arg;
2023-02-15 09:54:58 +00:00
// technically thrd_start_t and LPTHREAD_START_ROUTINE aren't the same
// but are close enough to be ABI compatible, namely a difference in
// signedness of the return val.
thr->handle = CreateThread(NULL, 0, _thread_call_user, info, 0, NULL);
return thr->handle != NULL ? thrd_success : thrd_error;
}
int thrd_detach(thrd_t thr) {
return CloseHandle(thr.handle) != 0 ? thrd_success : thrd_error;
}
int thrd_equal(thrd_t thr0, thrd_t thr1) {
return GetThreadId(thr0.handle) == GetThreadId(thr1.handle);
}
int thrd_join(thrd_t thr, int *res) {
2022-06-29 07:27:50 +00:00
DWORD wait = WaitForSingleObject(thr.handle, INFINITE);
if (wait == WAIT_FAILED) {
return thrd_error;
2022-06-29 07:27:50 +00:00
} else if (wait == WAIT_TIMEOUT) {
return thrd_timedout;
}
if (res != NULL) {
// snatch that exit code
DWORD ures;
if (GetExitCodeThread(thr.handle, &ures) == 0) {
CloseHandle(thr.handle);
return thrd_error;
}
*res = (int) ures;
}
CloseHandle(thr.handle);
return thrd_success;
}
void thrd_yield(void) {
Sleep(0);
}
_Noreturn void thrd_exit(int res) {
// TODO(NeGate): setup TSS dtors here
2022-08-05 04:21:07 +00:00
ExitThread((DWORD)res);
__builtin_unreachable();
}
2022-06-14 22:46:14 +00:00
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
2022-06-14 22:46:14 +00:00
try_recursive = true;
#endif
2022-06-14 22:46:14 +00:00
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
2022-06-14 22:46:14 +00:00
if (mtx->type != mtx_recursive) {
return thrd_error;
}
#endif
2022-06-14 22:46:14 +00:00
}
}
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
2022-06-14 22:46:14 +00:00
try_recursive = true;
#endif
2022-06-14 22:46:14 +00:00
if (try_recursive) {
DWORD tid = GetCurrentThreadId();
// Do we own this mutex on this thread already?
if (mtx->owner == tid) {
#ifdef _DEBUG
2022-06-14 22:46:14 +00:00
if (mtx->type != mtx_recursive) {
return thrd_error;
}
#endif
2022-06-14 22:46:14 +00:00
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
2022-06-14 22:46:14 +00:00
try_recursive = true;
#endif
2022-06-14 22:46:14 +00:00
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
2022-06-14 22:46:14 +00:00
if (mtx->type != mtx_recursive) {
return thrd_error;
}
#endif
2022-06-14 22:46:14 +00:00
}
}
} else {
// release?
if (atomic_fetch_sub_explicit(&mtx->counter, 1, memory_order_release) > 0) {
ReleaseSemaphore(mtx->semaphore, 1, NULL);
}
}
return thrd_success;
}