[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