[libc-commits] [libc] 0071a79 - [libc] Add implementation of pthread_exit and thrd_exit.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Tue Aug 9 23:29:09 PDT 2022


Author: Siva Chandra Reddy
Date: 2022-08-10T06:28:47Z
New Revision: 0071a79532e8d664b734956a431d8c8c942cc25e

URL: https://github.com/llvm/llvm-project/commit/0071a79532e8d664b734956a431d8c8c942cc25e
DIFF: https://github.com/llvm/llvm-project/commit/0071a79532e8d664b734956a431d8c8c942cc25e.diff

LOG: [libc] Add implementation of pthread_exit and thrd_exit.

Reviewed By: michaelrj

Differential Revision: https://reviews.llvm.org/D131451

Added: 
    libc/src/pthread/pthread_exit.cpp
    libc/src/pthread/pthread_exit.h
    libc/src/threads/thrd_exit.cpp
    libc/src/threads/thrd_exit.h
    libc/test/integration/src/pthread/pthread_exit_test.cpp
    libc/test/integration/src/threads/thrd_exit_test.cpp

Modified: 
    libc/config/linux/x86_64/entrypoints.txt
    libc/loader/linux/x86_64/start.cpp
    libc/spec/posix.td
    libc/spec/stdc.td
    libc/src/__support/threads/CMakeLists.txt
    libc/src/__support/threads/linux/thread.cpp
    libc/src/__support/threads/thread.cpp
    libc/src/__support/threads/thread.h
    libc/src/pthread/CMakeLists.txt
    libc/src/threads/CMakeLists.txt
    libc/test/integration/src/pthread/CMakeLists.txt
    libc/test/integration/src/threads/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 07e57d667e670..fcef386186da9 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -251,6 +251,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.pthread.pthread_create
     libc.src.pthread.pthread_detach
     libc.src.pthread.pthread_equal
+    libc.src.pthread.pthread_exit
     libc.src.pthread.pthread_getname_np
     libc.src.pthread.pthread_join
     libc.src.pthread.pthread_self
@@ -325,6 +326,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.threads.thrd_current
     libc.src.threads.thrd_detach
     libc.src.threads.thrd_equal
+    libc.src.threads.thrd_exit
     libc.src.threads.thrd_join
 
     # time.h entrypoints

diff  --git a/libc/loader/linux/x86_64/start.cpp b/libc/loader/linux/x86_64/start.cpp
index 389e621a2eaa8..3353588b1256b 100644
--- a/libc/loader/linux/x86_64/start.cpp
+++ b/libc/loader/linux/x86_64/start.cpp
@@ -205,6 +205,8 @@ extern "C" void _start() {
     __llvm_libc::syscall(SYS_exit, 1);
 
   __llvm_libc::self.attrib = &__llvm_libc::main_thread_attrib;
+  __llvm_libc::main_thread_attrib.atexit_callback_mgr =
+      __llvm_libc::internal::get_thread_atexit_callback_mgr();
 
   // We want the fini array callbacks to be run after other atexit
   // callbacks are run. So, we register them before running the init

diff  --git a/libc/spec/posix.td b/libc/spec/posix.td
index a6560147a7b3a..1a3177f50b810 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -474,6 +474,11 @@ def POSIX : StandardSpec<"POSIX"> {
           RetValSpec<IntType>,
           [ArgSpec<PThreadTType>]
       >,
+      FunctionSpec<
+          "pthread_exit",
+          RetValSpec<VoidType>,
+          [ArgSpec<VoidPtr>]
+      >,
       FunctionSpec<
           "pthread_self",
           RetValSpec<PThreadTType>,

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index f8385eb8c0ff0..beabc1bc72184 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -824,7 +824,12 @@ def StdC : StandardSpec<"stdc"> {
               "thrd_equal",
               RetValSpec<IntType>,
               [ArgSpec<ThrdTType>, ArgSpec<ThrdTType>]
-          >
+          >,
+          FunctionSpec<
+              "thrd_exit",
+              RetValSpec<VoidType>,
+              [ArgSpec<IntType>]
+          >,
       ]
   >;
 

diff  --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt
index 41d0c47eb5c57..9d4b0060c60f1 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -35,6 +35,8 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.thread)
     SRCS
       thread.cpp
     DEPENDS
+      .mutex
       .${LIBC_TARGET_OS}.thread
+      libc.src.__support.fixedvector
   )
 endif()

diff  --git a/libc/src/__support/threads/linux/thread.cpp b/libc/src/__support/threads/linux/thread.cpp
index b1b9634119ea4..c485276daf4d4 100644
--- a/libc/src/__support/threads/linux/thread.cpp
+++ b/libc/src/__support/threads/linux/thread.cpp
@@ -118,30 +118,19 @@ static void start_thread() {
   auto *start_args = reinterpret_cast<StartArgs *>(get_start_args_addr());
   auto *attrib = start_args->thread_attrib;
   self.attrib = attrib;
+  self.attrib->atexit_callback_mgr = internal::get_thread_atexit_callback_mgr();
 
-  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);
+    thread_exit(ThreadReturnValue(attrib->retval.posix_retval),
+                ThreadStyle::POSIX);
   } else {
     attrib->retval.stdc_retval =
         start_args->runner.stdc_runner(start_args->arg);
-    retval = long(attrib->retval.stdc_retval);
+    thread_exit(ThreadReturnValue(attrib->retval.stdc_retval),
+                ThreadStyle::STDC);
   }
-
-  uint32_t joinable_state = uint32_t(DetachState::JOINABLE);
-  if (!attrib->detach_state.compare_exchange_strong(
-          joinable_state, uint32_t(DetachState::EXITING))) {
-    // Thread is detached so cleanup the resources.
-    cleanup_thread_resources(attrib);
-
-    // Set the CLEAR_TID address to nullptr to prevent the kernel
-    // from signalling at a non-existent futex location.
-    __llvm_libc::syscall(SYS_set_tid_address, 0);
-  }
-
-  __llvm_libc::syscall(SYS_exit, retval);
 }
 
 int Thread::run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack,
@@ -375,4 +364,34 @@ int Thread::get_name(cpp::StringStream &name) const {
   return 0;
 }
 
+void thread_exit(ThreadReturnValue retval, ThreadStyle style) {
+  auto attrib = self.attrib;
+
+  // The very first thing we do is to call the thread's atexit callbacks.
+  // These callbacks could be the ones registered by the language runtimes,
+  // for example, the destructors of thread local objects. They can also
+  // be destructors of the TSS objects set using API like pthread_setspecific.
+  // NOTE: We cannot call the atexit callbacks as part of the 
+  // cleanup_thread_resources function as that function can be called from a
+  // 
diff erent thread. The destructors of thread local and TSS objects should
+  // be called by the thread which owns them.
+  internal::call_atexit_callbacks(attrib);
+
+  uint32_t joinable_state = uint32_t(DetachState::JOINABLE);
+  if (!attrib->detach_state.compare_exchange_strong(
+          joinable_state, uint32_t(DetachState::EXITING))) {
+    // Thread is detached so cleanup the resources.
+    cleanup_thread_resources(attrib);
+
+    // Set the CLEAR_TID address to nullptr to prevent the kernel
+    // from signalling at a non-existent futex location.
+    __llvm_libc::syscall(SYS_set_tid_address, 0);
+  }
+
+  if (style == ThreadStyle::POSIX)
+    __llvm_libc::syscall(SYS_exit, retval.posix_retval);
+  else
+    __llvm_libc::syscall(SYS_exit, retval.stdc_retval);
+}
+
 } // namespace __llvm_libc

diff  --git a/libc/src/__support/threads/thread.cpp b/libc/src/__support/threads/thread.cpp
index 6149f7724fca6..205be944cc17a 100644
--- a/libc/src/__support/threads/thread.cpp
+++ b/libc/src/__support/threads/thread.cpp
@@ -7,9 +7,75 @@
 //===----------------------------------------------------------------------===//
 
 #include "thread.h"
+#include "mutex.h"
+
+#include "src/__support/fixedvector.h"
 
 namespace __llvm_libc {
 
 thread_local Thread self;
 
+namespace {
+
+using AtExitCallback = void(void *);
+
+struct AtExitUnit {
+  AtExitCallback *callback = nullptr;
+  void *obj = nullptr;
+  constexpr AtExitUnit() = default;
+  constexpr AtExitUnit(AtExitCallback *cb, void *o) : callback(cb), obj(o) {}
+};
+
+} // anonymous namespace
+
+class ThreadAtExitCallbackMgr {
+  Mutex mtx;
+  // TODO: Use a BlockStore when compiled for production.
+  FixedVector<AtExitUnit, 1024> callback_list;
+
+public:
+  constexpr ThreadAtExitCallbackMgr() : mtx(false, false, false) {}
+
+  int add_callback(AtExitCallback *callback, void *obj) {
+    MutexLock lock(&mtx);
+    return callback_list.push_back({callback, obj});
+  }
+
+  void call() {
+    mtx.lock();
+    while (!callback_list.empty()) {
+      auto atexit_unit = callback_list.back();
+      callback_list.pop_back();
+      mtx.unlock();
+      atexit_unit.callback(atexit_unit.obj);
+      mtx.lock();
+    }
+  }
+};
+
+static thread_local ThreadAtExitCallbackMgr atexit_callback_mgr;
+
+// The function __cxa_thread_atexit is provided by C++ runtimes like libcxxabi.
+// It is used by thread local object runtime to register destructor calls. To
+// actually register destructor call with the threading library, it calls
+// __cxa_thread_atexit_impl, which is to be provided by the threading library.
+// The semantics are very similar to the __cxa_atexit function except for the
+// fact that the registered callback is thread specific.
+extern "C" int __cxa_thread_atexit_impl(AtExitCallback *callback, void *obj,
+                                        void *) {
+  return atexit_callback_mgr.add_callback(callback, obj);
+}
+
+namespace internal {
+
+ThreadAtExitCallbackMgr *get_thread_atexit_callback_mgr() {
+  return &atexit_callback_mgr;
+}
+
+void call_atexit_callbacks(ThreadAttributes *attrib) {
+  attrib->atexit_callback_mgr->call();
+}
+
+} // namespace internal
+
 } // namespace __llvm_libc

diff  --git a/libc/src/__support/threads/thread.h b/libc/src/__support/threads/thread.h
index 088b967ee4b0b..1b125dd65da50 100644
--- a/libc/src/__support/threads/thread.h
+++ b/libc/src/__support/threads/thread.h
@@ -31,6 +31,8 @@ union ThreadReturnValue {
   void *posix_retval;
   int stdc_retval;
   constexpr ThreadReturnValue() : posix_retval(nullptr) {}
+  constexpr ThreadReturnValue(int r) : stdc_retval(r) {}
+  constexpr ThreadReturnValue(void *r) : posix_retval(r) {}
 };
 
 #if (defined(LLVM_LIBC_ARCH_AARCH64) || defined(LLVM_LIBC_ARCH_X86_64))
@@ -56,6 +58,8 @@ enum class DetachType : int {
   CLEANUP = 2
 };
 
+class ThreadAtExitCallbackMgr;
+
 // 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
@@ -91,12 +95,14 @@ struct alignas(STACK_ALIGNMENT) ThreadAttributes {
   int tid;
   ThreadStyle style;
   ThreadReturnValue retval;
+  ThreadAtExitCallbackMgr *atexit_callback_mgr;
   void *platform_data;
 
   constexpr ThreadAttributes()
       : detach_state(uint32_t(DetachState::DETACHED)), stack(nullptr),
         stack_size(0), tls(0), tls_size(0), owned_stack(false), tid(-1),
-        style(ThreadStyle::POSIX), retval(), platform_data(nullptr) {}
+        style(ThreadStyle::POSIX), retval(), atexit_callback_mgr(nullptr),
+        platform_data(nullptr) {}
 };
 
 struct Thread {
@@ -177,6 +183,25 @@ struct Thread {
 
 extern thread_local Thread self;
 
+// Platforms should implement this function.
+void thread_exit(ThreadReturnValue retval, ThreadStyle style);
+
+namespace internal {
+// Internal namespace containing utilities which are to be used by platform
+// implementations of threads.
+
+// Return the current thread's atexit callback manager. After thread startup
+// but before running the thread function, platform implementations should
+// set the "atexit_callback_mgr" field of the thread's attributes to the value
+// returned by this function.
+ThreadAtExitCallbackMgr *get_thread_atexit_callback_mgr();
+
+// Call the currently registered thread specific atexit callbacks. Useful for
+// implementing the thread_exit function.
+void call_atexit_callbacks(ThreadAttributes *attrib);
+
+} // namespace internal
+
 } // namespace __llvm_libc
 
 #endif // LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H

diff  --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt
index 7d8a72197214e..a1d92e6cbf4e6 100644
--- a/libc/src/pthread/CMakeLists.txt
+++ b/libc/src/pthread/CMakeLists.txt
@@ -291,6 +291,17 @@ add_entrypoint_object(
     libc.src.__support.threads.thread
 )
 
+add_entrypoint_object(
+  pthread_exit
+  SRCS
+    pthread_exit.cpp
+  HDRS
+    pthread_exit.h
+  DEPENDS
+    libc.include.threads
+    libc.src.__support.threads.thread
+)
+
 add_entrypoint_object(
   pthread_self
   SRCS

diff  --git a/libc/src/pthread/pthread_exit.cpp b/libc/src/pthread/pthread_exit.cpp
new file mode 100644
index 0000000000000..d9866c6f48600
--- /dev/null
+++ b/libc/src/pthread/pthread_exit.cpp
@@ -0,0 +1,25 @@
+//===-- Implementation of the pthread_exit 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 "pthread_exit.h"
+
+#include "src/__support/common.h"
+#include "src/__support/threads/thread.h"
+
+#include <pthread.h> // For pthread_* type definitions.
+
+namespace __llvm_libc {
+
+static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread),
+              "Mismatch between pthread_t and internal Thread.");
+
+LLVM_LIBC_FUNCTION(void, pthread_exit, (void *retval)) {
+  thread_exit(ThreadReturnValue(retval), ThreadStyle::POSIX);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/pthread/pthread_exit.h b/libc/src/pthread/pthread_exit.h
new file mode 100644
index 0000000000000..dd22c57cbecca
--- /dev/null
+++ b/libc/src/pthread/pthread_exit.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for pthread_exit function ---------*- 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_PTHREAD_EXIT_H
+#define LLVM_LIBC_SRC_THREADS_PTHREAD_EXIT_H
+
+#include <pthread.h>
+
+namespace __llvm_libc {
+
+void pthread_exit(void *retval);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_THREADS_PTHREAD_EXIT_H

diff  --git a/libc/src/threads/CMakeLists.txt b/libc/src/threads/CMakeLists.txt
index 179d1e56205b8..15efef9221e07 100644
--- a/libc/src/threads/CMakeLists.txt
+++ b/libc/src/threads/CMakeLists.txt
@@ -68,6 +68,17 @@ add_entrypoint_object(
     libc.src.__support.threads.thread
 )
 
+add_entrypoint_object(
+  thrd_exit
+  SRCS
+    thrd_exit.cpp
+  HDRS
+    thrd_exit.h
+  DEPENDS
+    libc.include.threads
+    libc.src.__support.threads.thread
+)
+
 add_entrypoint_object(
   mtx_init
   SRCS

diff  --git a/libc/src/threads/thrd_exit.cpp b/libc/src/threads/thrd_exit.cpp
new file mode 100644
index 0000000000000..9935c23bba1eb
--- /dev/null
+++ b/libc/src/threads/thrd_exit.cpp
@@ -0,0 +1,24 @@
+//===-- Linux implementation of the thrd_exit 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_exit.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),
+              "Mismatch between thrd_t and internal Thread.");
+
+LLVM_LIBC_FUNCTION(void, thrd_exit, (int retval)) {
+  thread_exit(ThreadReturnValue(retval), ThreadStyle::STDC);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/threads/thrd_exit.h b/libc/src/threads/thrd_exit.h
new file mode 100644
index 0000000000000..feed4701254c6
--- /dev/null
+++ b/libc/src/threads/thrd_exit.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for thrd_exit function ------------*- 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_THRD_EXIT_H
+#define LLVM_LIBC_SRC_THREADS_THRD_EXIT_H
+
+#include "include/threads.h"
+
+namespace __llvm_libc {
+
+void thrd_exit(int retval);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_THREADS_THRD_EXIT_H

diff  --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index 09219227f3abc..a1a34beef444e 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -76,3 +76,18 @@ add_integration_test(
     libc.src.pthread.pthread_self
     libc.src.pthread.pthread_setname_np
 )
+
+add_integration_test(
+  pthread_exit_test
+  SUITE
+    libc-pthread-integration-tests
+  SRCS
+    pthread_exit_test.cpp
+  LOADER
+    libc.loader.linux.crt1
+  DEPENDS
+    libc.include.pthread
+    libc.src.pthread.pthread_create
+    libc.src.pthread.pthread_exit
+    libc.src.pthread.pthread_join
+)

diff  --git a/libc/test/integration/src/pthread/pthread_exit_test.cpp b/libc/test/integration/src/pthread/pthread_exit_test.cpp
new file mode 100644
index 0000000000000..655bfcd9aba96
--- /dev/null
+++ b/libc/test/integration/src/pthread/pthread_exit_test.cpp
@@ -0,0 +1,65 @@
+//===-- Tests for pthread_exit --------------------------------------------===//
+//
+// 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/pthread/pthread_create.h"
+#include "src/pthread/pthread_exit.h"
+#include "src/pthread/pthread_join.h"
+#include "utils/IntegrationTest/test.h"
+
+#include <pthread.h>
+
+bool dtor_called = false;
+
+class A {
+  int val;
+
+public:
+  A(int i) { val = i; }
+
+  void set(int i) { val = i; }
+
+  ~A() {
+    val = 0;
+    dtor_called = true;
+  }
+};
+
+thread_local A thread_local_a(123);
+
+void *func(void *) {
+  // Touch the thread local variable so that it gets initialized and a callback
+  // for its destructor gets registered with __cxa_thread_atexit.
+  thread_local_a.set(321);
+  __llvm_libc::pthread_exit(nullptr);
+  return nullptr;
+}
+
+TEST_MAIN() {
+  pthread_t th;
+  void *retval;
+
+  ASSERT_EQ(__llvm_libc::pthread_create(&th, nullptr, func, nullptr), 0);
+  ASSERT_EQ(__llvm_libc::pthread_join(th, &retval), 0);
+
+  ASSERT_TRUE(dtor_called);
+  __llvm_libc::pthread_exit(nullptr);
+  return 0;
+}
+
+extern "C" {
+
+using Destructor = void(void *);
+
+int __cxa_thread_atexit_impl(Destructor *, void *, void *);
+
+// We do not link integration tests to C++ runtime pieces like the libcxxabi.
+// So, we provide our own simple __cxa_thread_atexit implementation.
+int __cxa_thread_atexit(Destructor *dtor, void *obj, void *) {
+  return __cxa_thread_atexit_impl(dtor, obj, nullptr);
+}
+}

diff  --git a/libc/test/integration/src/threads/CMakeLists.txt b/libc/test/integration/src/threads/CMakeLists.txt
index 34dc47127005d..f3a70b61974fa 100644
--- a/libc/test/integration/src/threads/CMakeLists.txt
+++ b/libc/test/integration/src/threads/CMakeLists.txt
@@ -54,6 +54,21 @@ add_integration_test(
     libc.src.threads.thrd_join
 )
 
+add_integration_test(
+  thrd_exit_test
+  SUITE
+    libc-threads-integration-tests
+  SRCS
+    thrd_exit_test.cpp
+  LOADER
+    libc.loader.linux.crt1
+  DEPENDS
+    libc.include.threads
+    libc.src.threads.thrd_create
+    libc.src.threads.thrd_exit
+    libc.src.threads.thrd_join
+)
+
 add_integration_test(
   call_once_test
   SUITE

diff  --git a/libc/test/integration/src/threads/thrd_exit_test.cpp b/libc/test/integration/src/threads/thrd_exit_test.cpp
new file mode 100644
index 0000000000000..d5f7259b2e17a
--- /dev/null
+++ b/libc/test/integration/src/threads/thrd_exit_test.cpp
@@ -0,0 +1,63 @@
+//===-- Tests for thrd_exit -----------------------------------------------===//
+//
+// 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/threads/thrd_exit.h"
+#include "src/threads/thrd_join.h"
+#include "utils/IntegrationTest/test.h"
+
+#include <threads.h>
+
+bool dtor_called = false;
+
+class A {
+  int val;
+
+public:
+  A(int i) { val = i; }
+
+  void set(int i) { val = i; }
+
+  ~A() {
+    val = 0;
+    dtor_called = true;
+  }
+};
+
+thread_local A thread_local_a(123);
+
+int func(void *) {
+  thread_local_a.set(321);
+  __llvm_libc::thrd_exit(0);
+  return 0;
+}
+
+TEST_MAIN() {
+  thrd_t th;
+  int retval;
+
+  ASSERT_EQ(__llvm_libc::thrd_create(&th, func, nullptr), thrd_success);
+  ASSERT_EQ(__llvm_libc::thrd_join(&th, &retval), thrd_success);
+
+  ASSERT_TRUE(dtor_called);
+  __llvm_libc::thrd_exit(0);
+  return 0;
+}
+
+extern "C" {
+
+using Destructor = void(void *);
+
+int __cxa_thread_atexit_impl(Destructor *, void *, void *);
+
+// We do not link integration tests to C++ runtime pieces like the libcxxabi.
+// So, we provide our own simple __cxa_thread_atexit implementation.
+int __cxa_thread_atexit(Destructor *dtor, void *obj, void *) {
+  return __cxa_thread_atexit_impl(dtor, obj, nullptr);
+}
+}


        


More information about the libc-commits mailing list