rtengine/src/runtime/threading_thread.c
2023-12-10 23:09:19 +01:00

189 lines
5.2 KiB
C

#include "runtime.h"
#include "threading.h"
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
struct vy_thread_s {
HANDLE handle;
ptrdiff_t next_reusable;
vy_thread_entry_fn *entry;
void *param;
bool needs_join;
};
#define MAX_THREADS 256
static vy_thread _threads[MAX_THREADS];
static ptrdiff_t _first_reusable = MAX_THREADS;
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;
}
static DWORD WINAPI win32ThreadWrapper(LPVOID arg) {
vy_thread *user_thread = arg;
user_thread->needs_join = false;
user_thread->entry(user_thread->param);
user_thread->needs_join = true;
return 0;
}
VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param, const char *name) {
if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) {
vyReportError("core", "Failed to initialize the guard mutex.");
return NULL;
}
vy_thread *thrd = NULL;
if (WaitForSingleObject(_guard, INFINITE) != WAIT_OBJECT_0) {
vyLog("core", "Failed to lock the guard variable: %u", GetLastError());
return NULL;
}
if (_first_reusable < MAX_THREADS) {
thrd = &_threads[_first_reusable];
_first_reusable = thrd->next_reusable;
if (thrd->needs_join) {
WaitForSingleObject(thrd->handle, INFINITE);
CloseHandle(thrd->handle);
thrd->needs_join = false;
}
} else if (_next < MAX_THREADS) {
thrd = &_threads[_next];
thrd->next_reusable = MAX_THREADS;
++_next;
}
if (thrd) {
thrd->entry = entry;
thrd->param = param;
thrd->handle = CreateThread(NULL, 0, win32ThreadWrapper, (LPVOID)thrd, 0, NULL);
if (thrd->handle == NULL) {
vyLog("core", "Thread creation failed");
thrd = NULL;
}
WCHAR wname[64];
if (thrd && name && vyUTF8ToWStr(name, wname, sizeof(wname)) == VY_SUCCESS)
SetThreadDescription(thrd->handle, wname);
} else {
vyReportError("core", "Ran out of thread objects");
}
ReleaseMutex(_guard);
return thrd;
}
VY_DLLEXPORT void vyJoinThread(vy_thread *thread) {
WaitForSingleObject(thread->handle, INFINITE);
CloseHandle(thread->handle);
thread->needs_join = false;
ptrdiff_t index = thread - &_threads[0];
if (WaitForSingleObject(_guard, INFINITE) != WAIT_OBJECT_0) {
vyLog("core", "Failed to lock the guard variable: %u", GetLastError());
return;
}
thread->next_reusable = _first_reusable;
_first_reusable = index;
ReleaseMutex(_guard);
}
VY_DLLEXPORT unsigned int vyGetCPUCoreCount(void) {
SYSTEM_INFO info;
GetSystemInfo(&info);
return (unsigned int)info.dwNumberOfProcessors;
}
#elif defined(__linux__)
#define _GNU_SOURCE
#include <pthread.h>
#include <unistd.h>
struct vy_thread_s {
pthread_t handle;
ptrdiff_t next_reusable;
vy_thread_entry_fn *entry;
void *param;
bool needs_join;
};
#define MAX_THREADS 256
static vy_thread _threads[MAX_THREADS];
static ptrdiff_t _first_reusable = MAX_THREADS;
static ptrdiff_t _next = 0;
static pthread_mutex_t _guard = PTHREAD_MUTEX_INITIALIZER;
static void *linuxThreadWrapper(void *arg) {
vy_thread *user_thread = arg;
user_thread->needs_join = false;
user_thread->entry(user_thread->param);
user_thread->needs_join = true;
return NULL;
}
VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param, const char *name) {
vy_thread *thrd = NULL;
pthread_mutex_lock(&_guard);
if (_first_reusable < MAX_THREADS) {
thrd = &_threads[_first_reusable];
_first_reusable = thrd->next_reusable;
if (thrd->needs_join) {
pthread_join(thrd->handle, NULL);
thrd->needs_join = false;
}
} else if (_next < MAX_THREADS) {
thrd = &_threads[_next];
thrd->next_reusable = MAX_THREADS;
++_next;
}
if (thrd) {
thrd->entry = entry;
thrd->param = param;
if (pthread_create(&thrd->handle, NULL, linuxThreadWrapper, thrd) != 0) {
vyLog("core", "Thread creation failed");
thrd = NULL;
}
if (thrd && name)
pthread_setname_np(thrd->handle, name);
} else {
vyReportError("core", "Ran out of thread objects");
}
pthread_mutex_unlock(&_guard);
return thrd;
}
VY_DLLEXPORT void vyJoinThread(vy_thread *thread) {
pthread_join(thread->handle, NULL);
thread->needs_join = false;
ptrdiff_t index = thread - &_threads[0];
pthread_mutex_lock(&_guard);
thread->next_reusable = _first_reusable;
_first_reusable = index;
pthread_mutex_unlock(&_guard);
}
VY_DLLEXPORT unsigned int vyGetCPUCoreCount(void) {
int n = (int)sysconf(_SC_NPROCESSORS_ONLN);
return (n > 0) ? (unsigned int)n : (unsigned int)sysconf(_SC_NPROCESSORS_CONF);
}
#endif