[libc-commits] [libc] cfe1b90 - [libc] Moves __cxa_thread_atexit_impl into proper entrypoint (#229347)
via libc-commits
libc-commits at lists.llvm.org
Tue Oct 13 15:59:36 PDT 2026
Author: irhs0
Date: 2026-10-13T15:59:31-07:00
New Revision: cfe1b90e7cda8f2e801e23316c89e3800c5f363e
URL: https://github.com/llvm/llvm-project/commit/cfe1b90e7cda8f2e801e23316c89e3800c5f363e
DIFF: https://github.com/llvm/llvm-project/commit/cfe1b90e7cda8f2e801e23316c89e3800c5f363e.diff
LOG: [libc] Moves __cxa_thread_atexit_impl into proper entrypoint (#229347)
This PR resolves (#224664).
---------
Co-authored-by: Pavel Labath <pavel at labath.sk>
Added:
libc/src/stdlib/__cxa_thread_atexit_impl.cpp
libc/src/stdlib/__cxa_thread_atexit_impl.h
libc/test/integration/src/stdlib/atexit_thread_test.cpp
Modified:
libc/config/linux/aarch64/entrypoints.txt
libc/config/linux/riscv/entrypoints.txt
libc/config/linux/x86_64/entrypoints.txt
libc/src/__support/threads/thread.cpp
libc/src/__support/threads/thread.h
libc/src/stdlib/CMakeLists.txt
libc/test/integration/src/__support/threads/CMakeLists.txt
libc/test/integration/src/__support/threads/double_exit_test.cpp
libc/test/integration/src/__support/threads/main_exit_test.cpp
libc/test/integration/src/pthread/CMakeLists.txt
libc/test/integration/src/pthread/pthread_exit_test.cpp
libc/test/integration/src/stdlib/CMakeLists.txt
libc/test/integration/src/threads/CMakeLists.txt
libc/test/integration/src/threads/thrd_exit_test.cpp
Removed:
################################################################################
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index fa435af5365eb..8866925d22611 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1344,6 +1344,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.ungetc
# stdlib.h entrypoints
+ libc.src.stdlib.__cxa_thread_atexit_impl
libc.src.stdlib._Exit
libc.src.stdlib.abort
libc.src.stdlib.at_quick_exit
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 10322631603c5..1ab1e4007b900 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1525,6 +1525,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.ungetc
# stdlib.h entrypoints
+ libc.src.stdlib.__cxa_thread_atexit_impl
libc.src.stdlib._Exit
libc.src.stdlib.abort
libc.src.stdlib.at_quick_exit
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index b62b4807bc9c0..3cada69a99116 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1551,6 +1551,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.ungetc
# stdlib.h entrypoints
+ libc.src.stdlib.__cxa_thread_atexit_impl
libc.src.stdlib._Exit
libc.src.stdlib.abort
libc.src.stdlib.at_quick_exit
diff --git a/libc/src/__support/threads/thread.cpp b/libc/src/__support/threads/thread.cpp
index 950dea837d0f9..828014507fee5 100644
--- a/libc/src/__support/threads/thread.cpp
+++ b/libc/src/__support/threads/thread.cpp
@@ -139,17 +139,9 @@ class ThreadAtExitCallbackMgr {
static LIBC_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 *) {
+int add_thread_atexit_callback(AtExitCallback *callback, void *obj) {
return atexit_callback_mgr.add_callback(callback, obj);
}
-
namespace internal {
void call_atexit_callbacks() {
diff --git a/libc/src/__support/threads/thread.h b/libc/src/__support/threads/thread.h
index 56cca8e3c915c..afb18af39cd1c 100644
--- a/libc/src/__support/threads/thread.h
+++ b/libc/src/__support/threads/thread.h
@@ -177,6 +177,10 @@ void call_atexit_callbacks();
} // namespace internal
+using AtExitCallback = void(void *);
+
+int add_thread_atexit_callback(AtExitCallback *callback, void *obj);
+
LIBC_INLINE Thread current_thread() {
return Thread(get_current_thread_attrib());
}
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index 7b2667753d2c7..7cdb1a5bbfdfb 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -1,3 +1,14 @@
+add_entrypoint_object(
+ __cxa_thread_atexit_impl
+ SRCS
+ __cxa_thread_atexit_impl.cpp
+ HDRS
+ __cxa_thread_atexit_impl.h
+ DEPENDS
+ libc.src.__support.threads.thread
+ libc.hdr.types.pthread_t
+)
+
add_entrypoint_object(
atoi
SRCS
diff --git a/libc/src/stdlib/__cxa_thread_atexit_impl.cpp b/libc/src/stdlib/__cxa_thread_atexit_impl.cpp
new file mode 100644
index 0000000000000..366bc600cb222
--- /dev/null
+++ b/libc/src/stdlib/__cxa_thread_atexit_impl.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of __cxa_thread_atexit_impl
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/__cxa_thread_atexit_impl.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/thread.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// 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.
+LLVM_LIBC_FUNCTION(int, __cxa_thread_atexit_impl,
+ (AtExitCallback * callback, void *obj, void *)) {
+ return add_thread_atexit_callback(callback, obj);
+}
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/__cxa_thread_atexit_impl.h b/libc/src/stdlib/__cxa_thread_atexit_impl.h
new file mode 100644
index 0000000000000..62fda1bc8feb4
--- /dev/null
+++ b/libc/src/stdlib/__cxa_thread_atexit_impl.h
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Include header for implementation of __cxa_thread_atexit_impl
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___THREADS_CXA_THREAD_ATEXIT_IMPL_H
+#define LLVM_LIBC_SRC___THREADS_CXA_THREAD_ATEXIT_IMPL_H
+
+#include "src/__support/threads/thread.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int __cxa_thread_atexit_impl(AtExitCallback *callback, void *obj, void *);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_THREADS_CXA_THREAD_ATEXIT_IMPL_H
diff --git a/libc/test/integration/src/__support/threads/CMakeLists.txt b/libc/test/integration/src/__support/threads/CMakeLists.txt
index 2f64fd367689c..482c3f007fe34 100644
--- a/libc/test/integration/src/__support/threads/CMakeLists.txt
+++ b/libc/test/integration/src/__support/threads/CMakeLists.txt
@@ -33,7 +33,7 @@ add_integration_test(
SRCS
main_exit_test.cpp
DEPENDS
- libc.src.__support.threads.thread
+ libc.src.stdlib.__cxa_thread_atexit_impl
)
add_integration_test(
@@ -43,8 +43,8 @@ add_integration_test(
SRCS
double_exit_test.cpp
DEPENDS
- libc.src.__support.threads.thread
libc.src.stdlib.exit
+ libc.src.stdlib.__cxa_thread_atexit_impl
)
add_integration_test(
diff --git a/libc/test/integration/src/__support/threads/double_exit_test.cpp b/libc/test/integration/src/__support/threads/double_exit_test.cpp
index 86749d937c10c..fff2e4daa912e 100644
--- a/libc/test/integration/src/__support/threads/double_exit_test.cpp
+++ b/libc/test/integration/src/__support/threads/double_exit_test.cpp
@@ -6,14 +6,13 @@
//
//===----------------------------------------------------------------------===//
-#include "src/__support/threads/thread.h"
+#include "src/stdlib/__cxa_thread_atexit_impl.h"
#include "src/stdlib/exit.h"
#include "test/IntegrationTest/test.h"
extern "C" {
[[gnu::weak]]
void *__dso_handle = nullptr;
-int __cxa_thread_atexit_impl(void (*func)(void *), void *arg, void *dso);
}
int call_num = 0;
@@ -26,8 +25,9 @@ void check() {
}
TEST_MAIN() {
- __cxa_thread_atexit_impl([](void *) { LIBC_NAMESPACE::exit(0); }, nullptr,
- __dso_handle);
- __cxa_thread_atexit_impl([](void *) { ++call_num; }, nullptr, __dso_handle);
+ LIBC_NAMESPACE::__cxa_thread_atexit_impl(
+ [](void *) { LIBC_NAMESPACE::exit(0); }, nullptr, __dso_handle);
+ LIBC_NAMESPACE::__cxa_thread_atexit_impl([](void *) { ++call_num; }, nullptr,
+ __dso_handle);
LIBC_NAMESPACE::exit(1);
}
diff --git a/libc/test/integration/src/__support/threads/main_exit_test.cpp b/libc/test/integration/src/__support/threads/main_exit_test.cpp
index c90e4e569cfba..659257e609083 100644
--- a/libc/test/integration/src/__support/threads/main_exit_test.cpp
+++ b/libc/test/integration/src/__support/threads/main_exit_test.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "src/__support/threads/thread.h"
+#include "src/stdlib/__cxa_thread_atexit_impl.h"
#include "test/IntegrationTest/test.h"
bool called = false;
@@ -14,7 +14,6 @@ bool called = false;
extern "C" {
[[gnu::weak]]
void *__dso_handle = nullptr;
-int __cxa_thread_atexit_impl(void (*func)(void *), void *arg, void *dso);
}
[[gnu::destructor]]
@@ -24,7 +23,7 @@ void destructor() {
}
TEST_MAIN() {
- __cxa_thread_atexit_impl([](void *) { called = true; }, nullptr,
- __dso_handle);
+ LIBC_NAMESPACE::__cxa_thread_atexit_impl([](void *) { called = true; },
+ nullptr, __dso_handle);
return 0;
}
diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index f45a77be1aee9..aa99eb7f1cb39 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -217,6 +217,7 @@ add_integration_test(
libc.src.pthread.pthread_create
libc.src.pthread.pthread_exit
libc.src.pthread.pthread_join
+ libc.src.stdlib.__cxa_thread_atexit_impl
)
add_integration_test(
diff --git a/libc/test/integration/src/pthread/pthread_exit_test.cpp b/libc/test/integration/src/pthread/pthread_exit_test.cpp
index 48e6c81713a40..cba9530552a14 100644
--- a/libc/test/integration/src/pthread/pthread_exit_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_exit_test.cpp
@@ -9,6 +9,7 @@
#include "src/pthread/pthread_create.h"
#include "src/pthread/pthread_exit.h"
#include "src/pthread/pthread_join.h"
+#include "src/stdlib/__cxa_thread_atexit_impl.h"
#include "test/IntegrationTest/test.h"
#include <pthread.h>
@@ -55,11 +56,9 @@ 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);
+ return LIBC_NAMESPACE::__cxa_thread_atexit_impl(dtor, obj, nullptr);
}
}
diff --git a/libc/test/integration/src/stdlib/CMakeLists.txt b/libc/test/integration/src/stdlib/CMakeLists.txt
index 11b1e90fbbee8..f0b86ea6470af 100644
--- a/libc/test/integration/src/stdlib/CMakeLists.txt
+++ b/libc/test/integration/src/stdlib/CMakeLists.txt
@@ -110,4 +110,16 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
libc.src.unistd.read
libc.src.unistd.write
)
+ add_integration_test(
+ atexit_thread_test
+ SUITE
+ stdlib-integration-tests
+ SRCS
+ atexit_thread_test.cpp
+ DEPENDS
+ libc.src.pthread.pthread_create
+ libc.src.pthread.pthread_exit
+ libc.src.pthread.pthread_join
+ libc.src.stdlib.__cxa_thread_atexit_impl
+ )
endif()
diff --git a/libc/test/integration/src/stdlib/atexit_thread_test.cpp b/libc/test/integration/src/stdlib/atexit_thread_test.cpp
new file mode 100644
index 0000000000000..fc0ee9ba004e4
--- /dev/null
+++ b/libc/test/integration/src/stdlib/atexit_thread_test.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file consists of an integration test written to the functionality
+/// of __cxa_thread_atexit_impl() function while an exit of a pthread
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pthread/pthread_create.h"
+#include "src/pthread/pthread_exit.h"
+#include "src/pthread/pthread_join.h"
+#include "src/stdlib/__cxa_thread_atexit_impl.h"
+#include "test/IntegrationTest/test.h"
+
+#include "hdr/types/pthread_t.h"
+
+namespace {
+
+int dtor_order[3];
+int dtor_count = 0;
+
+struct Obj {
+ int id;
+ Obj(int i) : id(i) {}
+ ~Obj() { dtor_order[dtor_count++] = id; }
+};
+
+thread_local Obj tl_first(1);
+thread_local Obj tl_second(2);
+thread_local Obj tl_third(3);
+
+void touch_all() {
+ // touch is in definition order so the destruction order becomes well defined.
+ (void)tl_first.id;
+ (void)tl_second.id;
+ (void)tl_third.id;
+}
+
+void *return_func(void *) {
+ touch_all();
+ return nullptr;
+}
+
+void *exit_func(void *) {
+ touch_all();
+ LIBC_NAMESPACE::pthread_exit(nullptr);
+ return nullptr;
+}
+
+void run_thread(void *(*fn)(void *)) {
+ dtor_count = 0;
+
+ pthread_t th;
+ void *retval;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&th, nullptr, fn, nullptr), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_join(th, &retval), 0);
+
+ ASSERT_EQ(dtor_count, 3);
+ ASSERT_EQ(dtor_order[0], 3);
+ ASSERT_EQ(dtor_order[1], 2);
+ ASSERT_EQ(dtor_order[2], 1);
+}
+
+} // namespace
+
+extern "C" {
+// Integration tests are not linked to the C++ runtime, so provide a minimal
+// __cxa_thread_atexit for the compiler-emitted calls.
+int __cxa_thread_atexit(void (*dtor)(void *), void *obj, void *) {
+ return LIBC_NAMESPACE::__cxa_thread_atexit_impl(dtor, obj, nullptr);
+}
+}
+
+TEST_MAIN() {
+ run_thread(return_func);
+ run_thread(exit_func);
+ return 0;
+}
diff --git a/libc/test/integration/src/threads/CMakeLists.txt b/libc/test/integration/src/threads/CMakeLists.txt
index 3355a27a309cf..93b7666d6ac7c 100644
--- a/libc/test/integration/src/threads/CMakeLists.txt
+++ b/libc/test/integration/src/threads/CMakeLists.txt
@@ -60,6 +60,7 @@ add_integration_test(
libc.src.threads.thrd_create
libc.src.threads.thrd_exit
libc.src.threads.thrd_join
+ libc.src.stdlib.__cxa_thread_atexit_impl
)
add_integration_test(
diff --git a/libc/test/integration/src/threads/thrd_exit_test.cpp b/libc/test/integration/src/threads/thrd_exit_test.cpp
index 0939a7621f443..5e64af6a81d12 100644
--- a/libc/test/integration/src/threads/thrd_exit_test.cpp
+++ b/libc/test/integration/src/threads/thrd_exit_test.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "src/stdlib/__cxa_thread_atexit_impl.h"
#include "src/threads/thrd_create.h"
#include "src/threads/thrd_exit.h"
#include "src/threads/thrd_join.h"
@@ -53,11 +54,9 @@ 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);
+ return LIBC_NAMESPACE::__cxa_thread_atexit_impl(dtor, obj, nullptr);
}
}
More information about the libc-commits
mailing list