#include "twn_util.h" #include "twn_workers_c.h" #include "rendering/twn_draw_c.h" #ifndef __EMSCRIPTEN__ SDL_sem *workers_job_semaphore; static SDL_mutex *workers_mutex; static SDL_sem *workers_exit_semaphore; /* should come to count of `workers_pool_size` */ static bool workers_should_exit; #else /* accomulate to late poll from main thread itself */ static uint32_t workers_job_count; #endif static size_t workers_pool_size; /* logic is such that when job is posted, worker threads attempt to grab it from any possible entry point */ /* if it did something, which is signaled by `true` return, go back to waiting on semaphore, so that it's decremented properly */ static int worker_thread(void *udata) { (void)udata; while (true) { #ifndef __EMSCRIPTEN__ /* check whether loop should end */ SDL_LockMutex(workers_mutex); if (workers_should_exit) { SDL_UnlockMutex(workers_mutex); break; } SDL_UnlockMutex(workers_mutex); /* wait and occasionally go back to check whether it all should end */ if (SDL_SemWaitTimeout(workers_job_semaphore, 100) == SDL_MUTEX_TIMEDOUT) continue; #else if (workers_job_count <= 0) break; workers_job_count--; #endif /* process models, which will trigger texture loads */ if (models_load_workers_thread()) continue; if (textures_load_workers_thread()) continue; } #ifndef __EMSCRIPTEN__ /* let the main thread collect it */ SDL_SemPost(workers_exit_semaphore); #endif return 0; } /* TODO: have a path for platforms without thread support? */ /* TODO: limit stack size? */ bool workers_init(size_t worker_count) { SDL_assert(workers_pool_size == 0); if (worker_count > MAX_WORKERS) worker_count = MAX_WORKERS; #ifndef __EMSCRIPTEN__ /* spawn a bunch of detached threads without references to them */ for (size_t i = 0; i < worker_count; ++i) { SDL_Thread *thread = SDL_CreateThread(worker_thread, "worker", NULL); SDL_assert_always(thread); SDL_DetachThread(thread); } workers_pool_size = worker_count; workers_job_semaphore = SDL_CreateSemaphore(0); workers_exit_semaphore = SDL_CreateSemaphore(0); workers_mutex = SDL_CreateMutex(); #endif return true; } void workers_deinit(void) { #ifndef __EMSCRIPTEN__ SDL_LockMutex(workers_mutex); workers_should_exit = true; SDL_UnlockMutex(workers_mutex); for (size_t i = 0; i < workers_pool_size; ++i) { SDL_SemWait(workers_exit_semaphore); } SDL_DestroyMutex(workers_mutex); SDL_DestroySemaphore(workers_job_semaphore); SDL_DestroySemaphore(workers_exit_semaphore); #endif workers_pool_size = 0; } void workers_add_job(void) { #ifndef __EMSCRIPTEN__ SDL_SemPost(workers_job_semaphore); #else workers_job_count++; #endif } void workers_poll(void) { #ifdef __EMSCRIPTEN__ worker_thread(NULL); #else #endif }