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

@ -11,10 +11,11 @@
/// Thread information structure. /// Thread information structure.
typedef struct Thread { typedef struct Thread {
Handle handle; ///< Thread handle. Handle handle; ///< Thread handle.
void* stack_mem; ///< Pointer to stack memory. bool owns_stack_mem; ///< Whether the stack memory is automatically allocated.
void* stack_mirror; ///< Pointer to stack memory mirror. void* stack_mem; ///< Pointer to stack memory.
size_t stack_sz; ///< Stack size. void* stack_mirror; ///< Pointer to stack memory mirror.
size_t stack_sz; ///< Stack size.
void** tls_array; void** tls_array;
struct Thread* next; struct Thread* next;
struct Thread** prev_next; struct Thread** prev_next;
@ -31,14 +32,15 @@ static inline Waiter waiterForThread(Thread* t)
* @param t Thread information structure which will be filled in. * @param t Thread information structure which will be filled in.
* @param entry Entrypoint of the thread. * @param entry Entrypoint of the thread.
* @param arg Argument to pass to the entrypoint. * @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 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. * @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. * @return Result code.
*/ */
Result threadCreate( Result threadCreate(
Thread* t, ThreadFunc entry, void* arg, size_t stack_sz, int prio, Thread* t, ThreadFunc entry, void* arg, void *stack_mem, size_t stack_sz,
int cpuid); int prio, int cpuid);
/** /**
* @brief Starts the execution of a thread. * @brief Starts the execution of a thread.

View File

@ -61,73 +61,98 @@ static void _EntryWrap(ThreadEntryArgs* args) {
} }
Result threadCreate( Result threadCreate(
Thread* t, ThreadFunc entry, void* arg, size_t stack_sz, int prio, Thread* t, ThreadFunc entry, void* arg, void* stack_mem, size_t stack_sz,
int cpuid) int prio, int cpuid)
{ {
stack_sz = (stack_sz+0xFFF) &~ 0xFFF;
Result rc = 0; void* tls;
size_t reent_sz = (sizeof(struct _reent)+0xF) &~ 0xF; const size_t tls_sz = (__tls_end-__tls_start+0xF) &~ 0xF;
size_t tls_sz = (__tls_end-__tls_start+0xF) &~ 0xF; void* reent;
void* stack = memalign(0x1000, stack_sz + reent_sz + tls_sz); const size_t reent_sz = (sizeof(struct _reent)+0xF) &~ 0xF;
if (stack == NULL) { if (stack_mem == NULL) {
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); // 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);
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);
}
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;
} }
else {
void* stack_mirror = virtmemReserveStack(stack_sz); if (stack_mem == NULL) {
rc = svcMapMemory(stack_mirror, stack, stack_sz); return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
}
void* stack_mirror = virtmemReserveStack(stack_sz);
Result rc = svcMapMemory(stack_mirror, stack_mem, stack_sz);
if (R_SUCCEEDED(rc))
{
uintptr_t stack_top = ((uintptr_t)stack_mirror) + stack_sz - sizeof(ThreadEntryArgs);
ThreadEntryArgs* args = (ThreadEntryArgs*) stack_top;
Handle handle;
rc = svcCreateThread(
&handle, (ThreadFunc) &_EntryWrap, args, (void*)stack_top,
prio, cpuid);
if (R_SUCCEEDED(rc)) if (R_SUCCEEDED(rc))
{ {
u64 stack_top = ((u64)stack_mirror) + stack_sz - sizeof(ThreadEntryArgs); t->handle = handle;
ThreadEntryArgs* args = (ThreadEntryArgs*) stack_top; t->stack_mem = stack_mem;
Handle handle; t->stack_mirror = stack_mirror;
t->stack_sz = stack_sz - sizeof(ThreadEntryArgs);
t->tls_array = NULL;
t->next = NULL;
t->prev_next = NULL;
rc = svcCreateThread( args->t = t;
&handle, (ThreadFunc) &_EntryWrap, args, (void*)stack_top, args->entry = entry;
prio, cpuid); args->arg = arg;
args->reent = reent;
args->tls = tls;
if (R_SUCCEEDED(rc)) // Set up child thread's reent struct, inheriting standard file handles
{ _REENT_INIT_PTR(args->reent);
t->handle = handle; struct _reent* cur = getThreadVars()->reent;
t->stack_mem = stack; args->reent->_stdin = cur->_stdin;
t->stack_mirror = stack_mirror; args->reent->_stdout = cur->_stdout;
t->stack_sz = stack_sz; args->reent->_stderr = cur->_stderr;
t->tls_array = NULL;
t->next = NULL;
t->prev_next = NULL;
args->t = t; // Set up child thread's TLS segment
args->entry = entry; size_t tls_load_sz = __tdata_lma_end - __tdata_lma;
args->arg = arg; size_t tls_bss_sz = tls_sz - tls_load_sz;
args->reent = (struct _reent*)((u8*)stack + stack_sz); if (tls_load_sz)
args->tls = (u8*)stack + stack_sz + reent_sz; memcpy(args->tls, __tdata_lma, tls_load_sz);
if (tls_bss_sz)
// Set up child thread's reent struct, inheriting standard file handles memset(args->tls+tls_load_sz, 0, tls_bss_sz);
_REENT_INIT_PTR(args->reent);
struct _reent* cur = getThreadVars()->reent;
args->reent->_stdin = cur->_stdin;
args->reent->_stdout = cur->_stdout;
args->reent->_stderr = cur->_stderr;
// Set up child thread's TLS segment
size_t tls_load_sz = __tdata_lma_end - __tdata_lma;
size_t tls_bss_sz = tls_sz - tls_load_sz;
if (tls_load_sz)
memcpy(args->tls, __tdata_lma, tls_load_sz);
if (tls_bss_sz)
memset(args->tls+tls_load_sz, 0, tls_bss_sz);
}
if (R_FAILED(rc)) {
svcUnmapMemory(stack_mirror, stack, stack_sz);
}
} }
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
virtmemFreeStack(stack_mirror, stack_sz); svcUnmapMemory(stack_mirror, stack_mem, stack_sz);
free(stack); }
}
if (R_FAILED(rc)) {
virtmemFreeStack(stack_mirror, stack_sz);
if (t->owns_stack_mem) {
free(stack_mem);
} }
} }
@ -178,9 +203,14 @@ Result threadClose(Thread* t) {
return MAKERESULT(Module_Libnx, LibnxError_BadInput); return MAKERESULT(Module_Libnx, LibnxError_BadInput);
rc = svcUnmapMemory(t->stack_mirror, t->stack_mem, t->stack_sz); rc = svcUnmapMemory(t->stack_mirror, t->stack_mem, t->stack_sz);
virtmemFreeStack(t->stack_mirror, t->stack_sz);
free(t->stack_mem); if (R_SUCCEEDED(rc)) {
svcCloseHandle(t->handle); virtmemFreeStack(t->stack_mirror, t->stack_sz);
if (t->owns_stack_mem) {
free(t->stack_mem);
}
svcCloseHandle(t->handle);
}
return rc; 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) 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; return EINVAL;
if (!stack_size) if (!stack_size)
stack_size = 128*1024; stack_size = 128*1024;
@ -194,7 +194,7 @@ int __syscall_thread_create(struct __pthread_t **thread, void* (*func)(void*), v
mutexInit(&info.mutex); mutexInit(&info.mutex);
condvarInit(&info.cond); 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)) if (R_FAILED(rc))
goto _error1; goto _error1;