[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