#include "runtime.h" #include "threading.h" #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include 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 vy_thread_id _main_thread_id; /* Called by the runtime during setup */ extern void SetMainThreadId(void) { _main_thread_id = (vy_thread_id)GetCurrentThreadId(); } 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; } VY_DLLEXPORT vy_thread_id vyGetCurrentThreadId(void) { return (vy_thread_id)GetCurrentThreadId(); } VY_DLLEXPORT bool vyIsMainThread(void) { return vyGetCurrentThreadId() == _main_thread_id; } #elif defined(__linux__) #define _GNU_SOURCE #include #include 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); } VY_DLLEXPORT vy_thread_id vyGetCurrentThreadId(void) { return (vy_thread_id)GetCurrentThreadId(); } VY_DLLEXPORT bool vyIsMainThread(void) { return vyGetCurrentThreadId() == _main_thread_id; } #endif