forked from OSchip/llvm-project
[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.
This commit is contained in:
parent
2b3de29717
commit
f4580c6d5a
|
@ -27,8 +27,6 @@
|
|||
|
||||
namespace __llvm_libc {
|
||||
|
||||
template <typename ReturnType> 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 <typename ReturnType> 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 <typename ReturnType> struct alignas(STACK_ALIGNMENT) StartArgs {
|
||||
Thread<ReturnType> *thread;
|
||||
ThreadRunner<ReturnType> *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 <typename ReturnType> struct Thread {
|
||||
struct Thread {
|
||||
private:
|
||||
ThreadAttributes<ReturnType> *attrib;
|
||||
ThreadAttributes *attrib;
|
||||
cpp::Atomic<FutexWordType> *clear_tid;
|
||||
|
||||
public:
|
||||
Thread() = default;
|
||||
|
||||
static void start_thread() __attribute__((noinline));
|
||||
static void start_thread() __attribute__((noinline)) {
|
||||
auto *start_args = reinterpret_cast<StartArgs *>(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);
|
||||
}
|
||||
|
||||
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(ThreadRunner<ReturnType> *f, void *arg, void *stack, size_t size,
|
||||
bool detached = false) {
|
||||
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<uintptr_t>(stack) + size -
|
||||
sizeof(StartArgs<ReturnType>) -
|
||||
sizeof(ThreadAttributes<ReturnType>) -
|
||||
sizeof(StartArgs) - sizeof(ThreadAttributes) -
|
||||
sizeof(cpp::Atomic<FutexWordType>);
|
||||
adjusted_stack &= ~(uintptr_t(STACK_ALIGNMENT) - 1);
|
||||
|
||||
auto *start_args =
|
||||
reinterpret_cast<StartArgs<ReturnType> *>(adjusted_stack);
|
||||
auto *start_args = reinterpret_cast<StartArgs *>(adjusted_stack);
|
||||
start_args->thread = this;
|
||||
start_args->func = f;
|
||||
start_args->runner = runner;
|
||||
start_args->arg = arg;
|
||||
|
||||
attrib = reinterpret_cast<ThreadAttributes<ReturnType> *>(
|
||||
adjusted_stack + sizeof(StartArgs<ReturnType>));
|
||||
attrib = reinterpret_cast<ThreadAttributes *>(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<cpp::Atomic<FutexWordType> *>(
|
||||
adjusted_stack + sizeof(StartArgs<ReturnType>) +
|
||||
sizeof(ThreadAttributes<ReturnType>));
|
||||
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 <typename ReturnType>
|
||||
__attribute__((noinline)) void Thread<ReturnType>::start_thread() {
|
||||
auto *start_args =
|
||||
reinterpret_cast<StartArgs<ReturnType> *>(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
|
||||
|
|
|
@ -11,6 +11,19 @@
|
|||
|
||||
#include <stddef.h>
|
||||
|
||||
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<ReturnType> 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.
|
||||
|
|
|
@ -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 <typename ReturnType>
|
||||
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<uint32_t> 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
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
|
||||
namespace __llvm_libc {
|
||||
|
||||
static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread<int>),
|
||||
"Mismatch between pthread_t and internal Thread<int>.");
|
||||
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<void *> *>(th);
|
||||
auto *thread = reinterpret_cast<__llvm_libc::Thread *>(th);
|
||||
int result = thread->run(func, arg, nullptr, 0);
|
||||
if (result != 0 && result != EPERM)
|
||||
return EAGAIN;
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
|
||||
namespace __llvm_libc {
|
||||
|
||||
static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread<void *>),
|
||||
"Mismatch between pthread_t and internal Thread<void *>.");
|
||||
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<Thread<void *> *>(&th);
|
||||
auto *thread = reinterpret_cast<Thread *>(&th);
|
||||
thread->detach();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
|
||||
namespace __llvm_libc {
|
||||
|
||||
static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread<int>),
|
||||
"Mismatch between pthread_t and internal Thread<int>.");
|
||||
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<Thread<void *> *>(&th);
|
||||
auto *thread = reinterpret_cast<Thread *>(&th);
|
||||
int result = thread->join(retval);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
|
||||
namespace __llvm_libc {
|
||||
|
||||
static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread<int>),
|
||||
"Mismatch between thrd_t and internal Thread<int>.");
|
||||
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<int> *>(th);
|
||||
auto *thread = reinterpret_cast<__llvm_libc::Thread *>(th);
|
||||
int result = thread->run(func, arg, nullptr, 0);
|
||||
if (result == 0)
|
||||
return thrd_success;
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
|
||||
namespace __llvm_libc {
|
||||
|
||||
static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread<int>),
|
||||
"Mismatch between thrd_t and internal Thread<int>.");
|
||||
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<Thread<int> *>(&th);
|
||||
auto *thread = reinterpret_cast<Thread *>(&th);
|
||||
thread->detach();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
|
||||
namespace __llvm_libc {
|
||||
|
||||
static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread<int>),
|
||||
"Mismatch between thrd_t and internal Thread<int>.");
|
||||
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<Thread<int> *>(th);
|
||||
auto *thread = reinterpret_cast<Thread *>(th);
|
||||
int result = thread->join(retval);
|
||||
return result == 0 ? thrd_success : thrd_error;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue