#include "runtime.h" #include "threading.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include 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 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