[libc-commits] [libc] 2ce09e6 - [libc] Add a linux Thread class in __support/threads.
Siva Chandra Reddy via libc-commits
libc-commits at lists.llvm.org
Thu Apr 7 09:13:35 PDT 2022
Author: Siva Chandra Reddy
Date: 2022-04-07T16:13:21Z
New Revision: 2ce09e680a7dc1201463ae74e199eac66ac52a8d
URL: https://github.com/llvm/llvm-project/commit/2ce09e680a7dc1201463ae74e199eac66ac52a8d
DIFF: https://github.com/llvm/llvm-project/commit/2ce09e680a7dc1201463ae74e199eac66ac52a8d.diff
LOG: [libc] Add a linux Thread class in __support/threads.
This change is essentially a mechanical change which moves the thread
creation and join implementations from src/threads/linux to
src/__support/threads/linux/thread.h. The idea being that, in future, a
pthread implementation can reuse the common thread implementations in
src/__support/threads.
Reviewed By: lntue
Differential Revision: https://reviews.llvm.org/D123287
Added:
libc/src/__support/CPP/error.h
libc/src/__support/threads/linux/futex_word.h
libc/src/__support/threads/linux/thread.h
libc/src/__support/threads/thread.h
libc/src/__support/threads/thread_attrib.h
libc/src/threads/thrd_create.cpp
libc/src/threads/thrd_join.cpp
Modified:
libc/include/llvm-libc-types/thrd_t.h
libc/src/__support/CPP/CMakeLists.txt
libc/src/__support/threads/CMakeLists.txt
libc/src/__support/threads/linux/CMakeLists.txt
libc/src/__support/threads/linux/mutex.h
libc/src/threads/CMakeLists.txt
libc/src/threads/linux/CMakeLists.txt
libc/src/threads/linux/CndVar.h
libc/src/threads/thrd_create.h
Removed:
libc/src/threads/linux/Thread.h
libc/src/threads/linux/thrd_create.cpp
libc/src/threads/linux/thrd_join.cpp
################################################################################
diff --git a/libc/include/llvm-libc-types/thrd_t.h b/libc/include/llvm-libc-types/thrd_t.h
index ae41ac846e563..5ec29adb92ec6 100644
--- a/libc/include/llvm-libc-types/thrd_t.h
+++ b/libc/include/llvm-libc-types/thrd_t.h
@@ -12,11 +12,14 @@
#include <llvm-libc-types/__futex_word.h>
typedef struct {
+ struct {
+ void *__stack;
+ unsigned long long __stack_size;
+ unsigned char __managed_stack;
+ int __retval;
+ int __tid;
+ } __attrib;
__futex_word __clear_tid;
- int __tid;
- void *__stack;
- int __stack_size;
- int __retval;
} thrd_t;
#endif // __LLVM_LIBC_TYPES_THRD_T_H__
diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt
index a4ffea3a28388..75b77406bc8e9 100644
--- a/libc/src/__support/CPP/CMakeLists.txt
+++ b/libc/src/__support/CPP/CMakeLists.txt
@@ -73,3 +73,9 @@ add_header_library(
HDRS
blockstore.h
)
+
+add_header_library(
+ error
+ HDRS
+ error.h
+)
diff --git a/libc/src/__support/CPP/error.h b/libc/src/__support/CPP/error.h
new file mode 100644
index 0000000000000..1e7a501dc7df4
--- /dev/null
+++ b/libc/src/__support/CPP/error.h
@@ -0,0 +1,51 @@
+//===-- A simple classes to manage error return vals ----------*- 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_CPP_ERROR_H
+#define LLVM_LIBC_SRC_SUPPORT_CPP_ERROR_H
+
+namespace __llvm_libc {
+namespace cpp {
+// Many C functions return an error val and/or the actual result of the
+// evaluation/operation performed by the function. This file defines a simple
+// convenience data structure to encapsulate the error and the actual val in
+// a single place.
+
+struct Error {
+ int error_code;
+};
+
+// This class is implemented in a simple fashion as the intention is it add
+// more generality as required. Currently, it only supports simple copyable
+// types for T.
+template <typename T> class ErrorOr {
+ bool is_error;
+
+ union {
+ T val;
+ Error error;
+ };
+
+public:
+ ErrorOr(const T &value) : is_error(false), val(value) {}
+
+ ErrorOr(const Error &error) : is_error(true), error(error) {}
+
+ operator bool() { return !is_error; }
+
+ operator T &() { return val; }
+
+ T &value() { return val; }
+
+ int error_code() { return is_error ? error.error_code : 0; }
+};
+
+} // namespace cpp
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SUPPORT_CPP_ERROR_H
diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt
index 6ded0da525a40..26c2387faad38 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -4,6 +4,12 @@ add_header_library(
mutex_common.h
)
+add_header_library(
+ thread_attrib
+ HDRS
+ thread_attrib.h
+)
+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
add_subdirectory(${LIBC_TARGET_OS})
endif()
@@ -17,3 +23,13 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.mutex)
.${LIBC_TARGET_OS}.mutex
)
endif()
+
+if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.thread)
+ add_header_library(
+ thread
+ HDRS
+ thread.h
+ DEPENDS
+ .${LIBC_TARGET_OS}.thread
+ )
+endif()
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 5dd154d53e897..558cf14a9da1e 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -1,10 +1,29 @@
+add_header_library(
+ futex_word_type
+ HDRS
+ futex_word.h
+)
+
add_header_library(
mutex
HDRS
mutex.h
DEPENDS
+ .futex_word_type
libc.include.sys_syscall
libc.src.__support.CPP.atomic
libc.src.__support.OSUtil.osutil
libc.src.__support.threads.mutex_common
)
+
+add_header_library(
+ thread
+ HDRS
+ thread.h
+ DEPENDS
+ .futex_word_type
+ libc.include.sys_syscall
+ libc.src.__support.CPP.atomic
+ libc.src.__support.CPP.error
+ libc.src.__support.threads.thread_attrib
+)
diff --git a/libc/src/__support/threads/linux/futex_word.h b/libc/src/__support/threads/linux/futex_word.h
new file mode 100644
index 0000000000000..b39723d985979
--- /dev/null
+++ b/libc/src/__support/threads/linux/futex_word.h
@@ -0,0 +1,24 @@
+//===--- Definition of a type for a futex word ------------------*- 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_FUTEX_WORD_H
+#define LLVM_LIBC_SRC_SUPPORT_THREADS_LINUX_FUTEX_WORD_H
+
+#include "src/__support/architectures.h"
+
+namespace __llvm_libc {
+
+#if defined(LLVM_LIBC_ARCH_X86_64) || defined(LLVM_LIBC_ARCH_AARCH64)
+using FutexWordType = unsigned int;
+#else
+#error "FutexWordType not defined for the target architecture."
+#endif
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SUPPORT_THREADS_LINUX_FUTEX_WORD_H
diff --git a/libc/src/__support/threads/linux/mutex.h b/libc/src/__support/threads/linux/mutex.h
index 5926fd96e45f1..0133b2069d845 100644
--- a/libc/src/__support/threads/linux/mutex.h
+++ b/libc/src/__support/threads/linux/mutex.h
@@ -11,6 +11,7 @@
#include "src/__support/CPP/atomic.h"
#include "src/__support/OSUtil/syscall.h" // For syscall functions.
+#include "src/__support/threads/linux/futex_word.h"
#include "src/__support/threads/mutex_common.h"
#include <linux/futex.h>
@@ -27,8 +28,6 @@ struct Mutex {
void *owner;
unsigned long long lock_count;
- using FutexWordType = unsigned int;
-
cpp::Atomic<FutexWordType> futex_word;
enum class LockState : FutexWordType {
diff --git a/libc/src/__support/threads/linux/thread.h b/libc/src/__support/threads/linux/thread.h
new file mode 100644
index 0000000000000..69bd2942328ff
--- /dev/null
+++ b/libc/src/__support/threads/linux/thread.h
@@ -0,0 +1,206 @@
+//===--- 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"
+
+#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 {
+
+template <typename ReturnType> struct Thread;
+
+#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);
+}
+
+template <typename ReturnType> using ThreadRunner = ReturnType(void *);
+
+// 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.
+template <typename ReturnType> struct alignas(16) StartArgs {
+ Thread<ReturnType> *thread;
+ ThreadRunner<ReturnType> *func;
+ 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.
+ 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. Similarly on aarch64, previous frame pointer and
+ // the value of the link register are pushed on to the stack. So, in
+ // both these cases, we have to step past two 64-bit values to get
+ // to the start args.
+ + sizeof(uintptr_t) * 2;
+}
+
+template <typename ReturnType> struct Thread {
+private:
+ ThreadAttributes<ReturnType> attrib;
+ cpp::Atomic<FutexWordType> clear_tid;
+
+public:
+ Thread() = default;
+
+ static void start_thread() __attribute__((noinline));
+
+ // Return 0 on success or an error value on failure.
+ int run(ThreadRunner<ReturnType> *f, void *arg, void *stack, size_t size) {
+ 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();
+ attrib.owned_stack = true;
+ } else {
+ attrib.owned_stack = false;
+ }
+ attrib.stack = stack;
+ attrib.stack_size = size;
+ clear_tid.val = CLEAR_TID_VALUE;
+
+ // 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.
+ uintptr_t adjusted_stack = reinterpret_cast<uintptr_t>(attrib.stack) +
+ attrib.stack_size -
+ sizeof(StartArgs<ReturnType>);
+ auto *start_args =
+ reinterpret_cast<StartArgs<ReturnType> *>(adjusted_stack);
+ start_args->thread = this;
+ start_args->func = f;
+ start_args->arg = arg;
+
+ // 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) {
+ 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() {
+ // 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);
+ }
+ return 0;
+ }
+
+ // Meaningful only after the thread finishes.
+ ReturnType return_value() { return attrib.retval; }
+};
+
+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;
+ thread->attrib.retval = start_args->func(start_args->arg);
+ if (thread->attrib.owned_stack)
+ free_stack(thread->attrib.stack, thread->attrib.stack_size);
+ __llvm_libc::syscall(SYS_exit, thread->attrib.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
new file mode 100644
index 0000000000000..2d38c37d060e0
--- /dev/null
+++ b/libc/src/__support/threads/thread.h
@@ -0,0 +1,47 @@
+//===--- A platform independent indirection for a 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_THREAD_H
+#define LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H
+
+#include <stddef.h>
+
+// 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 *f, void *arg, void *stack, size_t size);
+//
+// Returns:
+// 0 on success and an error value on failure.
+// Args:
+// 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.
+//
+// If callers pass a non-null |stack| value, then it will 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();
+// 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.
+//
+// 4. Has an operator== for comparison between two threads.
+#ifdef __unix__
+#include "linux/thread.h"
+#endif // __unix__
+
+#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
new file mode 100644
index 0000000000000..9aaf4477417c2
--- /dev/null
+++ b/libc/src/__support/threads/thread_attrib.h
@@ -0,0 +1,29 @@
+//===--- 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
+
+namespace __llvm_libc {
+
+// A data type to hold common thread attributes which have to be stored as
+// thread state. A platform thread implementation should store the attrib object
+// in its Thread data structure. Note that this is
diff erent from public
+// attribute types like pthread_attr which contain information which need not
+// be saved as part of a thread's state. For example, the stack guard size.
+template <typename ReturnType> struct ThreadAttributes {
+ void *stack; // Pointer to the thread stack
+ 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;
+};
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_ATTRIB_H
diff --git a/libc/src/threads/CMakeLists.txt b/libc/src/threads/CMakeLists.txt
index a02fc161ecc5c..bd63a53061f36 100644
--- a/libc/src/threads/CMakeLists.txt
+++ b/libc/src/threads/CMakeLists.txt
@@ -11,16 +11,28 @@ add_entrypoint_object(
add_entrypoint_object(
thrd_create
- ALIAS
+ SRCS
+ thrd_create.cpp
+ HDRS
+ thrd_create.h
DEPENDS
- .${LIBC_TARGET_OS}.thrd_create
+ libc.src.__support.threads.thread
+ libc.include.threads
+ COMPILE_OPTIONS
+ -O3
+ -fno-omit-frame-pointer # This allows us to sniff out the thread args from
+ # the new thread's stack reliably.
)
add_entrypoint_object(
thrd_join
- ALIAS
+ SRCS
+ thrd_join.cpp
+ HDRS
+ thrd_join.h
DEPENDS
- .${LIBC_TARGET_OS}.thrd_join
+ libc.include.threads
+ libc.src.__support.threads.thread
)
add_entrypoint_object(
diff --git a/libc/src/threads/linux/CMakeLists.txt b/libc/src/threads/linux/CMakeLists.txt
index 0abdf5d23363f..339c2b5dc3d5d 100644
--- a/libc/src/threads/linux/CMakeLists.txt
+++ b/libc/src/threads/linux/CMakeLists.txt
@@ -17,48 +17,13 @@ add_header_library(
HDRS
CndVar.h
Futex.h
- Thread.h
DEPENDS
libc.include.sys_syscall
libc.include.threads
libc.src.__support.CPP.atomic
libc.src.__support.OSUtil.osutil
libc.src.__support.threads.mutex
-)
-
-add_entrypoint_object(
- thrd_create
- SRCS
- thrd_create.cpp
- HDRS
- ../thrd_create.h
- DEPENDS
- .threads_utils
- libc.include.errno
- libc.include.sys_mman
- libc.include.sys_syscall
- libc.include.threads
- libc.src.__support.common
- libc.src.__support.OSUtil.osutil
- COMPILE_OPTIONS
- -O3
- -fno-omit-frame-pointer # This allows us to sniff out the thread args from
- # the new thread's stack reliably.
-)
-
-add_entrypoint_object(
- thrd_join
- SRCS
- thrd_join.cpp
- HDRS
- ../thrd_join.h
- DEPENDS
- .threads_utils
- libc.include.sys_syscall
- libc.include.threads
- libc.src.__support.CPP.atomic
- libc.src.__support.common
- libc.src.__support.OSUtil.osutil
+ libc.src.__support.threads.linux.futex_word_type
)
add_entrypoint_object(
diff --git a/libc/src/threads/linux/CndVar.h b/libc/src/threads/linux/CndVar.h
index 2b914d480c764..009cb9f06c3f3 100644
--- a/libc/src/threads/linux/CndVar.h
+++ b/libc/src/threads/linux/CndVar.h
@@ -13,6 +13,7 @@
#include "include/threads.h" // For values like thrd_success etc.
#include "src/__support/CPP/atomic.h"
#include "src/__support/OSUtil/syscall.h" // For syscall functions.
+#include "src/__support/threads/linux/futex_word.h"
#include "src/__support/threads/mutex.h"
#include <linux/futex.h> // For futex operations.
@@ -106,7 +107,7 @@ struct CndVar {
if (waitq_front == nullptr)
waitq_back = nullptr;
- qmtx.futex_word = Mutex::FutexWordType(Mutex::LockState::Free);
+ qmtx.futex_word = FutexWordType(Mutex::LockState::Free);
__llvm_libc::syscall(
SYS_futex, &qmtx.futex_word.val, FUTEX_WAKE_OP, 1, 1,
diff --git a/libc/src/threads/linux/Thread.h b/libc/src/threads/linux/Thread.h
deleted file mode 100644
index de58ae4c64c62..0000000000000
--- a/libc/src/threads/linux/Thread.h
+++ /dev/null
@@ -1,23 +0,0 @@
-//===-- Linux specific definitions for threads implementations. --*- 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_THREADS_LINUX_THREAD_UTILS_H
-#define LLVM_LIBC_SRC_THREADS_LINUX_THREAD_UTILS_H
-
-#include <stdint.h>
-
-namespace __llvm_libc {
-
-struct ThreadParams {
- static constexpr uintptr_t DEFAULT_STACK_SIZE = 1 << 16; // 64 KB
- static constexpr uint32_t CLEAR_TID_VALUE = 0xABCD1234;
-};
-
-} // namespace __llvm_libc
-
-#endif // LLVM_LIBC_SRC_THREADS_LINUX_THREAD_UTILS_H
diff --git a/libc/src/threads/linux/thrd_create.cpp b/libc/src/threads/linux/thrd_create.cpp
deleted file mode 100644
index 5f25ded38bd39..0000000000000
--- a/libc/src/threads/linux/thrd_create.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-//===-- Linux implementation of the thrd_create function ------------------===//
-//
-// 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 "Futex.h"
-
-#include "src/__support/OSUtil/syscall.h" // For syscall function.
-#include "src/__support/architectures.h"
-#include "src/__support/common.h"
-#include "src/threads/linux/Thread.h"
-#include "src/threads/thrd_create.h"
-
-#include <errno.h> // For E* error values.
-#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.
-#include <threads.h> // For thrd_* type definitions.
-
-#ifdef SYS_mmap2
-constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap2;
-#elif SYS_mmap
-constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap;
-#else
-#error "SYS_mmap or SYS_mmap2 not available on the target platform"
-#endif
-
-namespace __llvm_libc {
-
-// 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(16) StartArgs {
- thrd_t *thread;
- thrd_start_t func;
- 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.
- 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. Similarly on aarch64, previous frame pointer and
- // the value of the link register are pushed on to the stack. So, in
- // both these cases, we have to step past two 64-bit values to get
- // to the start args.
- + sizeof(uintptr_t) * 2;
-}
-
-static __attribute__((noinline)) void start_thread() {
- StartArgs *start_args = reinterpret_cast<StartArgs *>(get_start_args_addr());
- __llvm_libc::syscall(SYS_exit, start_args->thread->__retval =
- start_args->func(start_args->arg));
-}
-
-LLVM_LIBC_FUNCTION(int, thrd_create,
- (thrd_t * thread, thrd_start_t func, void *arg)) {
- unsigned clone_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 and futex
- // wake the joining thread.
- // TODO: Add the CLONE_SETTLS flag and setup the TLS area correctly when
- // making the clone syscall.
-
- // Allocate thread stack.
- long mmap_result =
- __llvm_libc::syscall(MMAP_SYSCALL_NUMBER,
- 0, // No special address
- ThreadParams::DEFAULT_STACK_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 - ThreadParams::DEFAULT_STACK_SIZE)) {
- return -mmap_result == ENOMEM ? thrd_nomem : thrd_error;
- }
- void *stack = reinterpret_cast<void *>(mmap_result);
-
- thread->__stack = stack;
- thread->__stack_size = ThreadParams::DEFAULT_STACK_SIZE;
- thread->__retval = -1;
- FutexWordType *clear_tid_address = &thread->__clear_tid.__word;
- *clear_tid_address = ThreadParams::CLEAR_TID_VALUE;
-
- // 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.
- uintptr_t adjusted_stack = reinterpret_cast<uintptr_t>(stack) +
- ThreadParams::DEFAULT_STACK_SIZE -
- sizeof(StartArgs);
- StartArgs *start_args = reinterpret_cast<StartArgs *>(adjusted_stack);
- start_args->thread = thread;
- start_args->func = func;
- start_args->arg = arg;
-
- // 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_flags, adjusted_stack,
- &thread->__tid, // The address where the child tid is written
- clear_tid_address, // 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_flags, adjusted_stack,
- &thread->__tid, // The address where the child tid is written
- 0, // Set TLS to null for now.
- clear_tid_address // The futex where the child thread status is signalled
- );
-#else
-#error "Unsupported architecture for the clone syscall."
-#endif
-
- if (clone_result == 0) {
- start_thread();
- } else if (clone_result < 0) {
- __llvm_libc::syscall(SYS_munmap, mmap_result,
- ThreadParams::DEFAULT_STACK_SIZE);
- int error_val = -clone_result;
- return error_val == ENOMEM ? thrd_nomem : thrd_error;
- }
-
- return thrd_success;
-}
-
-} // namespace __llvm_libc
diff --git a/libc/src/threads/linux/thrd_join.cpp b/libc/src/threads/linux/thrd_join.cpp
deleted file mode 100644
index 30275ff737545..0000000000000
--- a/libc/src/threads/linux/thrd_join.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-//===-- Linux implementation of the thrd_join function --------------------===//
-//
-// 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 "Futex.h"
-
-#include "src/__support/CPP/atomic.h"
-#include "src/__support/OSUtil/syscall.h" // For syscall function.
-#include "src/__support/common.h"
-#include "src/threads/linux/Thread.h"
-#include "src/threads/thrd_join.h"
-
-#include <linux/futex.h> // For futex operations.
-#include <sys/syscall.h> // For syscall numbers.
-#include <threads.h> // For thrd_* type definitions.
-
-namespace __llvm_libc {
-
-LLVM_LIBC_FUNCTION(int, thrd_join, (thrd_t * thread, int *retval)) {
- auto *clear_tid_address =
- reinterpret_cast<cpp::Atomic<FutexWordType> *>(&thread->__clear_tid);
-
- // 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_address->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_address->val, FUTEX_WAIT,
- ThreadParams::CLEAR_TID_VALUE, nullptr);
- }
-
- *retval = thread->__retval;
-
- if (__llvm_libc::syscall(SYS_munmap, reinterpret_cast<long>(thread->__stack),
- thread->__stack_size) == -1)
- return thrd_error;
-
- return thrd_success;
-}
-
-} // namespace __llvm_libc
diff --git a/libc/src/threads/thrd_create.cpp b/libc/src/threads/thrd_create.cpp
new file mode 100644
index 0000000000000..91b36a71f8239
--- /dev/null
+++ b/libc/src/threads/thrd_create.cpp
@@ -0,0 +1,33 @@
+//===-- Linux implementation of the thrd_create function ------------------===//
+//
+// 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/threads/thrd_create.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/thread.h"
+
+#include <errno.h>
+#include <threads.h> // For thrd_* type definitions.
+
+namespace __llvm_libc {
+
+static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread<int>),
+ "Mismatch between thrd_t and internal Thread<int>.");
+
+LLVM_LIBC_FUNCTION(int, thrd_create,
+ (thrd_t * th, thrd_start_t func, void *arg)) {
+ auto *thread = reinterpret_cast<__llvm_libc::Thread<int> *>(th);
+ int result = thread->run(func, arg, nullptr, 0);
+ if (result == 0)
+ return thrd_success;
+ else if (result == ENOMEM)
+ return thrd_nomem;
+ else
+ return thrd_error;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/threads/thrd_create.h b/libc/src/threads/thrd_create.h
index d2bb7dfad41c5..9ed19c48c0a4e 100644
--- a/libc/src/threads/thrd_create.h
+++ b/libc/src/threads/thrd_create.h
@@ -9,7 +9,7 @@
#ifndef LLVM_LIBC_SRC_THREADS_THRD_CREATE_H
#define LLVM_LIBC_SRC_THREADS_THRD_CREATE_H
-#include "include/threads.h"
+#include <threads.h>
namespace __llvm_libc {
diff --git a/libc/src/threads/thrd_join.cpp b/libc/src/threads/thrd_join.cpp
new file mode 100644
index 0000000000000..20096a7be1bf1
--- /dev/null
+++ b/libc/src/threads/thrd_join.cpp
@@ -0,0 +1,30 @@
+//===-- Linux implementation of the thrd_join function --------------------===//
+//
+// 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/threads/thrd_join.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/thread.h"
+
+#include <threads.h> // For thrd_* type definitions.
+
+namespace __llvm_libc {
+
+static_assert(sizeof(thrd_t) == sizeof(__llvm_libc::Thread<int>),
+ "Mismatch between thrd_t and internal Thread<int>.");
+
+LLVM_LIBC_FUNCTION(int, thrd_join, (thrd_t * th, int *retval)) {
+ auto *thread = reinterpret_cast<Thread<int> *>(th);
+ int result = thread->join();
+ if (result == 0) {
+ *retval = thread->return_value();
+ return thrd_success;
+ }
+ return thrd_error;
+}
+
+} // namespace __llvm_libc
More information about the libc-commits
mailing list