152 lines
4.3 KiB
C
152 lines
4.3 KiB
C
#include "runtime.h"
|
|
#include "threading.h"
|
|
|
|
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <Windows.h>
|
|
|
|
struct rt_mutex_s {
|
|
HANDLE handle;
|
|
ptrdiff_t next_reusable;
|
|
DWORD owner;
|
|
};
|
|
|
|
#define MAX_MUTEX 1024
|
|
static rt_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) {
|
|
RT_UNUSED(initOnce);
|
|
RT_UNUSED(parameter);
|
|
RT_UNUSED(context);
|
|
|
|
_guard = CreateMutexW(NULL, FALSE, NULL);
|
|
return _guard != NULL;
|
|
}
|
|
|
|
RT_DLLEXPORT rt_mutex *rtCreateMutex(void) {
|
|
if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) {
|
|
rtReportError("core", "Failed to initialize the guard mutex.");
|
|
return NULL;
|
|
}
|
|
|
|
if (WaitForSingleObjectEx(_guard, INFINITE, TRUE) != WAIT_OBJECT_0) {
|
|
rtLog("core", "Failed to lock the guard variable: %u", GetLastError());
|
|
return NULL;
|
|
}
|
|
if (_first_reusable < MAX_MUTEX) {
|
|
rt_mutex *mtx = &_mutex[_first_reusable];
|
|
_first_reusable = mtx->next_reusable;
|
|
mtx->owner = 0;
|
|
return mtx;
|
|
} else if (_next < MAX_MUTEX) {
|
|
rt_mutex *mtx = &_mutex[_next];
|
|
mtx->handle = CreateMutexW(NULL, FALSE, NULL);
|
|
if (!mtx->handle) {
|
|
rtLog("core", "Mutex creation failed: %u", GetLastError());
|
|
return NULL;
|
|
}
|
|
mtx->next_reusable = MAX_MUTEX;
|
|
mtx->owner = 0;
|
|
++_next;
|
|
return mtx;
|
|
}
|
|
rtReportError("core", "Ran out of mutex objects");
|
|
return NULL;
|
|
}
|
|
|
|
RT_DLLEXPORT void rtDestroyMutex(rt_mutex *mutex) {
|
|
ptrdiff_t index = mutex - &_mutex[0];
|
|
mutex->next_reusable = _first_reusable;
|
|
_first_reusable = index;
|
|
}
|
|
|
|
RT_DLLEXPORT bool rtLockMutex(rt_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) {
|
|
rtLog("core",
|
|
"WaitForSingleObjectEx returned %x (GetLastError(): %u)",
|
|
result,
|
|
(result == WAIT_FAILED) ? GetLastError() : 0);
|
|
}
|
|
} while (result == WAIT_IO_COMPLETION);
|
|
return result == WAIT_OBJECT_0;
|
|
}
|
|
|
|
RT_DLLEXPORT bool rtUnlockMutex(rt_mutex *mutex) {
|
|
DWORD caller = GetCurrentThreadId();
|
|
if (caller != mutex->owner) {
|
|
rtReportError("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 rt_mutex_s {
|
|
pthread_mutex_t handle;
|
|
ptrdiff_t next_reusable;
|
|
};
|
|
|
|
#define MAX_MUTEX 1024
|
|
static rt_mutex _mutex[MAX_MUTEX];
|
|
static ptrdiff_t _first_reusable = MAX_MUTEX;
|
|
static ptrdiff_t _next = 0;
|
|
static pthread_mutex_t _guard = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
RT_DLLEXPORT rt_mutex *rtCreateMutex(void) {
|
|
pthread_mutex_lock(&_guard);
|
|
if (_first_reusable < MAX_MUTEX) {
|
|
rt_mutex *mtx = &_mutex[_first_reusable];
|
|
_first_reusable = mtx->next_reusable;
|
|
pthread_mutex_unlock(&_guard);
|
|
return mtx;
|
|
} else if (_next < MAX_MUTEX) {
|
|
rt_mutex *mtx = &_mutex[_next];
|
|
if (pthread_mutex_init(&mtx->handle, NULL) != 0) {
|
|
rtLog("core", "Mutex creation failed");
|
|
pthread_mutex_unlock(&_guard);
|
|
return NULL;
|
|
}
|
|
mtx->next_reusable = MAX_MUTEX;
|
|
++_next;
|
|
pthread_mutex_unlock(&_guard);
|
|
return mtx;
|
|
}
|
|
rtReportError("core", "Ran out of mutex objects");
|
|
pthread_mutex_unlock(&_guard);
|
|
return NULL;
|
|
}
|
|
|
|
RT_DLLEXPORT void rtDestroyMutex(rt_mutex *mutex) {
|
|
ptrdiff_t index = mutex - &_mutex[0];
|
|
pthread_mutex_lock(&_guard);
|
|
mutex->next_reusable = _first_reusable;
|
|
_first_reusable = index;
|
|
pthread_mutex_unlock(&_guard);
|
|
}
|
|
|
|
RT_DLLEXPORT bool rtLockMutex(rt_mutex *mutex) {
|
|
return pthread_mutex_lock(&mutex->handle) == 0;
|
|
}
|
|
|
|
RT_DLLEXPORT bool rtUnlockMutex(rt_mutex *mutex) {
|
|
return pthread_mutex_unlock(&mutex->handle) == 0;
|
|
}
|
|
|
|
#endif
|