From ceb2f45acf41ce6e1c47f0662903ef222a64e61b Mon Sep 17 00:00:00 2001 From: Mefiresu <15063879+Mefiresu@users.noreply.github.com> Date: Sun, 1 Jun 2025 12:18:04 +0200 Subject: [PATCH] Add support for C++ std::thread and derivatives Implements dkp's gthread interface using LWP functions. With this, std::{thread,mutex,recursive_mutex,condition_variable} are now supported. --- Makefile | 3 +- libogc/ogc_gthread.cpp | 195 +++++++++++++++++++++++++++++++++++++++++ libogc/system.c | 3 + 3 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 libogc/ogc_gthread.cpp diff --git a/Makefile b/Makefile index 401abd29..8cc4b71f 100644 --- a/Makefile +++ b/Makefile @@ -100,6 +100,7 @@ INCLUDES += -I$(BASEDIR)/cube endif CFLAGS := $(FALSE_POSITIVES) -g -O2 -fno-strict-aliasing -Wall $(MACHDEP) $(INCLUDES) +CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti ASFLAGS := $(MACHDEP) -mregnames -D_LANGUAGE_ASSEMBLY $(INCLUDES) #--------------------------------------------------------------------------------- @@ -149,7 +150,7 @@ OGCOBJ := \ console_font_8x16.o timesupp.o lock_supp.o usbgecko.o usbmouse.o \ sbrk.o malloc_lock.o kprintf.o stm.o aes.o sha.o ios.o es.o isfs.o usb.o network_common.o \ sdgecko_io.o sdgecko_buf.o gcsd.o argv.o network_wii.o wiisd.o conf.o usbstorage.o \ - texconv.o wiilaunch.o sys_report.o + texconv.o wiilaunch.o sys_report.o ogc_gthread.o #--------------------------------------------------------------------------------- MODOBJ := freqtab.o mixer.o modplay.o semitonetab.o gcmodplay.o diff --git a/libogc/ogc_gthread.cpp b/libogc/ogc_gthread.cpp new file mode 100644 index 00000000..97c22a1f --- /dev/null +++ b/libogc/ogc_gthread.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include + +#include "lwp.h" +#include "mutex.h" +#include "cond.h" + +#define __OGC_GTHR_BASE_PRIO (64) + +#define __OGC_ONCE_INIT (0) +#define __OGC_ONCE_STARTED (1) +#define __OGC_ONCE_DONE (2) + +extern "C" { + +typedef struct { + lwpq_t queue; + lwp_t thread; +} gthr_thread_t; + +int __gthr_impl_active(void) +{ + return 1; +} + +int __gthr_impl_create(__gthread_t *__threadid, void *(*__func) (void*), void *__args) +{ + gthr_thread_t *th = (gthr_thread_t*)malloc(sizeof(gthr_thread_t)); + + if (!th) { + return ENOMEM; + } + + if (LWP_InitQueue(&th->queue) != LWP_SUCCESSFUL) { + free(th); + return EINVAL; + } + + if (LWP_CreateThread(&th->thread, __func, __args, NULL, 0, __OGC_GTHR_BASE_PRIO) != LWP_SUCCESSFUL) { + LWP_CloseQueue(th->queue); + free(th); + return EINVAL; + } + + *__threadid = (__gthread_t)th; + return 0; +} + +int __gthr_impl_join(__gthread_t __threadid, void **__value_ptr) +{ + gthr_thread_t *th = (gthr_thread_t*)__threadid; + + int res = LWP_JoinThread(th->thread, __value_ptr); + if (res != LWP_SUCCESSFUL) { + return -1; + } + + /* Clean up thread data */ + LWP_CloseQueue(th->queue); + free(th); + + return 0; +} + +int __gthr_impl_detach(__gthread_t __threadid) +{ + /* Not supported */ + return -1; +} + +int __gthr_impl_equal(__gthread_t __t1, __gthread_t __t2) +{ + return (gthr_thread_t*)__t1 == (gthr_thread_t*)__t2; +} + +__gthread_t __gthr_impl_self(void) +{ + /* + HACK: __gthread_self() is only used for std::thread::get_id(), so returning + LWP_GetSelf() works as a unique id even though it's technically not a thread + */ + return (__gthread_t)LWP_GetSelf(); +} + +int __gthr_impl_yield(void) +{ + LWP_YieldThread(); + return 0; +} + +int __gthr_impl_once(__gthread_once_t *__once, void (*__func) (void)) +{ + uint32_t expected = __OGC_ONCE_INIT; + if (__atomic_compare_exchange_n((uint32_t *)__once, &expected, __OGC_ONCE_STARTED, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { + __func(); + __atomic_store_n((uint32_t *)__once, __OGC_ONCE_DONE, __ATOMIC_RELEASE); + } else if (expected != __OGC_ONCE_DONE) { + do { + __atomic_load((uint32_t *)__once, &expected, __ATOMIC_ACQUIRE); + } while (expected != __OGC_ONCE_DONE); + } + + return 0; +} + +void __gthr_impl_mutex_init_function(__gthread_mutex_t *mutex) +{ + LWP_MutexInit(((mutex_t*)mutex), false); +} + +int __gthr_impl_mutex_lock(__gthread_mutex_t *mutex) +{ + return LWP_MutexLock(*((mutex_t*)mutex)); +} + +int __gthr_impl_mutex_trylock(__gthread_mutex_t *mutex) +{ + return LWP_MutexTryLock(*((mutex_t*)mutex)); +} + +int __gthr_impl_mutex_unlock(__gthread_mutex_t *mutex) +{ + return LWP_MutexUnlock(*((mutex_t*)mutex)); +} + +int __gthr_impl_mutex_destroy(__gthread_mutex_t *mutex) +{ + return LWP_MutexDestroy(*((mutex_t*)mutex)); +} + +int __gthr_impl_recursive_mutex_init_function(__gthread_recursive_mutex_t *mutex) +{ + return LWP_MutexInit(((mutex_t *)mutex), true); +} + +int __gthr_impl_recursive_mutex_lock(__gthread_recursive_mutex_t *mutex) +{ + return LWP_MutexLock(*((mutex_t*)mutex)); +} + +int __gthr_impl_recursive_mutex_trylock(__gthread_recursive_mutex_t *mutex) +{ + return LWP_MutexTryLock(*((mutex_t*)mutex)); +} + +int __gthr_impl_recursive_mutex_unlock(__gthread_recursive_mutex_t *mutex) +{ + return LWP_MutexUnlock(*((mutex_t*)mutex)); +} + +int __gthr_impl_recursive_mutex_destroy(__gthread_recursive_mutex_t *mutex) +{ + return LWP_MutexDestroy(*((mutex_t*)mutex)); +} + +void __gthr_impl_cond_init_function(__gthread_cond_t *__cond) +{ + LWP_CondInit((cond_t*)__cond); +} + +int __gthr_impl_cond_broadcast(__gthread_cond_t *__cond) +{ + return LWP_CondBroadcast(*(cond_t*)__cond); +} + +int __gthr_impl_cond_signal(__gthread_cond_t *__cond) +{ + return LWP_CondSignal(*(cond_t*)__cond); +} + +int __gthr_impl_cond_wait(__gthread_cond_t *__cond, __gthread_mutex_t *__mutex) +{ + return LWP_CondWait(*(cond_t*)__cond, *(mutex_t*)__mutex); +} + +int __gthr_impl_cond_timedwait(__gthread_cond_t *__cond, __gthread_mutex_t *__mutex, const __gthread_time_t *__abs_timeout) +{ + return LWP_CondTimedWait(*(cond_t*)__cond, *(mutex_t*)__mutex, __abs_timeout); +} + +int __gthr_impl_cond_wait_recursive(__gthread_cond_t *__cond, __gthread_recursive_mutex_t *__mutex) +{ + return LWP_CondWait(*(cond_t*)__cond, *(mutex_t*)__mutex); +} + +int __gthr_impl_cond_destroy(__gthread_cond_t* __cond) +{ + return LWP_CondDestroy(*(cond_t*)__cond); +} + +/* Dummy function required so that the linker doesn't strip this module */ +void __ogc_gthread_init() {} +} \ No newline at end of file diff --git a/libogc/system.c b/libogc/system.c index 504625e1..f7fcc57a 100644 --- a/libogc/system.c +++ b/libogc/system.c @@ -215,6 +215,8 @@ extern int __libogc_nanosleep(const struct timespec *tb, struct timespec *rem); extern u64 gettime(void); extern void settime(u64); +extern void __ogc_gthread_init(); + extern u8 __gxregs[]; extern u8 __text_start[]; extern u8 __isIPL[]; @@ -1097,6 +1099,7 @@ void SYS_Init(void) IRQ_Request(IRQ_PI_RSW,__RSWHandler,NULL); __MaskIrq(IRQMASK(IRQ_PI_RSW)); #endif + __ogc_gthread_init(); __lwp_thread_startmultitasking(); _CPU_ISR_Restore(level); }