From f4580c6d5a44379f3b1be033f39ec7af78dbbbfa Mon Sep 17 00:00:00 2001 From: Siva Chandra Reddy Date: Thu, 23 Jun 2022 18:18:50 +0000 Subject: [PATCH] [libc][NFC] Remove the templatization from the linux implementation of thread. This enables setting up a single "self" thread object to be returned by API like thrd_self and pthread_self. --- libc/src/__support/threads/linux/thread.h | 119 +++++++++++++++++++---------- libc/src/__support/threads/thread.h | 22 +++++- libc/src/__support/threads/thread_attrib.h | 8 +- libc/src/pthread/pthread_create.cpp | 6 +- libc/src/pthread/pthread_detach.cpp | 6 +- libc/src/pthread/pthread_join.cpp | 6 +- libc/src/threads/thrd_create.cpp | 6 +- libc/src/threads/thrd_detach.cpp | 6 +- libc/src/threads/thrd_join.cpp | 6 +- 9 files changed, 120 insertions(+), 65 deletions(-) diff --git a/libc/src/__support/threads/linux/thread.h b/libc/src/__support/threads/linux/thread.h index a947613..365f0fc 100644 --- a/libc/src/__support/threads/linux/thread.h +++ b/libc/src/__support/threads/linux/thread.h @@ -27,8 +27,6 @@ namespace __llvm_libc { -template struct Thread; - #ifdef SYS_mmap2 static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap2; #elif SYS_mmap @@ -72,16 +70,16 @@ static inline void free_stack(void *stack, size_t size) { __llvm_libc::syscall(SYS_munmap, stack, size); } -template using ThreadRunner = ReturnType(void *); +struct Thread; // We align the start args to 16-byte boundary as we adjust the allocated // stack memory with its size. We want the adjusted address to be at a // 16-byte boundary to satisfy the x86_64 and aarch64 ABI requirements. // If different architecture in future requires higher alignment, then we // can add a platform specific alignment spec. -template struct alignas(STACK_ALIGNMENT) StartArgs { - Thread *thread; - ThreadRunner *func; +struct alignas(STACK_ALIGNMENT) StartArgs { + Thread *thread; + ThreadRunner runner; void *arg; }; @@ -104,19 +102,57 @@ __attribute__((always_inline)) inline uintptr_t get_start_args_addr() { #endif } -template struct Thread { +struct Thread { private: - ThreadAttributes *attrib; + ThreadAttributes *attrib; cpp::Atomic *clear_tid; public: Thread() = default; - static void start_thread() __attribute__((noinline)); + static void start_thread() __attribute__((noinline)) { + auto *start_args = reinterpret_cast(get_start_args_addr()); + auto *thread = start_args->thread; + auto *attrib = thread->attrib; + long retval; + if (attrib->style == ThreadStyle::POSIX) { + attrib->retval.posix_retval = + start_args->runner.posix_runner(start_args->arg); + retval = long(attrib->retval.posix_retval); + } else { + attrib->retval.stdc_retval = + start_args->runner.stdc_runner(start_args->arg); + retval = long(attrib->retval.stdc_retval); + } - // Return 0 on success or an error value on failure. - int run(ThreadRunner *f, void *arg, void *stack, size_t size, + uint32_t joinable_state = uint32_t(DetachState::JOINABLE); + if (!thread->attrib->detach_state.compare_exchange_strong( + joinable_state, uint32_t(DetachState::EXITING))) { + // Thread is detached so cleanup the resources. + if (thread->attrib->owned_stack) + free_stack(thread->attrib->stack, thread->attrib->stack_size); + } + + __llvm_libc::syscall(SYS_exit, retval); + } + + int run(ThreadRunnerPosix *func, void *arg, void *stack, size_t size, + bool detached = false) { + ThreadRunner runner; + runner.posix_runner = func; + return run(ThreadStyle::POSIX, runner, arg, stack, size, detached); + } + + int run(ThreadRunnerStdc *func, void *arg, void *stack, size_t size, bool detached = false) { + ThreadRunner runner; + runner.stdc_runner = func; + return run(ThreadStyle::STDC, runner, arg, stack, size, detached); + } + + // Return 0 on success or an error value on failure. + int run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack, + size_t size, bool detached) { bool owned_stack = false; if (stack == nullptr) { if (size == 0) @@ -139,19 +175,18 @@ public: // Likewise, the actual thread state information is also stored on the // stack memory. uintptr_t adjusted_stack = reinterpret_cast(stack) + size - - sizeof(StartArgs) - - sizeof(ThreadAttributes) - + sizeof(StartArgs) - sizeof(ThreadAttributes) - sizeof(cpp::Atomic); adjusted_stack &= ~(uintptr_t(STACK_ALIGNMENT) - 1); - auto *start_args = - reinterpret_cast *>(adjusted_stack); + auto *start_args = reinterpret_cast(adjusted_stack); start_args->thread = this; - start_args->func = f; + start_args->runner = runner; start_args->arg = arg; - attrib = reinterpret_cast *>( - adjusted_stack + sizeof(StartArgs)); + attrib = reinterpret_cast(adjusted_stack + + sizeof(StartArgs)); + attrib->style = style; attrib->detach_state = uint32_t(detached ? DetachState::DETACHED : DetachState::JOINABLE); attrib->stack = stack; @@ -159,8 +194,7 @@ public: attrib->owned_stack = owned_stack; clear_tid = reinterpret_cast *>( - adjusted_stack + sizeof(StartArgs) + - sizeof(ThreadAttributes)); + adjusted_stack + sizeof(StartArgs) + sizeof(ThreadAttributes)); clear_tid->val = CLEAR_TID_VALUE; // The clone syscall takes arguments in an architecture specific order. @@ -203,10 +237,32 @@ public: return 0; } - int join(ReturnType *retval) { + int join(int *val) { + ThreadReturnValue retval; + int status = join(retval); + if (status != 0) + return status; + *val = retval.stdc_retval; + return 0; + } + + int join(void **val) { + ThreadReturnValue retval; + int status = join(retval); + if (status != 0) + return status; + *val = retval.posix_retval; + return 0; + } + + int join(ThreadReturnValue &retval) { wait(); - *retval = attrib->retval; + if (attrib->style == ThreadStyle::POSIX) + retval.posix_retval = attrib->retval.posix_retval; + else + retval.stdc_retval = attrib->retval.stdc_retval; + if (attrib->owned_stack) free_stack(attrib->stack, attrib->stack_size); @@ -258,25 +314,6 @@ public: } }; -template -__attribute__((noinline)) void Thread::start_thread() { - auto *start_args = - reinterpret_cast *>(get_start_args_addr()); - auto *thread = start_args->thread; - ReturnType retval = thread->attrib->retval = - start_args->func(start_args->arg); - - uint32_t joinable_state = uint32_t(DetachState::JOINABLE); - if (!thread->attrib->detach_state.compare_exchange_strong( - joinable_state, uint32_t(DetachState::EXITING))) { - // Thread is detached so cleanup the resources. - if (thread->attrib->owned_stack) - free_stack(thread->attrib->stack, thread->attrib->stack_size); - } - - __llvm_libc::syscall(SYS_exit, retval); -} - } // namespace __llvm_libc #endif // LLVM_LIBC_SRC_SUPPORT_THREADS_LINUX_THREAD_H diff --git a/libc/src/__support/threads/thread.h b/libc/src/__support/threads/thread.h index 2d38c37..368df2b 100644 --- a/libc/src/__support/threads/thread.h +++ b/libc/src/__support/threads/thread.h @@ -11,6 +11,19 @@ #include +using ThreadRunnerPosix = void *(void *); +using ThreadRunnerStdc = int(void *); + +union ThreadRunner { + ThreadRunnerPosix *posix_runner; + ThreadRunnerStdc *stdc_runner; +}; + +union ThreadReturnValue { + void *posix_retval; + int stdc_retval; +}; + // The platform specific implemnetations are pulled via the following include. // The idea is for the platform implementation to implement a class named Thread // in the namespace __llvm_libc with the following properties: @@ -19,22 +32,25 @@ // // 2. Has a "run" method with the following signature: // -// int run(ThreadRunner *f, void *arg, void *stack, size_t size); +// int run(ThreadRunner runner, void *arg, void *stack, size_t size, +// bool detached); // // Returns: // 0 on success and an error value on failure. // Args: +// runner - The function to execute in the new thread. // arg - The argument to be passed to the thread runner after the thread // is created. // stack - The stack to use for the thread. // size - The stack size. +// detached - The detached state of the thread at startup. // -// If callers pass a non-null |stack| value, then it will assumed that +// If callers pass a non-null |stack| value, then it will be assumed that // 1. The clean up the stack memory is their responsibility // 2. The guard area is setup appropriately by the caller. // // 3. Has a "join" method with the following signature: -// ErrorOr join(); +// int join(ThreadReturnValue &retval); // The "join" method should return 0 on success and set retcode to the // threads return value. On failure, an appropriate errno value should be // returned. diff --git a/libc/src/__support/threads/thread_attrib.h b/libc/src/__support/threads/thread_attrib.h index 31d97ac..c4deabd 100644 --- a/libc/src/__support/threads/thread_attrib.h +++ b/libc/src/__support/threads/thread_attrib.h @@ -27,6 +27,8 @@ enum class DetachState : uint32_t { DETACHED = 0x33 }; +enum class ThreadStyle : uint8_t { POSIX = 0x1, STDC = 0x2 }; + // Detach type is useful in testing the detach operation. enum class DetachType : int { // Indicates that the detach operation just set the detach state to DETACHED @@ -44,7 +46,6 @@ enum class DetachType : int { // // Thread attributes are typically stored on the stack. So, we align as required // for the target architecture. -template struct alignas(STACK_ALIGNMENT) ThreadAttributes { // We want the "detach_state" attribute to be an atomic value as it could be // updated by one thread while the self thread is reading it. It is a tristate @@ -66,12 +67,13 @@ struct alignas(STACK_ALIGNMENT) ThreadAttributes { // exits. cpp::Atomic detach_state; void *stack; // Pointer to the thread stack + void *tls; unsigned long long stack_size; // Size of the stack unsigned char owned_stack; // Indicates if the thread owns this stack memory - ReturnType retval; // The return value of thread runner is saved here int tid; + ThreadStyle style; + ThreadReturnValue retval; }; - } // namespace __llvm_libc #endif // LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_ATTRIB_H diff --git a/libc/src/pthread/pthread_create.cpp b/libc/src/pthread/pthread_create.cpp index bc945a9..ec1e0e3 100644 --- a/libc/src/pthread/pthread_create.cpp +++ b/libc/src/pthread/pthread_create.cpp @@ -16,14 +16,14 @@ namespace __llvm_libc { -static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread), - "Mismatch between pthread_t and internal Thread."); +static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread), + "Mismatch between pthread_t and internal Thread."); LLVM_LIBC_FUNCTION(int, pthread_create, (pthread_t *__restrict th, const pthread_attr_t *__restrict attr, __pthread_start_t func, void *arg)) { - auto *thread = reinterpret_cast<__llvm_libc::Thread *>(th); + auto *thread = reinterpret_cast<__llvm_libc::Thread *>(th); int result = thread->run(func, arg, nullptr, 0); if (result != 0 && result != EPERM) return EAGAIN; diff --git a/libc/src/pthread/pthread_detach.cpp b/libc/src/pthread/pthread_detach.cpp index c71b12f..009438e 100644 --- a/libc/src/pthread/pthread_detach.cpp +++ b/libc/src/pthread/pthread_detach.cpp @@ -15,11 +15,11 @@ namespace __llvm_libc { -static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread), - "Mismatch between pthread_t and internal Thread."); +static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread), + "Mismatch between pthread_t and internal Thread."); LLVM_LIBC_FUNCTION(int, pthread_detach, (pthread_t th)) { - auto *thread = reinterpret_cast *>(&th); + auto *thread = reinterpret_cast(&th); thread->detach(); return 0; } diff --git a/libc/src/pthread/pthread_join.cpp b/libc/src/pthread/pthread_join.cpp index c3bf4ad..0774d02 100644 --- a/libc/src/pthread/pthread_join.cpp +++ b/libc/src/pthread/pthread_join.cpp @@ -15,11 +15,11 @@ namespace __llvm_libc { -static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread), - "Mismatch between pthread_t and internal Thread."); +static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread), + "Mismatch between pthread_t and internal Thread."); LLVM_LIBC_FUNCTION(int, pthread_join, (pthread_t th, void **retval)) { - auto *thread = reinterpret_cast *>(&th); + auto *thread = reinterpret_cast(&th); int result = thread->join(retval); return result; } diff --git a/libc/src/threads/thrd_create.cpp b/libc/src/threads/thrd_create.cpp index 91b36a7..9e81f0f 100644 --- a/libc/src/threads/thrd_create.cpp +++ b/libc/src/threads/thrd_create.cpp @@ -15,12 +15,12 @@ namespace __llvm_libc { -static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread), - "Mismatch between thrd_t and internal Thread."); +static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread), + "Mismatch between thrd_t and internal Thread."); LLVM_LIBC_FUNCTION(int, thrd_create, (thrd_t * th, thrd_start_t func, void *arg)) { - auto *thread = reinterpret_cast<__llvm_libc::Thread *>(th); + auto *thread = reinterpret_cast<__llvm_libc::Thread *>(th); int result = thread->run(func, arg, nullptr, 0); if (result == 0) return thrd_success; diff --git a/libc/src/threads/thrd_detach.cpp b/libc/src/threads/thrd_detach.cpp index e3c4301..20c6540 100644 --- a/libc/src/threads/thrd_detach.cpp +++ b/libc/src/threads/thrd_detach.cpp @@ -14,11 +14,11 @@ namespace __llvm_libc { -static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread), - "Mismatch between thrd_t and internal Thread."); +static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread), + "Mismatch between thrd_t and internal Thread."); LLVM_LIBC_FUNCTION(int, thrd_detach, (thrd_t th)) { - auto *thread = reinterpret_cast *>(&th); + auto *thread = reinterpret_cast(&th); thread->detach(); return 0; } diff --git a/libc/src/threads/thrd_join.cpp b/libc/src/threads/thrd_join.cpp index fbdfa78..4c3ecac 100644 --- a/libc/src/threads/thrd_join.cpp +++ b/libc/src/threads/thrd_join.cpp @@ -14,11 +14,11 @@ namespace __llvm_libc { -static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread), - "Mismatch between thrd_t and internal Thread."); +static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread), + "Mismatch between thrd_t and internal Thread."); LLVM_LIBC_FUNCTION(int, thrd_join, (thrd_t * th, int *retval)) { - auto *thread = reinterpret_cast *>(th); + auto *thread = reinterpret_cast(th); int result = thread->join(retval); return result == 0 ? thrd_success : thrd_error; } -- 2.7.4