[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