threads: support using existing mem as stack

This commit is contained in:
Michael Scire 2019-10-18 18:21:31 -07:00
parent 5f5d4c9785
commit 3109b2c480
3 changed files with 98 additions and 66 deletions

View File

@ -12,6 +12,7 @@
/// Thread information structure.
typedef struct Thread {
Handle handle; ///< Thread handle.
bool owns_stack_mem; ///< Whether the stack memory is automatically allocated.
void* stack_mem; ///< Pointer to stack memory.
void* stack_mirror; ///< Pointer to stack memory mirror.
size_t stack_sz; ///< Stack size.
@ -31,14 +32,15 @@ static inline Waiter waiterForThread(Thread* t)
* @param t Thread information structure which will be filled in.
* @param entry Entrypoint of the thread.
* @param arg Argument to pass to the entrypoint.
* @param stack_sz Stack size (rounded up to page alignment).
* @param stack_mem Memory to use as backing for stack/tls/reent. Must be page-aligned. NULL argument means to allocate new memory.
* @param stack_sz Stack size (rounded up to page alignment if stack_mem is NULL).
* @param prio Thread priority (0x00~0x3F); 0x2C is the usual priority of the main thread, 0x3B is a special priority on cores 0..2 that enables preemptive multithreading (0x3F on core 3).
* @param cpuid ID of the core on which to create the thread (0~3); or -2 to use the default core for the current process.
* @return Result code.
*/
Result threadCreate(
Thread* t, ThreadFunc entry, void* arg, size_t stack_sz, int prio,
int cpuid);
Thread* t, ThreadFunc entry, void* arg, void *stack_mem, size_t stack_sz,
int prio, int cpuid);
/**
* @brief Starts the execution of a thread.

View File

@ -61,26 +61,50 @@ static void _EntryWrap(ThreadEntryArgs* args) {
}
Result threadCreate(
Thread* t, ThreadFunc entry, void* arg, size_t stack_sz, int prio,
int cpuid)
Thread* t, ThreadFunc entry, void* arg, void* stack_mem, size_t stack_sz,
int prio, int cpuid)
{
void* tls;
const size_t tls_sz = (__tls_end-__tls_start+0xF) &~ 0xF;
void* reent;
const size_t reent_sz = (sizeof(struct _reent)+0xF) &~ 0xF;
if (stack_mem == NULL) {
// Allocate new memory, stack then reent then tls.
stack_sz = (stack_sz+0xFFF) & ~0xFFF;
stack_mem = memalign(0x1000, stack_sz + reent_sz + tls_sz);
reent = (void*)((uintptr_t)stack_mem + stack_sz);
tls = (void*)((uintptr_t)reent + reent_sz);
Result rc = 0;
size_t reent_sz = (sizeof(struct _reent)+0xF) &~ 0xF;
size_t tls_sz = (__tls_end-__tls_start+0xF) &~ 0xF;
void* stack = memalign(0x1000, stack_sz + reent_sz + tls_sz);
if (stack == NULL) {
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
t->owns_stack_mem = true;
} else {
// Use provided memory for stack, reent, and tls.
if (((uintptr_t)stack_mem & 0xFFF) || (stack_sz & 0xFFF)) {
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
}
else {
tls = (void*)((uintptr_t)stack_mem + stack_sz - tls_sz);
reent = (void*)((uintptr_t)tls - reent_sz);
// Ensure we don't go out of bounds.
if ((uintptr_t)reent <= (uintptr_t)stack_mem) {
return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
}
t->owns_stack_mem = false;
}
if (stack_mem == NULL) {
return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
}
void* stack_mirror = virtmemReserveStack(stack_sz);
rc = svcMapMemory(stack_mirror, stack, stack_sz);
Result rc = svcMapMemory(stack_mirror, stack_mem, stack_sz);
if (R_SUCCEEDED(rc))
{
u64 stack_top = ((u64)stack_mirror) + stack_sz - sizeof(ThreadEntryArgs);
uintptr_t stack_top = ((uintptr_t)stack_mirror) + stack_sz - sizeof(ThreadEntryArgs);
ThreadEntryArgs* args = (ThreadEntryArgs*) stack_top;
Handle handle;
@ -91,9 +115,9 @@ Result threadCreate(
if (R_SUCCEEDED(rc))
{
t->handle = handle;
t->stack_mem = stack;
t->stack_mem = stack_mem;
t->stack_mirror = stack_mirror;
t->stack_sz = stack_sz;
t->stack_sz = stack_sz - sizeof(ThreadEntryArgs);
t->tls_array = NULL;
t->next = NULL;
t->prev_next = NULL;
@ -101,8 +125,8 @@ Result threadCreate(
args->t = t;
args->entry = entry;
args->arg = arg;
args->reent = (struct _reent*)((u8*)stack + stack_sz);
args->tls = (u8*)stack + stack_sz + reent_sz;
args->reent = reent;
args->tls = tls;
// Set up child thread's reent struct, inheriting standard file handles
_REENT_INIT_PTR(args->reent);
@ -121,13 +145,14 @@ Result threadCreate(
}
if (R_FAILED(rc)) {
svcUnmapMemory(stack_mirror, stack, stack_sz);
svcUnmapMemory(stack_mirror, stack_mem, stack_sz);
}
}
if (R_FAILED(rc)) {
virtmemFreeStack(stack_mirror, stack_sz);
free(stack);
if (t->owns_stack_mem) {
free(stack_mem);
}
}
@ -178,9 +203,14 @@ Result threadClose(Thread* t) {
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
rc = svcUnmapMemory(t->stack_mirror, t->stack_mem, t->stack_sz);
if (R_SUCCEEDED(rc)) {
virtmemFreeStack(t->stack_mirror, t->stack_sz);
if (t->owns_stack_mem) {
free(t->stack_mem);
}
svcCloseHandle(t->handle);
}
return rc;
}

View File

@ -170,7 +170,7 @@ static void __thread_entry(void* __arg)
int __syscall_thread_create(struct __pthread_t **thread, void* (*func)(void*), void *arg, void *stack_addr, size_t stack_size)
{
if (stack_addr || (stack_size & 0xFFF))
if (((uintptr_t)stack_addr & 0xFFF) || (stack_size & 0xFFF))
return EINVAL;
if (!stack_size)
stack_size = 128*1024;
@ -194,7 +194,7 @@ int __syscall_thread_create(struct __pthread_t **thread, void* (*func)(void*), v
mutexInit(&info.mutex);
condvarInit(&info.cond);
rc = threadCreate(&t->thr, __thread_entry, &info, stack_size, 0x3B, -2);
rc = threadCreate(&t->thr, __thread_entry, &info, stack_addr, stack_size, 0x3B, -2);
if (R_FAILED(rc))
goto _error1;