From 2b67eb4012c16e0bfe509bcec09dbdd7c704fd2a Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 11 May 2024 15:40:28 +0200 Subject: [PATCH] threads: style fixes, cross-platform crap --- Makefile | 6 ++-- src/pc/pc_main.c | 72 ++++++++++++++++++++++++++++------------------- src/pc/platform.c | 24 ++++++++++++---- src/pc/thread.c | 34 ++++++++++++++++------ src/pc/thread.h | 35 ++++++++++++++++------- 5 files changed, 116 insertions(+), 55 deletions(-) diff --git a/Makefile b/Makefile index 287ad961..54902d90 100644 --- a/Makefile +++ b/Makefile @@ -573,16 +573,16 @@ endif ifeq ($(WINDOWS_BUILD),1) CC_CHECK := $(CC) -fsyntax-only -fsigned-char $(BACKEND_CFLAGS) $(INCLUDE_CFLAGS) -Wall -Wextra -Wno-format-security $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) - CFLAGS := $(OPT_FLAGS) $(INCLUDE_CFLAGS) $(BACKEND_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv + CFLAGS := $(OPT_FLAGS) $(INCLUDE_CFLAGS) $(BACKEND_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv -pthread else ifeq ($(TARGET_WEB),1) CC_CHECK := $(CC) -fsyntax-only -fsigned-char $(BACKEND_CFLAGS) $(INCLUDE_CFLAGS) -Wall -Wextra -Wno-format-security $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -s USE_SDL=2 - CFLAGS := $(OPT_FLAGS) $(INCLUDE_CFLAGS) $(BACKEND_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv -s USE_SDL=2 + CFLAGS := $(OPT_FLAGS) $(INCLUDE_CFLAGS) $(BACKEND_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv -pthread -s USE_SDL=2 # Linux / Other builds below else CC_CHECK := $(CC) -fsyntax-only -fsigned-char $(BACKEND_CFLAGS) $(PLATFORM_CFLAGS) $(INCLUDE_CFLAGS) -Wall -Wextra -Wno-format-security $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) - CFLAGS := $(OPT_FLAGS) $(PLATFORM_CFLAGS) $(INCLUDE_CFLAGS) $(BACKEND_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv + CFLAGS := $(OPT_FLAGS) $(PLATFORM_CFLAGS) $(INCLUDE_CFLAGS) $(BACKEND_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv -pthread endif # Check for enhancement options diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index cbef0f75..98ddf78b 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -2,7 +2,8 @@ #include #include //Header file for sleep(). man 3 sleep for details. #include -#include +#include +#include #ifdef TARGET_WEB #include @@ -38,6 +39,7 @@ #include "game/game_init.h" #include "game/main.h" #include "game/thread6.h" +#include "game/display.h" #ifdef DISCORDRPC #include "pc/discord/discordrpc.h" @@ -60,17 +62,17 @@ static struct AudioAPI *audio_api; static struct GfxWindowManagerAPI *wm_api; static struct GfxRenderingAPI *rendering_api; -pthread_t pcthread_audio_id; -pthread_mutex_t pcthread_audio_mutex = PTHREAD_MUTEX_INITIALIZER; +sys_thread_t pcthread_audio_id; +sys_mutex_t pcthread_audio_mutex = SYS_MUTEX_INITIALIZER; bool pcthread_audio_init = false; bool pcthread_audio_rendering = false; -sem_t pcthread_audio_sema; +sys_sem_t pcthread_audio_sema; -pthread_mutex_t pcthread_game_mutex = PTHREAD_MUTEX_INITIALIZER; +sys_mutex_t pcthread_game_mutex = SYS_MUTEX_INITIALIZER; bool pcthread_game_loop_iterating = false; bool pcthread_game_reset_sound = false; bool pcthread_wait_for_gameloop = false; -sem_t pcthread_game_sema; +sys_sem_t pcthread_game_sema; extern void gfx_run(Gfx *commands); extern void thread5_game_loop(void *arg); @@ -87,29 +89,34 @@ void set_vblank_handler(s32 index, struct VblankHandler *handler, OSMesgQueue *q // Send a request for non-game threads to wait for the game thread to finish void pc_request_gameloop_wait(void) { - pcthread_mutex_lock(&pcthread_game_mutex); pcthread_wait_for_gameloop = true; pcthread_mutex_unlock(&pcthread_game_mutex); + sys_mutex_lock(&pcthread_game_mutex); + pcthread_wait_for_gameloop = true; + sys_mutex_unlock(&pcthread_game_mutex); } // Wait for the audio thread to finish rendering audio void pc_wait_for_audio(void) { - pcthread_semaphore_wait(&pcthread_audio_sema); + sys_semaphore_wait(&pcthread_audio_sema); } // Check if the audio thread is currently rendering audio bool pc_check_audio_rendering(void) { - pcthread_mutex_lock(&pcthread_audio_mutex); bool rendering = pcthread_audio_rendering; pcthread_mutex_unlock(&pcthread_audio_mutex); + sys_mutex_lock(&pcthread_audio_mutex); + bool rendering = pcthread_audio_rendering; + sys_mutex_unlock(&pcthread_audio_mutex); return rendering; } // Check if the game thread should finish before continuing bool pc_check_gameloop_wait(void) { - pcthread_mutex_lock(&pcthread_game_mutex); bool waiting = pcthread_wait_for_gameloop; pcthread_mutex_unlock(&pcthread_game_mutex); + sys_mutex_lock(&pcthread_game_mutex); + bool waiting = pcthread_wait_for_gameloop; + sys_mutex_unlock(&pcthread_game_mutex); return waiting; } static bool inited = false; -#include "game/display.h" // for gGlobalTimer void send_display_list(struct SPTask *spTask) { if (!inited) return; gfx_run((Gfx *)spTask->task.t.data_ptr); @@ -129,12 +136,12 @@ void produce_one_frame(void) { game_loop_one_iteration(); // Post the game thread semaphore if the game thread requested it - pcthread_mutex_lock(&pcthread_game_mutex); + sys_mutex_lock(&pcthread_game_mutex); if (pcthread_wait_for_gameloop) { - pcthread_semaphore_post(&pcthread_game_sema); + sys_semaphore_post(&pcthread_game_sema); pcthread_wait_for_gameloop = false; } - pcthread_mutex_unlock(&pcthread_game_mutex); + sys_mutex_unlock(&pcthread_game_mutex); thread6_rumble_loop(NULL); @@ -142,14 +149,13 @@ void produce_one_frame(void) { } // Seperate the audio thread from the main thread so that your ears won't bleed at a low framerate -// RACE CONDITION: void* audio_thread() { // Keep track of the time in microseconds const f64 frametime_micro = 16666.666; // 16.666666 ms = 60Hz; run this thread 60 times a second like the original game f64 start_time; f64 end_time; bool doloop = true; - pcthread_semaphore_wait(&pcthread_game_sema); + sys_semaphore_wait(&pcthread_game_sema); while(doloop) { start_time = sys_profile_time(); const f32 master_mod = (f32)configMasterVolume / 127.0f; @@ -167,10 +173,14 @@ void* audio_thread() { u32 num_audio_samples = audio_cnt < 2 ? 528 : 544;*/ if (!pc_check_gameloop_wait()) { - pcthread_mutex_lock(&pcthread_audio_mutex); pcthread_audio_rendering = true; pcthread_mutex_unlock(&pcthread_audio_mutex); + sys_mutex_lock(&pcthread_audio_mutex); + pcthread_audio_rendering = true; + sys_mutex_unlock(&pcthread_audio_mutex); create_next_audio_buffer(audio_buffer, num_audio_samples); - pcthread_semaphore_post(&pcthread_audio_sema); - pcthread_mutex_lock(&pcthread_audio_mutex); pcthread_audio_rendering = false; pcthread_mutex_unlock(&pcthread_audio_mutex); + sys_semaphore_post(&pcthread_audio_sema); + sys_mutex_lock(&pcthread_audio_mutex); + pcthread_audio_rendering = false; + sys_mutex_unlock(&pcthread_audio_mutex); } /* else { printf("Audio thread: dropped frame\n"); } */ @@ -186,23 +196,27 @@ void* audio_thread() { if (nap_time > 0.0) sys_sleep(nap_time); // Check if the game thread is still running - pcthread_mutex_lock(&pcthread_audio_mutex); doloop = pcthread_audio_init; pcthread_mutex_unlock(&pcthread_audio_mutex); + sys_mutex_lock(&pcthread_audio_mutex); + doloop = pcthread_audio_init; + sys_mutex_unlock(&pcthread_audio_mutex); } return NULL; } void audio_thread_init() { pcthread_audio_init = true; - pcthread_semaphore_init(&pcthread_audio_sema, 0, 1); - pthread_create(&pcthread_audio_id, NULL, audio_thread, NULL); -} + sys_semaphore_init(&pcthread_audio_sema, 0, 1); + sys_thread_create(&pcthread_audio_id, audio_thread, NULL); +} void audio_shutdown(void) { // Tell the audio thread to stop - pcthread_mutex_lock(&pcthread_audio_mutex); pcthread_audio_init = false; pcthread_mutex_unlock(&pcthread_audio_mutex); - pcthread_semaphore_wait(&pcthread_audio_sema); // Wait for the audio thread to finish rendering audio, then destroy it all - pthread_join(pcthread_audio_id, NULL); - pcthread_semaphore_destroy(&pcthread_audio_sema); + sys_mutex_lock(&pcthread_audio_mutex); + pcthread_audio_init = false; + sys_mutex_unlock(&pcthread_audio_mutex); + sys_semaphore_wait(&pcthread_audio_sema); // Wait for the audio thread to finish rendering audio, then destroy it all + sys_thread_join(&pcthread_audio_id, NULL); + sys_semaphore_destroy(&pcthread_audio_sema); if (audio_api) { if (audio_api->shutdown) audio_api->shutdown(); @@ -218,7 +232,7 @@ void game_deinit(void) { controller_shutdown(); audio_shutdown(); gfx_shutdown(); - pcthread_semaphore_destroy(&pcthread_game_sema); + sys_semaphore_destroy(&pcthread_game_sema); inited = false; } @@ -350,7 +364,7 @@ void main_func(void) { request_anim_frame(on_anim_frame); #else // initialize multithreading - pcthread_semaphore_init(&pcthread_game_sema, 0, 0); + sys_semaphore_init(&pcthread_game_sema, 0, 0); audio_thread_init(); while (true) { diff --git a/src/pc/platform.c b/src/pc/platform.c index 4198e834..79b6089a 100644 --- a/src/pc/platform.c +++ b/src/pc/platform.c @@ -11,6 +11,13 @@ #include "configfile.h" #include "platform.h" +#if !defined(HAVE_SDL2) && !defined(CLOCK_MONOTONIC) +#include +#elif defined(HAVE_SDL2) +// we can just ask SDL for most of this shit if we have it +#include +#endif + /* NULL terminated list of platform specific read-only data paths */ /* priority is top first */ const char *sys_ropaths[] = { @@ -75,10 +82,20 @@ void sys_sleep(const uint64_t us) { // A high-resolution profiling timer. Returns the current time in microseconds. double sys_profile_time(void) { - // TODO: Platform specific stuff +#if defined(HAVE_SDL2) + static double freq = 0.0; + if (freq == 0.0) + freq = (double)SDL_GetPerformanceFrequency() / 1000000.0; + return (double)SDL_GetPerformanceCounter() / freq; +#elif defined(CLOCK_MONOTONIC) struct timespec tv; clock_gettime(CLOCK_MONOTONIC, &tv); - return (double)(tv.tv_nsec) / 1000.0 + (double)(tv.tv_sec) * 1000000.0; + return (double)tv.tv_nsec / 1000.0 + (double)tv.tv_sec * 1000000.0; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_usec + (double)tv.tv_sec * 1000000.0; +#endif } /* this calls a platform-specific impl function after forming the error message */ @@ -97,9 +114,6 @@ void sys_fatal(const char *fmt, ...) { #ifdef HAVE_SDL2 -// we can just ask SDL for most of this shit if we have it -#include - // TEMPORARY: check the old save folder and copy contents to the new path // this will be removed after a while static inline bool copy_userdata(const char *userdir) { diff --git a/src/pc/thread.c b/src/pc/thread.c index f205900a..097ae945 100644 --- a/src/pc/thread.c +++ b/src/pc/thread.c @@ -1,26 +1,44 @@ #include "thread.h" -// TODO: Cross-platform stuff +#ifdef HAVE_POSIX_THREADS -void pcthread_mutex_lock(pthread_mutex_t *mutex) { +#include +#include + +int sys_thread_create(sys_thread_t *thread, sys_thread_func_t func, void *arg) { + return pthread_create(thread, NULL, func, arg); +} + +int sys_thread_join(sys_thread_t *thread, void **result) { + return pthread_join(*thread, result); +} + +void sys_mutex_lock(sys_mutex_t *mutex) { pthread_mutex_lock(mutex); } -void pcthread_mutex_unlock(pthread_mutex_t *mutex) { +void sys_mutex_unlock(sys_mutex_t *mutex) { pthread_mutex_unlock(mutex); } -void pcthread_semaphore_init(sem_t *sem, int pshared, unsigned int value) { + +void sys_semaphore_init(sys_sem_t *sem, int pshared, unsigned int value) { sem_init(sem, pshared, value); } -void pcthread_semaphore_wait(sem_t *sem) { +void sys_semaphore_wait(sys_sem_t *sem) { sem_wait(sem); } -void pcthread_semaphore_post(sem_t *sem) { +void sys_semaphore_post(sys_sem_t *sem) { sem_post(sem); } -void pcthread_semaphore_destroy(sem_t *sem) { +void sys_semaphore_destroy(sys_sem_t *sem) { sem_destroy(sem); -} \ No newline at end of file +} + +#else + +#error "Implement thread.c for your platform." + +#endif diff --git a/src/pc/thread.h b/src/pc/thread.h index eeda592a..f956bfe2 100644 --- a/src/pc/thread.h +++ b/src/pc/thread.h @@ -1,15 +1,30 @@ #ifndef _PC_THREAD_H #define _PC_THREAD_H -#include -#include -#include +// TODO: add other backends if required +#define HAVE_POSIX_THREADS 1 -void pcthread_mutex_lock(pthread_mutex_t *mutex); -void pcthread_mutex_unlock(pthread_mutex_t *mutex); -void pcthread_semaphore_init(sem_t *sem, int pshared, unsigned int value); -void pcthread_semaphore_wait(sem_t *sem); -void pcthread_semaphore_post(sem_t *sem); -void pcthread_semaphore_destroy(sem_t *sem); +#ifdef HAVE_POSIX_THREADS +#include +#include +typedef pthread_t sys_thread_t; +typedef void *(*sys_thread_func_t)(void *); +typedef pthread_mutex_t sys_mutex_t; +typedef sem_t sys_sem_t; +#define SYS_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#else +#error "Implement thread.c for your platform." +#endif -#endif // _PC_THREAD_H \ No newline at end of file +int sys_thread_create(sys_thread_t *thread, sys_thread_func_t func, void *arg); +int sys_thread_join(sys_thread_t *thread, void **result); + +void sys_mutex_lock(sys_mutex_t *mutex); +void sys_mutex_unlock(sys_mutex_t *mutex); + +void sys_semaphore_init(sys_sem_t *sem, int pshared, unsigned int value); +void sys_semaphore_wait(sys_sem_t *sem); +void sys_semaphore_post(sys_sem_t *sem); +void sys_semaphore_destroy(sys_sem_t *sem); + +#endif // _PC_THREAD_H