rtengine/src/runtime/threading_mutex.c
2023-12-12 10:58:09 +01:00

152 lines
4.3 KiB
C

#include "runtime.h"
#include "threading.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
struct vy_mutex_s {
HANDLE handle;
ptrdiff_t next_reusable;
DWORD owner;
};
#define MAX_MUTEX 1024
static vy_mutex _mutex[MAX_MUTEX];
static ptrdiff_t _first_reusable = MAX_MUTEX;
static ptrdiff_t _next = 0;
static HANDLE _guard;
static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT;
static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, PVOID parameter, PVOID *context) {
VY_UNUSED(initOnce);
VY_UNUSED(parameter);
VY_UNUSED(context);
_guard = CreateMutexW(NULL, FALSE, NULL);
return _guard != NULL;
}
VY_DLLEXPORT vy_mutex *vyCreateMutex(void) {
if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) {
vyReportError("core", "Failed to initialize the guard mutex.");
return NULL;
}
if (WaitForSingleObjectEx(_guard, INFINITE, TRUE) != WAIT_OBJECT_0) {
vyLog("core", "Failed to lock the guard variable: %u", GetLastError());
return NULL;
}
if (_first_reusable < MAX_MUTEX) {
vy_mutex *mtx = &_mutex[_first_reusable];
_first_reusable = mtx->next_reusable;
mtx->owner = 0;
return mtx;
} else if (_next < MAX_MUTEX) {
vy_mutex *mtx = &_mutex[_next];
mtx->handle = CreateMutexW(NULL, FALSE, NULL);
if (!mtx->handle) {
vyLog("core", "Mutex creation failed: %u", GetLastError());
return NULL;
}
mtx->next_reusable = MAX_MUTEX;
mtx->owner = 0;
++_next;
return mtx;
}
vyReportError("core", "Ran out of mutex objects");
return NULL;
}
VY_DLLEXPORT void vyDestroyMutex(vy_mutex *mutex) {
ptrdiff_t index = mutex - &_mutex[0];
mutex->next_reusable = _first_reusable;
_first_reusable = index;
}
VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) {
DWORD caller = GetCurrentThreadId();
DWORD result;
do {
result = WaitForSingleObjectEx(mutex->handle, INFINITE, TRUE);
if (result == WAIT_OBJECT_0) {
mutex->owner = caller;
} else if (result != WAIT_IO_COMPLETION) {
vyLog("core",
"WaitForSingleObjectEx returned %x (GetLastError(): %u)",
result,
(result == WAIT_FAILED) ? GetLastError() : 0);
}
} while (result == WAIT_IO_COMPLETION);
return result == WAIT_OBJECT_0;
}
VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) {
DWORD caller = GetCurrentThreadId();
if (caller != mutex->owner) {
vyReportError("core", "Tried to unlock a mutex held by another thread.");
return false;
}
mutex->owner = 0;
bool result = ReleaseMutex(mutex->handle) != 0;
if (!result)
mutex->owner = caller;
return result;
}
#elif defined(__linux__)
#include <pthread.h>
struct vy_mutex_s {
pthread_mutex_t handle;
ptrdiff_t next_reusable;
};
#define MAX_MUTEX 1024
static vy_mutex _mutex[MAX_MUTEX];
static ptrdiff_t _first_reusable = MAX_MUTEX;
static ptrdiff_t _next = 0;
static pthread_mutex_t _guard = PTHREAD_MUTEX_INITIALIZER;
VY_DLLEXPORT vy_mutex *vyCreateMutex(void) {
pthread_mutex_lock(&_guard);
if (_first_reusable < MAX_MUTEX) {
vy_mutex *mtx = &_mutex[_first_reusable];
_first_reusable = mtx->next_reusable;
pthread_mutex_unlock(&_guard);
return mtx;
} else if (_next < MAX_MUTEX) {
vy_mutex *mtx = &_mutex[_next];
if (pthread_mutex_init(&mtx->handle, NULL) != 0) {
vyLog("core", "Mutex creation failed");
pthread_mutex_unlock(&_guard);
return NULL;
}
mtx->next_reusable = MAX_MUTEX;
++_next;
pthread_mutex_unlock(&_guard);
return mtx;
}
vyReportError("core", "Ran out of mutex objects");
pthread_mutex_unlock(&_guard);
return NULL;
}
VY_DLLEXPORT void vyDestroyMutex(vy_mutex *mutex) {
ptrdiff_t index = mutex - &_mutex[0];
pthread_mutex_lock(&_guard);
mutex->next_reusable = _first_reusable;
_first_reusable = index;
pthread_mutex_unlock(&_guard);
}
VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) {
return pthread_mutex_lock(&mutex->handle) == 0;
}
VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) {
return pthread_mutex_unlock(&mutex->handle) == 0;
}
#endif