189 lines
5.2 KiB
C
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
|