diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index a2c8e72..6d59617 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -16,6 +16,7 @@ extern "C" { #include <3ds/srv.h> #include <3ds/os.h> #include <3ds/synchronization.h> +#include <3ds/thread.h> #include <3ds/gfx.h> #include <3ds/console.h> #include <3ds/env.h> diff --git a/libctru/include/3ds/svc.h b/libctru/include/3ds/svc.h index c5ab683..8a5af58 100644 --- a/libctru/include/3ds/svc.h +++ b/libctru/include/3ds/svc.h @@ -579,7 +579,7 @@ Result svcSetThreadIdealProcessor(Handle thread, s32 processorid); * @brief Returns the ID of the processor the current thread is running on. * @sa svcCreateThread */ -s32 svcGetProcessorID(); +s32 svcGetProcessorID(void); /** * @brief Gets the ID of a thread. @@ -755,7 +755,7 @@ Result svcClearTimer(Handle timer); * @brief Gets the current system tick. * @return The current system tick. */ -u64 svcGetSystemTick(); +u64 svcGetSystemTick(void); ///@} ///@name System diff --git a/libctru/include/3ds/thread.h b/libctru/include/3ds/thread.h new file mode 100644 index 0000000..36ede63 --- /dev/null +++ b/libctru/include/3ds/thread.h @@ -0,0 +1,20 @@ +/** + * @file thread.h + * @brief Provides functions to use threads. + */ +#pragma once +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/synchronization.h> +#include <3ds/svc.h> + +typedef struct Thread_tag* Thread; + +Thread threadCreate(ThreadFunc entrypoint, void* arg, size_t stack_size, int prio, int affinity, bool detached); +Handle threadGetHandle(Thread thread); +int threadGetExitCode(Thread thread); +void threadFree(Thread thread); +Result threadJoin(Thread thread, u64 timeout_ns); + +Thread threadGetCurrent(void); +void threadExit(int rc) __attribute__((noreturn)); diff --git a/libctru/source/internal.h b/libctru/source/internal.h index 53a4c9f..269c9c3 100644 --- a/libctru/source/internal.h +++ b/libctru/source/internal.h @@ -1,8 +1,9 @@ #pragma once +#include #include <3ds/types.h> #include <3ds/result.h> #include <3ds/svc.h> -#include +#include <3ds/thread.h> #define THREADVARS_MAGIC 0x21545624 // !TV$ #define FS_OVERRIDE_MAGIC 0x21465324 // !FS$ @@ -13,6 +14,9 @@ typedef struct // Magic value used to check if the struct is initialized u32 magic; + // Pointer to the current thread (if exists) + Thread thread_ptr; + // Pointer to this thread's newlib state struct _reent* reent; diff --git a/libctru/source/system/syscalls.c b/libctru/source/system/syscalls.c index 3cb5734..a27482b 100644 --- a/libctru/source/system/syscalls.c +++ b/libctru/source/system/syscalls.c @@ -45,4 +45,5 @@ void __system_initSyscalls(void) ThreadVars* tv = getThreadVars(); tv->magic = THREADVARS_MAGIC; tv->reent = _impure_ptr; + tv->thread_ptr = NULL; } diff --git a/libctru/source/thread.c b/libctru/source/thread.c new file mode 100644 index 0000000..382534c --- /dev/null +++ b/libctru/source/thread.c @@ -0,0 +1,112 @@ +#include "internal.h" +#include +#include + +struct Thread_tag +{ + Handle handle; + ThreadFunc ep; + void* arg; + int rc; + bool detached, finished; + struct _reent reent; + void* stacktop; +}; + +static void __panic(void) +{ + svcBreak(USERBREAK_PANIC); + for (;;); +} + +static void _thread_begin(void* arg) +{ + Thread t = (Thread)arg; + ThreadVars* tv = getThreadVars(); + tv->magic = THREADVARS_MAGIC; + tv->reent = &t->reent; + tv->thread_ptr = t; + t->ep(t->arg); + threadExit(0); +} + +Thread threadCreate(ThreadFunc entrypoint, void* arg, size_t stack_size, int prio, int affinity, bool detached) +{ + size_t stackoffset = (sizeof(struct Thread_tag)+7)&~7; + size_t allocsize = stackoffset + ((stack_size+7)&~7); + if (allocsize < stackoffset) return NULL; // guard against overflow + if ((allocsize-stackoffset) < stack_size) return NULL; // guard against overflow + Thread t = (Thread)malloc(allocsize); + if (!t) return NULL; + + t->ep = entrypoint; + t->arg = arg; + t->detached = detached; + t->finished = false; + t->stacktop = (u8*)t + allocsize; + + // Set up child thread's reent struct, inheriting standard file handles + _REENT_INIT_PTR(&t->reent); + struct _reent* cur = getThreadVars()->reent; + t->reent._stdin = cur->_stdin; + t->reent._stdout = cur->_stdout; + t->reent._stderr = cur->_stderr; + + Result rc; + rc = svcCreateThread(&t->handle, _thread_begin, (u32)t, (u32*)t->stacktop, prio, affinity); + if (R_FAILED(rc)) + { + free(t); + return NULL; + } + + return t; +} + +Handle threadGetHandle(Thread thread) +{ + if (!thread || thread->finished) return ~0UL; + return thread->handle; +} + +int threadGetExitCode(Thread thread) +{ + if (!thread || !thread->finished) return 0; + return thread->rc; +} + +void threadFree(Thread thread) +{ + if (!thread || !thread->finished) return; + svcCloseHandle(thread->handle); + free(thread); +} + +Result threadJoin(Thread thread, u64 timeout_ns) +{ + if (!thread || thread->finished) return 0; + return svcWaitSynchronization(thread->handle, timeout_ns); +} + +Thread threadGetCurrent(void) +{ + ThreadVars* tv = getThreadVars(); + if (tv->magic != THREADVARS_MAGIC) + __panic(); + return tv->thread_ptr; +} + +void threadExit(int rc) +{ + Thread t = threadGetCurrent(); + if (!t) + __panic(); + + t->finished = true; + if (t->detached) + threadFree(t); + else + t->rc = rc; + + svcExitThread(); +}