[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