[libc-commits] [libc] fe80174 - [libc][NFC] Make the support thread library an object library.
Siva Chandra Reddy via libc-commits
libc-commits at lists.llvm.org
Mon Jun 27 01:48:12 PDT 2022
Author: Siva Chandra Reddy
Date: 2022-06-27T08:47:05Z
New Revision: fe8017476c7788806859a0fbac0c82be38874b47
URL: https://github.com/llvm/llvm-project/commit/fe8017476c7788806859a0fbac0c82be38874b47
DIFF: https://github.com/llvm/llvm-project/commit/fe8017476c7788806859a0fbac0c82be38874b47.diff
LOG: [libc][NFC] Make the support thread library an object library.
It was previously a header library. Making it an object library will
allow us to declare thread local variables which can used to setup a
thread's self object.
Added:
libc/src/__support/threads/linux/thread.cpp
Modified:
libc/src/__support/threads/CMakeLists.txt
libc/src/__support/threads/thread.h
Removed:
libc/src/__support/threads/linux/thread.h
libc/src/__support/threads/thread_attrib.h
################################################################################
diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt
index 5dce96cfc2ab2..04f028799b323 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -28,11 +28,17 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.mutex)
endif()
if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.thread)
- add_header_library(
+ add_object_library(
thread
HDRS
thread.h
+ SRCS
+ ${LIBC_TARGET_OS}/thread.cpp
DEPENDS
- .${LIBC_TARGET_OS}.thread
+ .thread_attrib
+ COMPILE_OPTIONS
+ -O3
+ -fno-omit-frame-pointer # This allows us to sniff out the thread args from
+ # the new thread's stack reliably.
)
endif()
diff --git a/libc/src/__support/threads/linux/thread.cpp b/libc/src/__support/threads/linux/thread.cpp
new file mode 100644
index 0000000000000..fd0a3f69a7bd5
--- /dev/null
+++ b/libc/src/__support/threads/linux/thread.cpp
@@ -0,0 +1,260 @@
+//===--- Implementation of a Linux thread class -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/threads/thread.h"
+#include "src/__support/CPP/atomic.h"
+#include "src/__support/CPP/error.h"
+#include "src/__support/OSUtil/syscall.h" // For syscall functions.
+#include "src/__support/threads/linux/futex_word.h" // For FutexWordType
+
+#ifdef LLVM_LIBC_ARCH_AARCH64
+#include <arm_acle.h>
+#endif
+
+#include <linux/futex.h>
+#include <linux/sched.h> // For CLONE_* flags.
+#include <stdint.h>
+#include <sys/mman.h> // For PROT_* and MAP_* definitions.
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+#ifdef SYS_mmap2
+static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap2;
+#elif SYS_mmap
+static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap;
+#else
+#error "SYS_mmap or SYS_mmap2 not available on the target platform"
+#endif
+
+static constexpr size_t DEFAULT_STACK_SIZE = (1 << 16); // 64KB
+static constexpr uint32_t CLEAR_TID_VALUE = 0xABCD1234;
+static constexpr unsigned CLONE_SYSCALL_FLAGS =
+ CLONE_VM // Share the memory space with the parent.
+ | CLONE_FS // Share the file system with the parent.
+ | CLONE_FILES // Share the files with the parent.
+ | CLONE_SIGHAND // Share the signal handlers with the parent.
+ | CLONE_THREAD // Same thread group as the parent.
+ | CLONE_SYSVSEM // Share a single list of System V semaphore adjustment
+ // values
+ | CLONE_PARENT_SETTID // Set child thread ID in |ptid| of the parent.
+ | CLONE_CHILD_CLEARTID; // Let the kernel clear the tid address
+ // wake the joining thread.
+// TODO: Add the CLONE_SETTLS flag and setup the TLS area correctly
+// when making the clone syscall.
+
+static inline cpp::ErrorOr<void *> alloc_stack(size_t size) {
+ long mmap_result =
+ __llvm_libc::syscall(MMAP_SYSCALL_NUMBER,
+ 0, // No special address
+ size,
+ PROT_READ | PROT_WRITE, // Read and write stack
+ MAP_ANONYMOUS | MAP_PRIVATE, // Process private
+ -1, // Not backed by any file
+ 0 // No offset
+ );
+ if (mmap_result < 0 && (uintptr_t(mmap_result) >= UINTPTR_MAX - size))
+ return cpp::Error{int(-mmap_result)};
+ return reinterpret_cast<void *>(mmap_result);
+}
+
+static inline void free_stack(void *stack, size_t size) {
+ __llvm_libc::syscall(SYS_munmap, stack, size);
+}
+
+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
diff erent architecture in future requires higher alignment, then we
+// can add a platform specific alignment spec.
+struct alignas(STACK_ALIGNMENT) StartArgs {
+ Thread *thread;
+ ThreadRunner runner;
+ void *arg;
+};
+
+__attribute__((always_inline)) inline uintptr_t get_start_args_addr() {
+// NOTE: For __builtin_frame_address to work reliably across compilers,
+// architectures and various optimization levels, the TU including this file
+// should be compiled with -fno-omit-frame-pointer.
+#ifdef LLVM_LIBC_ARCH_X86_64
+ return reinterpret_cast<uintptr_t>(__builtin_frame_address(0))
+ // The x86_64 call instruction pushes resume address on to the stack.
+ // Next, The x86_64 SysV ABI requires that the frame pointer be pushed
+ // on to the stack. So, we have to step past two 64-bit values to get
+ // to the start args.
+ + sizeof(uintptr_t) * 2;
+#elif defined(LLVM_LIBC_ARCH_AARCH64)
+ // The frame pointer after cloning the new thread in the Thread::run method
+ // is set to the stack pointer where start args are stored. So, we fetch
+ // from there.
+ return reinterpret_cast<uintptr_t>(__builtin_frame_address(1));
+#endif
+}
+
+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 Thread::run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack,
+ size_t size, bool detached) {
+ bool owned_stack = false;
+ if (stack == nullptr) {
+ if (size == 0)
+ size = DEFAULT_STACK_SIZE;
+ auto alloc = alloc_stack(size);
+ if (!alloc)
+ return alloc.error_code();
+ else
+ stack = alloc.value();
+ owned_stack = true;
+ }
+
+ // When the new thread is spawned by the kernel, the new thread gets the
+ // stack we pass to the clone syscall. However, this stack is empty and does
+ // not have any local vars present in this function. Hence, one cannot
+ // pass arguments to the thread start function, or use any local vars from
+ // here. So, we pack them into the new stack from where the thread can sniff
+ // them out.
+ //
+ // 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) - sizeof(ThreadAttributes) -
+ sizeof(cpp::Atomic<FutexWordType>);
+ adjusted_stack &= ~(uintptr_t(STACK_ALIGNMENT) - 1);
+
+ auto *start_args = reinterpret_cast<StartArgs *>(adjusted_stack);
+ start_args->thread = this;
+ start_args->runner = runner;
+ start_args->arg = arg;
+
+ attrib =
+ reinterpret_cast<ThreadAttributes *>(adjusted_stack + sizeof(StartArgs));
+ attrib->style = style;
+ attrib->detach_state =
+ uint32_t(detached ? DetachState::DETACHED : DetachState::JOINABLE);
+ attrib->stack = stack;
+ attrib->stack_size = size;
+ attrib->owned_stack = owned_stack;
+
+ auto clear_tid = reinterpret_cast<cpp::Atomic<FutexWordType> *>(
+ adjusted_stack + sizeof(StartArgs) + sizeof(ThreadAttributes));
+ clear_tid->val = CLEAR_TID_VALUE;
+ platform_data = clear_tid;
+
+ // The clone syscall takes arguments in an architecture specific order.
+ // Also, we want the result of the syscall to be in a register as the child
+ // thread gets a completely
diff erent stack after it is created. The stack
+ // variables from this function will not be availalbe to the child thread.
+#ifdef LLVM_LIBC_ARCH_X86_64
+ long register clone_result asm("rax");
+ clone_result = __llvm_libc::syscall(
+ SYS_clone, CLONE_SYSCALL_FLAGS, adjusted_stack,
+ &attrib->tid, // The address where the child tid is written
+ &clear_tid->val, // The futex where the child thread status is signalled
+ 0 // Set TLS to null for now.
+ );
+#elif defined(LLVM_LIBC_ARCH_AARCH64)
+ long register clone_result asm("x0");
+ clone_result = __llvm_libc::syscall(
+ SYS_clone, CLONE_SYSCALL_FLAGS, adjusted_stack,
+ &attrib->tid, // The address where the child tid is written
+ 0, // Set TLS to null for now.
+ &clear_tid->val // The futex where the child thread status is signalled
+ );
+#else
+#error "Unsupported architecture for the clone syscall."
+#endif
+
+ if (clone_result == 0) {
+#ifdef LLVM_LIBC_ARCH_AARCH64
+ // We set the frame pointer to be the same as the "sp" so that start args
+ // can be sniffed out from start_thread.
+ __arm_wsr64("x29", __arm_rsr64("sp"));
+#endif
+ start_thread();
+ } else if (clone_result < 0) {
+ if (attrib->owned_stack)
+ free_stack(attrib->stack, attrib->stack_size);
+ return -clone_result;
+ }
+
+ return 0;
+}
+
+int Thread::join(ThreadReturnValue &retval) {
+ wait();
+
+ 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);
+
+ return 0;
+}
+
+int Thread::detach() {
+ uint32_t joinable_state = uint32_t(DetachState::JOINABLE);
+ if (attrib->detach_state.compare_exchange_strong(
+ joinable_state, uint32_t(DetachState::DETACHED))) {
+ return int(DetachType::SIMPLE);
+ }
+
+ // If the thread was already detached, then the detach method should not
+ // be called at all. If the thread is exiting, then we wait for it to exit
+ // and free up resources.
+ wait();
+
+ if (attrib->owned_stack)
+ free_stack(attrib->stack, attrib->stack_size);
+ return int(DetachType::CLEANUP);
+}
+
+void Thread::wait() {
+ // The kernel should set the value at the clear tid address to zero.
+ // If not, it is a spurious wake and we should continue to wait on
+ // the futex.
+ auto *clear_tid =
+ reinterpret_cast<cpp::Atomic<FutexWordType> *>(platform_data);
+ while (clear_tid->load() != 0) {
+ // We cannot do a FUTEX_WAIT_PRIVATE here as the kernel does a
+ // FUTEX_WAKE and not a FUTEX_WAKE_PRIVATE.
+ __llvm_libc::syscall(SYS_futex, &clear_tid->val, FUTEX_WAIT,
+ CLEAR_TID_VALUE, nullptr);
+ }
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/__support/threads/linux/thread.h b/libc/src/__support/threads/linux/thread.h
deleted file mode 100644
index 365f0fc55979b..0000000000000
--- a/libc/src/__support/threads/linux/thread.h
+++ /dev/null
@@ -1,319 +0,0 @@
-//===--- Implementation of a Linux thread class -----------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_SRC_SUPPORT_THREADS_LINUX_THREAD_H
-#define LLVM_LIBC_SRC_SUPPORT_THREADS_LINUX_THREAD_H
-
-#include "src/__support/CPP/atomic.h"
-#include "src/__support/CPP/error.h"
-#include "src/__support/OSUtil/syscall.h" // For syscall functions.
-#include "src/__support/threads/linux/futex_word.h" // For FutexWordType
-#include "src/__support/threads/thread_attrib.h"
-
-#ifdef LLVM_LIBC_ARCH_AARCH64
-#include <arm_acle.h>
-#endif
-
-#include <linux/futex.h>
-#include <linux/sched.h> // For CLONE_* flags.
-#include <stdint.h>
-#include <sys/mman.h> // For PROT_* and MAP_* definitions.
-#include <sys/syscall.h> // For syscall numbers.
-
-namespace __llvm_libc {
-
-#ifdef SYS_mmap2
-static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap2;
-#elif SYS_mmap
-static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap;
-#else
-#error "SYS_mmap or SYS_mmap2 not available on the target platform"
-#endif
-
-static constexpr size_t DEFAULT_STACK_SIZE = (1 << 16); // 64KB
-static constexpr uint32_t CLEAR_TID_VALUE = 0xABCD1234;
-static constexpr unsigned CLONE_SYSCALL_FLAGS =
- CLONE_VM // Share the memory space with the parent.
- | CLONE_FS // Share the file system with the parent.
- | CLONE_FILES // Share the files with the parent.
- | CLONE_SIGHAND // Share the signal handlers with the parent.
- | CLONE_THREAD // Same thread group as the parent.
- | CLONE_SYSVSEM // Share a single list of System V semaphore adjustment
- // values
- | CLONE_PARENT_SETTID // Set child thread ID in |ptid| of the parent.
- | CLONE_CHILD_CLEARTID; // Let the kernel clear the tid address
- // wake the joining thread.
-// TODO: Add the CLONE_SETTLS flag and setup the TLS area correctly
-// when making the clone syscall.
-
-static inline cpp::ErrorOr<void *> alloc_stack(size_t size) {
- long mmap_result =
- __llvm_libc::syscall(MMAP_SYSCALL_NUMBER,
- 0, // No special address
- size,
- PROT_READ | PROT_WRITE, // Read and write stack
- MAP_ANONYMOUS | MAP_PRIVATE, // Process private
- -1, // Not backed by any file
- 0 // No offset
- );
- if (mmap_result < 0 && (uintptr_t(mmap_result) >= UINTPTR_MAX - size))
- return cpp::Error{int(-mmap_result)};
- return reinterpret_cast<void *>(mmap_result);
-}
-
-static inline void free_stack(void *stack, size_t size) {
- __llvm_libc::syscall(SYS_munmap, stack, size);
-}
-
-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
diff erent architecture in future requires higher alignment, then we
-// can add a platform specific alignment spec.
-struct alignas(STACK_ALIGNMENT) StartArgs {
- Thread *thread;
- ThreadRunner runner;
- void *arg;
-};
-
-__attribute__((always_inline)) inline uintptr_t get_start_args_addr() {
- // NOTE: For __builtin_frame_address to work reliably across compilers,
- // architectures and various optimization levels, the TU including this file
- // should be compiled with -fno-omit-frame-pointer.
-#ifdef LLVM_LIBC_ARCH_X86_64
- return reinterpret_cast<uintptr_t>(__builtin_frame_address(0))
- // The x86_64 call instruction pushes resume address on to the stack.
- // Next, The x86_64 SysV ABI requires that the frame pointer be pushed
- // on to the stack. So, we have to step past two 64-bit values to get
- // to the start args.
- + sizeof(uintptr_t) * 2;
-#elif defined(LLVM_LIBC_ARCH_AARCH64)
- // The frame pointer after cloning the new thread in the Thread::run method
- // is set to the stack pointer where start args are stored. So, we fetch
- // from there.
- return reinterpret_cast<uintptr_t>(__builtin_frame_address(1));
-#endif
-}
-
-struct Thread {
-private:
- ThreadAttributes *attrib;
- cpp::Atomic<FutexWordType> *clear_tid;
-
-public:
- Thread() = default;
-
- 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(ThreadStyle style, ThreadRunner runner, void *arg, void *stack,
- size_t size, bool detached) {
- bool owned_stack = false;
- if (stack == nullptr) {
- if (size == 0)
- size = DEFAULT_STACK_SIZE;
- auto alloc = alloc_stack(size);
- if (!alloc)
- return alloc.error_code();
- else
- stack = alloc.value();
- owned_stack = true;
- }
-
- // When the new thread is spawned by the kernel, the new thread gets the
- // stack we pass to the clone syscall. However, this stack is empty and does
- // not have any local vars present in this function. Hence, one cannot
- // pass arguments to the thread start function, or use any local vars from
- // here. So, we pack them into the new stack from where the thread can sniff
- // them out.
- //
- // 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) - sizeof(ThreadAttributes) -
- sizeof(cpp::Atomic<FutexWordType>);
- adjusted_stack &= ~(uintptr_t(STACK_ALIGNMENT) - 1);
-
- auto *start_args = reinterpret_cast<StartArgs *>(adjusted_stack);
- start_args->thread = this;
- start_args->runner = runner;
- start_args->arg = arg;
-
- attrib = reinterpret_cast<ThreadAttributes *>(adjusted_stack +
- sizeof(StartArgs));
- attrib->style = style;
- attrib->detach_state =
- uint32_t(detached ? DetachState::DETACHED : DetachState::JOINABLE);
- attrib->stack = stack;
- attrib->stack_size = size;
- attrib->owned_stack = owned_stack;
-
- clear_tid = reinterpret_cast<cpp::Atomic<FutexWordType> *>(
- adjusted_stack + sizeof(StartArgs) + sizeof(ThreadAttributes));
- clear_tid->val = CLEAR_TID_VALUE;
-
- // The clone syscall takes arguments in an architecture specific order.
- // Also, we want the result of the syscall to be in a register as the child
- // thread gets a completely
diff erent stack after it is created. The stack
- // variables from this function will not be availalbe to the child thread.
-#ifdef LLVM_LIBC_ARCH_X86_64
- long register clone_result asm("rax");
- clone_result = __llvm_libc::syscall(
- SYS_clone, CLONE_SYSCALL_FLAGS, adjusted_stack,
- &attrib->tid, // The address where the child tid is written
- &clear_tid->val, // The futex where the child thread status is signalled
- 0 // Set TLS to null for now.
- );
-#elif defined(LLVM_LIBC_ARCH_AARCH64)
- long register clone_result asm("x0");
- clone_result = __llvm_libc::syscall(
- SYS_clone, CLONE_SYSCALL_FLAGS, adjusted_stack,
- &attrib->tid, // The address where the child tid is written
- 0, // Set TLS to null for now.
- &clear_tid->val // The futex where the child thread status is signalled
- );
-#else
-#error "Unsupported architecture for the clone syscall."
-#endif
-
- if (clone_result == 0) {
-#ifdef LLVM_LIBC_ARCH_AARCH64
- // We set the frame pointer to be the same as the "sp" so that start args
- // can be sniffed out from start_thread.
- __arm_wsr64("x29", __arm_rsr64("sp"));
-#endif
- start_thread();
- } else if (clone_result < 0) {
- if (attrib->owned_stack)
- free_stack(attrib->stack, attrib->stack_size);
- return -clone_result;
- }
-
- return 0;
- }
-
- 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();
-
- 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);
-
- return 0;
- }
-
- // Detach a joinable thread.
- //
- // This method does not have error return value. However, the type of detach
- // is returned to help with testing.
- int detach() {
- uint32_t joinable_state = uint32_t(DetachState::JOINABLE);
- if (attrib->detach_state.compare_exchange_strong(
- joinable_state, uint32_t(DetachState::DETACHED))) {
- return int(DetachType::SIMPLE);
- }
-
- // If the thread was already detached, then the detach method should not
- // be called at all. If the thread is exiting, then we wait for it to exit
- // and free up resources.
- wait();
-
- if (attrib->owned_stack)
- free_stack(attrib->stack, attrib->stack_size);
- return int(DetachType::CLEANUP);
- }
-
- // Wait for the thread to finish. This method can only be called
- // if:
- // 1. A detached thread is guaranteed to be running.
- // 2. A joinable thread has not been detached or joined. As long as it has
- // not been detached or joined, wait can be called multiple times.
- //
- // Also, only one thread can wait and expect to get woken up when the thread
- // finishes.
- //
- // NOTE: This function is to be used for testing only. There is no standard
- // which requires exposing it via a public API.
- void wait() {
- // The kernel should set the value at the clear tid address to zero.
- // If not, it is a spurious wake and we should continue to wait on
- // the futex.
- while (clear_tid->load() != 0) {
- // We cannot do a FUTEX_WAIT_PRIVATE here as the kernel does a
- // FUTEX_WAKE and not a FUTEX_WAKE_PRIVATE.
- __llvm_libc::syscall(SYS_futex, &clear_tid->val, FUTEX_WAIT,
- CLEAR_TID_VALUE, nullptr);
- }
- }
-};
-
-} // 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 368df2bda9860..36a191cbc046d 100644
--- a/libc/src/__support/threads/thread.h
+++ b/libc/src/__support/threads/thread.h
@@ -9,7 +9,13 @@
#ifndef LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H
#define LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H
-#include <stddef.h>
+#include "src/__support/CPP/atomic.h"
+#include "src/__support/architectures.h"
+
+#include <stddef.h> // For size_t
+#include <stdint.h>
+
+namespace __llvm_libc {
using ThreadRunnerPosix = void *(void *);
using ThreadRunnerStdc = int(void *);
@@ -24,40 +30,132 @@ union ThreadReturnValue {
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:
-//
-// 1. Has a defaulted default constructor (not a default constructor).
-//
-// 2. Has a "run" method with the following signature:
-//
-// 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 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:
-// 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.
+#if (defined(LLVM_LIBC_ARCH_AARCH64) || defined(LLVM_LIBC_ARCH_X86_64))
+constexpr unsigned int STACK_ALIGNMENT = 16;
+#endif
+// TODO: Provide stack alignment requirements for other architectures.
+
+enum class DetachState : uint32_t {
+ JOINABLE = 0x11,
+ EXITING = 0x22,
+ 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
+ // and returned.
+ SIMPLE = 1,
+
+ // Indicates that the detach operation performed thread cleanup.
+ CLEANUP = 2
+};
+
+// A data type to hold common thread attributes which have to be stored as
+// thread state. Note that this is
diff erent from public attribute types like
+// pthread_attr_t which might contain information which need not be saved as
+// part of a thread's state. For example, the stack guard size.
//
-// 4. Has an operator== for comparison between two threads.
-#ifdef __unix__
-#include "linux/thread.h"
-#endif // __unix__
+// Thread attributes are typically stored on the stack. So, we align as required
+// for the target architecture.
+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
+ // variable with the following state transitions:
+ // 1. The a thread is created in a detached state, then user code should never
+ // call a detach or join function. Calling either of them can lead to
+ // undefined behavior.
+ // The value of |detach_state| is expected to be DetachState::DETACHED for
+ // its lifetime.
+ // 2. If a thread is created in a joinable state, |detach_state| will start
+ // with the value DetachState::JOINABLE. Another thread can detach this
+ // thread before it exits. The state transitions will as follows:
+ // (a) If the detach method sees the state as JOINABLE, then it will
+ // compare exchange to a state of DETACHED. The thread will clean
+ // itself up after it finishes.
+ // (b) If the detach method does not see JOINABLE in (a), then it will
+ // conclude that the thread is EXITING and will wait until the thread
+ // exits. It will clean up the thread resources once the thread
+ // 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
+ int tid;
+ ThreadStyle style;
+ ThreadReturnValue retval;
+};
+
+struct Thread {
+ ThreadAttributes *attrib;
+ void *platform_data;
+
+ Thread() = default;
+
+ 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);
+ }
+
+ 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;
+ }
+
+ // Platform should implement the functions below.
+
+ // Return 0 on success or an error value on failure.
+ int run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack,
+ size_t stack_size, bool detached);
+
+ // Return 0 on success or an error value on failure.
+ int join(ThreadReturnValue &retval);
+
+ // Detach a joinable thread.
+ //
+ // This method does not have error return value. However, the type of detach
+ // is returned to help with testing.
+ int detach();
+
+ // Wait for the thread to finish. This method can only be called
+ // if:
+ // 1. A detached thread is guaranteed to be running.
+ // 2. A joinable thread has not been detached or joined. As long as it has
+ // not been detached or joined, wait can be called multiple times.
+ //
+ // Also, only one thread can wait and expect to get woken up when the thread
+ // finishes.
+ //
+ // NOTE: This function is to be used for testing only. There is no standard
+ // which requires exposing it via a public API.
+ void wait();
+};
+
+} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H
diff --git a/libc/src/__support/threads/thread_attrib.h b/libc/src/__support/threads/thread_attrib.h
deleted file mode 100644
index c4deabd4c5898..0000000000000
--- a/libc/src/__support/threads/thread_attrib.h
+++ /dev/null
@@ -1,79 +0,0 @@
-//===--- A data type for thread attributes ----------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_ATTRIB_H
-#define LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_ATTRIB_H
-
-#include "src/__support/CPP/atomic.h"
-#include "src/__support/architectures.h"
-
-#include <stdint.h>
-
-namespace __llvm_libc {
-
-#if (defined(LLVM_LIBC_ARCH_AARCH64) || defined(LLVM_LIBC_ARCH_X86_64))
-constexpr unsigned int STACK_ALIGNMENT = 16;
-#endif
-// TODO: Provide stack alignment requirements for other architectures.
-
-enum class DetachState : uint32_t {
- JOINABLE = 0x11,
- EXITING = 0x22,
- 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
- // and returned.
- SIMPLE = 1,
-
- // Indicates that the detach operation performed thread cleanup.
- CLEANUP = 2
-};
-
-// A data type to hold common thread attributes which have to be stored as
-// thread state. Note that this is
diff erent from public attribute types like
-// pthread_attr_t which might contain information which need not be saved as
-// part of a thread's state. For example, the stack guard size.
-//
-// Thread attributes are typically stored on the stack. So, we align as required
-// for the target architecture.
-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
- // variable with the following state transitions:
- // 1. The a thread is created in a detached state, then user code should never
- // call a detach or join function. Calling either of them can lead to
- // undefined behavior.
- // The value of |detach_state| is expected to be DetachState::DETACHED for
- // its lifetime.
- // 2. If a thread is created in a joinable state, |detach_state| will start
- // with the value DetachState::JOINABLE. Another thread can detach this
- // thread before it exits. The state transitions will as follows:
- // (a) If the detach method sees the state as JOINABLE, then it will
- // compare exchange to a state of DETACHED. The thread will clean
- // itself up after it finishes.
- // (b) If the detach method does not see JOINABLE in (a), then it will
- // conclude that the thread is EXITING and will wait until the thread
- // exits. It will clean up the thread resources once the thread
- // 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
- int tid;
- ThreadStyle style;
- ThreadReturnValue retval;
-};
-} // namespace __llvm_libc
-
-#endif // LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_ATTRIB_H
More information about the libc-commits
mailing list