[libc-commits] [libc] [llvm] [libc] Moves __cxa_thread_atexit_impl into proper entrypoint (PR #229347)

via libc-commits libc-commits at lists.llvm.org
Fri Oct 9 08:56:52 PDT 2026


https://github.com/shri-acha updated https://github.com/llvm/llvm-project/pull/229347

>From 289b2bd0f907beb3a00aa85698c91116e50097ea Mon Sep 17 00:00:00 2001
From: Shriharsh Acharya <happy.irhs at gmail.com>
Date: Tue, 6 Oct 2026 14:46:39 +0545
Subject: [PATCH 01/10] feat: introduce __cxa_thread_atexit_impl into as a libc
 entry point

---
 CMakeFiles/Progress/count.txt                 |  1 +
 libc/config/linux/x86_64/entrypoints.txt      |  1 +
 libc/src/__support/threads/thread.cpp         |  8 ++---
 libc/src/__support/threads/thread.h           |  8 +++--
 libc/src/threads/CMakeLists.txt               | 10 ++++++
 libc/src/threads/__cxa_thread_atexit_impl.cpp | 33 +++++++++++++++++++
 libc/src/threads/__cxa_thread_atexit_impl.h   | 22 +++++++++++++
 7 files changed, 76 insertions(+), 7 deletions(-)
 create mode 100644 CMakeFiles/Progress/count.txt
 create mode 100644 libc/src/threads/__cxa_thread_atexit_impl.cpp
 create mode 100644 libc/src/threads/__cxa_thread_atexit_impl.h

diff --git a/CMakeFiles/Progress/count.txt b/CMakeFiles/Progress/count.txt
new file mode 100644
index 00000000000000..38b10c1b2badd8
--- /dev/null
+++ b/CMakeFiles/Progress/count.txt
@@ -0,0 +1 @@
+68
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 7928a35a6284a5..48326462a1d99f 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1592,6 +1592,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.search.twalk_r
 
     # threads.h entrypoints
+    libc.src.threads.__cxa_thread_atexit_impl
     libc.src.threads.call_once
     libc.src.threads.cnd_broadcast
     libc.src.threads.cnd_destroy
diff --git a/libc/src/__support/threads/thread.cpp b/libc/src/__support/threads/thread.cpp
index 950dea837d0f97..9644c50c9793a5 100644
--- a/libc/src/__support/threads/thread.cpp
+++ b/libc/src/__support/threads/thread.cpp
@@ -139,17 +139,15 @@ class ThreadAtExitCallbackMgr {
 
 static LIBC_THREAD_LOCAL ThreadAtExitCallbackMgr atexit_callback_mgr;
 
+int add_thread_atexit_callback(AtExitCallback *callback, void *obj) {
+  return atexit_callback_mgr.add_callback(callback, obj);
+}
 // 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 {
 
 void call_atexit_callbacks() {
diff --git a/libc/src/__support/threads/thread.h b/libc/src/__support/threads/thread.h
index 56cca8e3c915cb..746e1dcda13a7f 100644
--- a/libc/src/__support/threads/thread.h
+++ b/libc/src/__support/threads/thread.h
@@ -175,11 +175,15 @@ namespace internal {
 // implementing the thread_exit function.
 void call_atexit_callbacks();
 
-} // namespace internal
+}//internal namespace
+
+using AtExitCallback = void(void *);
+
+int add_thread_atexit_callback(AtExitCallback *callback, void *obj);
 
 LIBC_INLINE Thread current_thread() {
   return Thread(get_current_thread_attrib());
-}
+  }
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/threads/CMakeLists.txt b/libc/src/threads/CMakeLists.txt
index 56247204c30f90..90ddbaadd5190e 100644
--- a/libc/src/threads/CMakeLists.txt
+++ b/libc/src/threads/CMakeLists.txt
@@ -179,6 +179,16 @@ add_entrypoint_object(
     libc.src.__support.threads.mutex
 )
 
+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
+)
+
 add_entrypoint_object(
   cnd_init
   ALIAS
diff --git a/libc/src/threads/__cxa_thread_atexit_impl.cpp b/libc/src/threads/__cxa_thread_atexit_impl.cpp
new file mode 100644
index 00000000000000..67b387ab76fdb3
--- /dev/null
+++ b/libc/src/threads/__cxa_thread_atexit_impl.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/threads/__cxa_thread_atexit_impl.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/threads/thread.h" 
+
+#include <threads.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/threads/__cxa_thread_atexit_impl.h b/libc/src/threads/__cxa_thread_atexit_impl.h
new file mode 100644
index 00000000000000..729498b409a82e
--- /dev/null
+++ b/libc/src/threads/__cxa_thread_atexit_impl.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for __cxa_thread_atexit_impl 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_CXA_THREAD_ATEXIT_IMPL_H
+#define LLVM_LIBC_SRC___THREADS_CXA_THREAD_ATEXIT_IMPL_H
+
+#include "src/__support/macros/config.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
+

>From e560c23de195facd3afe12b67872f84a24e3a63e Mon Sep 17 00:00:00 2001
From: Shriharsh Acharya <happy.irhs at gmail.com>
Date: Wed, 7 Oct 2026 08:55:37 +0545
Subject: [PATCH 02/10] fix: moved out the implementation and headers from
 threads to stdlib

---
 libc/config/linux/aarch64/entrypoints.txt             |  1 +
 libc/config/linux/riscv/entrypoints.txt               |  1 +
 libc/config/linux/x86_64/entrypoints.txt              |  2 +-
 libc/src/__support/threads/thread.cpp                 |  6 ------
 libc/src/stdlib/CMakeLists.txt                        | 10 ++++++++++
 .../{threads => stdlib}/__cxa_thread_atexit_impl.cpp  | 11 ++++-------
 .../{threads => stdlib}/__cxa_thread_atexit_impl.h    | 11 ++++-------
 libc/src/threads/CMakeLists.txt                       | 10 ----------
 8 files changed, 21 insertions(+), 31 deletions(-)
 rename libc/src/{threads => stdlib}/__cxa_thread_atexit_impl.cpp (81%)
 rename libc/src/{threads => stdlib}/__cxa_thread_atexit_impl.h (58%)

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index c7ded2fade05d9..b31ba2dc55d68c 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -223,6 +223,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdbit.stdc_trailing_zeros_us
 
     # stdlib.h entrypoints
+    libc.src.stdlib.__cxa_thread_atexit_impl
     libc.src.stdlib.abs
     libc.src.stdlib.atof
     libc.src.stdlib.atoi
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 296985eeff937c..8743969bf97537 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1507,6 +1507,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 48326462a1d99f..fc9ff112d118d2 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1533,6 +1533,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
@@ -1592,7 +1593,6 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.search.twalk_r
 
     # threads.h entrypoints
-    libc.src.threads.__cxa_thread_atexit_impl
     libc.src.threads.call_once
     libc.src.threads.cnd_broadcast
     libc.src.threads.cnd_destroy
diff --git a/libc/src/__support/threads/thread.cpp b/libc/src/__support/threads/thread.cpp
index 9644c50c9793a5..828014507fee5f 100644
--- a/libc/src/__support/threads/thread.cpp
+++ b/libc/src/__support/threads/thread.cpp
@@ -142,12 +142,6 @@ static LIBC_THREAD_LOCAL ThreadAtExitCallbackMgr atexit_callback_mgr;
 int add_thread_atexit_callback(AtExitCallback *callback, void *obj) {
   return atexit_callback_mgr.add_callback(callback, obj);
 }
-// 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.
 namespace internal {
 
 void call_atexit_callbacks() {
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index faa31c62d1d8e8..9e16bdfffc9de7 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -1,3 +1,13 @@
+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
+)
+
 add_entrypoint_object(
   atoi
   SRCS
diff --git a/libc/src/threads/__cxa_thread_atexit_impl.cpp b/libc/src/stdlib/__cxa_thread_atexit_impl.cpp
similarity index 81%
rename from libc/src/threads/__cxa_thread_atexit_impl.cpp
rename to libc/src/stdlib/__cxa_thread_atexit_impl.cpp
index 67b387ab76fdb3..040b369e2effff 100644
--- a/libc/src/threads/__cxa_thread_atexit_impl.cpp
+++ b/libc/src/stdlib/__cxa_thread_atexit_impl.cpp
@@ -11,12 +11,9 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#include "src/threads/__cxa_thread_atexit_impl.h"
-#include "src/__support/common.h"
-#include "src/__support/macros/config.h"
-#include "src/__support/threads/thread.h" 
+#include "src/stdlib/__cxa_thread_atexit_impl.h"
 
-#include <threads.h>
+#include "src/__support/common.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -27,7 +24,7 @@ namespace LIBC_NAMESPACE_DECL {
 // 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 *)) {
+                   (AtExitCallback * callback, void* obj, void*)) {
   return add_thread_atexit_callback(callback, obj);
 }
-} // namespace LIBC_NAMESPACE_DECL
+}  // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/threads/__cxa_thread_atexit_impl.h b/libc/src/stdlib/__cxa_thread_atexit_impl.h
similarity index 58%
rename from libc/src/threads/__cxa_thread_atexit_impl.h
rename to libc/src/stdlib/__cxa_thread_atexit_impl.h
index 729498b409a82e..32e30d714a08bb 100644
--- a/libc/src/threads/__cxa_thread_atexit_impl.h
+++ b/libc/src/stdlib/__cxa_thread_atexit_impl.h
@@ -1,4 +1,4 @@
-//===-- Implementation header for __cxa_thread_atexit_impl 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.
@@ -9,14 +9,11 @@
 #ifndef LLVM_LIBC_SRC___THREADS_CXA_THREAD_ATEXIT_IMPL_H
 #define LLVM_LIBC_SRC___THREADS_CXA_THREAD_ATEXIT_IMPL_H
 
-#include "src/__support/macros/config.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
+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/src/threads/CMakeLists.txt b/libc/src/threads/CMakeLists.txt
index 90ddbaadd5190e..56247204c30f90 100644
--- a/libc/src/threads/CMakeLists.txt
+++ b/libc/src/threads/CMakeLists.txt
@@ -179,16 +179,6 @@ add_entrypoint_object(
     libc.src.__support.threads.mutex
 )
 
-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
-)
-
 add_entrypoint_object(
   cnd_init
   ALIAS

>From ae7c889d69c64709079710a31c2a066a8d8a0776 Mon Sep 17 00:00:00 2001
From: Shriharsh Acharya <happy.irhs at gmail.com>
Date: Wed, 7 Oct 2026 12:06:43 +0545
Subject: [PATCH 03/10] fix: clang-format

---
 CMakeFiles/Progress/count.txt                | 1 -
 libc/src/__support/threads/thread.h          | 4 ++--
 libc/src/stdlib/__cxa_thread_atexit_impl.cpp | 4 ++--
 libc/src/stdlib/__cxa_thread_atexit_impl.h   | 6 +++---
 4 files changed, 7 insertions(+), 8 deletions(-)
 delete mode 100644 CMakeFiles/Progress/count.txt

diff --git a/CMakeFiles/Progress/count.txt b/CMakeFiles/Progress/count.txt
deleted file mode 100644
index 38b10c1b2badd8..00000000000000
--- a/CMakeFiles/Progress/count.txt
+++ /dev/null
@@ -1 +0,0 @@
-68
diff --git a/libc/src/__support/threads/thread.h b/libc/src/__support/threads/thread.h
index 746e1dcda13a7f..afb18af39cd1c2 100644
--- a/libc/src/__support/threads/thread.h
+++ b/libc/src/__support/threads/thread.h
@@ -175,7 +175,7 @@ namespace internal {
 // implementing the thread_exit function.
 void call_atexit_callbacks();
 
-}//internal namespace
+} // namespace internal
 
 using AtExitCallback = void(void *);
 
@@ -183,7 +183,7 @@ int add_thread_atexit_callback(AtExitCallback *callback, void *obj);
 
 LIBC_INLINE Thread current_thread() {
   return Thread(get_current_thread_attrib());
-  }
+}
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/stdlib/__cxa_thread_atexit_impl.cpp b/libc/src/stdlib/__cxa_thread_atexit_impl.cpp
index 040b369e2effff..c38a6516a6ff09 100644
--- a/libc/src/stdlib/__cxa_thread_atexit_impl.cpp
+++ b/libc/src/stdlib/__cxa_thread_atexit_impl.cpp
@@ -24,7 +24,7 @@ namespace LIBC_NAMESPACE_DECL {
 // 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*)) {
+                   (AtExitCallback * callback, void *obj, void *)) {
   return add_thread_atexit_callback(callback, obj);
 }
-}  // namespace LIBC_NAMESPACE_DECL
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/__cxa_thread_atexit_impl.h b/libc/src/stdlib/__cxa_thread_atexit_impl.h
index 32e30d714a08bb..0ee509d63f45c6 100644
--- a/libc/src/stdlib/__cxa_thread_atexit_impl.h
+++ b/libc/src/stdlib/__cxa_thread_atexit_impl.h
@@ -13,7 +13,7 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-int __cxa_thread_atexit_impl(AtExitCallback* callback, void* obj, void*);
-}  // 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
+#endif // LLVM_LIBC_SRC_THREADS_CXA_THREAD_ATEXIT_IMPL_H

>From 27b9cfc401992f8b36afd9dcfac31492350d142e Mon Sep 17 00:00:00 2001
From: Shriharsh Acharya <happy.irhs at gmail.com>
Date: Wed, 7 Oct 2026 14:51:01 +0545
Subject: [PATCH 04/10] fix: update tests to fix linker issues

---
 libc/test/integration/src/__support/threads/CMakeLists.txt | 4 ++--
 .../integration/src/__support/threads/double_exit_test.cpp | 7 +++----
 .../integration/src/__support/threads/main_exit_test.cpp   | 5 ++---
 libc/test/integration/src/pthread/CMakeLists.txt           | 1 +
 libc/test/integration/src/pthread/pthread_exit_test.cpp    | 1 +
 5 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/libc/test/integration/src/__support/threads/CMakeLists.txt b/libc/test/integration/src/__support/threads/CMakeLists.txt
index 2f64fd367689c6..482c3f007fe340 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 86749d937c10c0..fbd75e4c940942 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/exit.h"
+#include "src/stdlib/__cxa_thread_atexit_impl.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,8 @@ void check() {
 }
 
 TEST_MAIN() {
-  __cxa_thread_atexit_impl([](void *) { LIBC_NAMESPACE::exit(0); }, nullptr,
+  LIBC_NAMESPACE::__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 *) { ++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 c90e4e569cfba4..238f0204040dea 100644
--- a/libc/test/integration/src/__support/threads/main_exit_test.cpp
+++ b/libc/test/integration/src/__support/threads/main_exit_test.cpp
@@ -6,15 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "src/__support/threads/thread.h"
 #include "test/IntegrationTest/test.h"
+#include "src/stdlib/__cxa_thread_atexit_impl.h"
 
 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,
+	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 f45a77be1aee91..aa99eb7f1cb396 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 48e6c81713a403..1fbf9492ea08ea 100644
--- a/libc/test/integration/src/pthread/pthread_exit_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_exit_test.cpp
@@ -10,6 +10,7 @@
 #include "src/pthread/pthread_exit.h"
 #include "src/pthread/pthread_join.h"
 #include "test/IntegrationTest/test.h"
+#include "src/stdlib/__cxa_thread_atexit_impl.h"
 
 #include <pthread.h>
 

>From eb732e6d63817fa0261dfe68b556991012d6f900 Mon Sep 17 00:00:00 2001
From: Shriharsh Acharya <happy.irhs at gmail.com>
Date: Wed, 7 Oct 2026 15:05:36 +0545
Subject: [PATCH 05/10] fix: clang-format

---
 .../src/__support/threads/double_exit_test.cpp           | 9 +++++----
 .../integration/src/__support/threads/main_exit_test.cpp | 6 +++---
 2 files changed, 8 insertions(+), 7 deletions(-)

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 fbd75e4c940942..fff2e4daa912eb 100644
--- a/libc/test/integration/src/__support/threads/double_exit_test.cpp
+++ b/libc/test/integration/src/__support/threads/double_exit_test.cpp
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "src/stdlib/exit.h"
 #include "src/stdlib/__cxa_thread_atexit_impl.h"
+#include "src/stdlib/exit.h"
 #include "test/IntegrationTest/test.h"
 
 extern "C" {
@@ -25,8 +25,9 @@ void check() {
 }
 
 TEST_MAIN() {
-  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::__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 238f0204040dea..659257e6090833 100644
--- a/libc/test/integration/src/__support/threads/main_exit_test.cpp
+++ b/libc/test/integration/src/__support/threads/main_exit_test.cpp
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "test/IntegrationTest/test.h"
 #include "src/stdlib/__cxa_thread_atexit_impl.h"
+#include "test/IntegrationTest/test.h"
 
 bool called = false;
 
@@ -23,7 +23,7 @@ void destructor() {
 }
 
 TEST_MAIN() {
-	LIBC_NAMESPACE::__cxa_thread_atexit_impl([](void *) { called = true; }, nullptr,
-                           __dso_handle);
+  LIBC_NAMESPACE::__cxa_thread_atexit_impl([](void *) { called = true; },
+                                           nullptr, __dso_handle);
   return 0;
 }

>From 8fbde9ca59d0fdcc8e65a9157ec7f4d3f3f769aa Mon Sep 17 00:00:00 2001
From: Shriharsh Acharya <happy.irhs at gmail.com>
Date: Wed, 7 Oct 2026 15:17:57 +0545
Subject: [PATCH 06/10] fix: formatting and replace __cxa_thread_atexit_impl

---
 libc/test/integration/src/pthread/pthread_exit_test.cpp | 6 ++----
 libc/test/integration/src/threads/thrd_exit_test.cpp    | 5 ++---
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/libc/test/integration/src/pthread/pthread_exit_test.cpp b/libc/test/integration/src/pthread/pthread_exit_test.cpp
index 1fbf9492ea08ea..cba9530552a14b 100644
--- a/libc/test/integration/src/pthread/pthread_exit_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_exit_test.cpp
@@ -9,8 +9,8 @@
 #include "src/pthread/pthread_create.h"
 #include "src/pthread/pthread_exit.h"
 #include "src/pthread/pthread_join.h"
-#include "test/IntegrationTest/test.h"
 #include "src/stdlib/__cxa_thread_atexit_impl.h"
+#include "test/IntegrationTest/test.h"
 
 #include <pthread.h>
 
@@ -56,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/threads/thrd_exit_test.cpp b/libc/test/integration/src/threads/thrd_exit_test.cpp
index 0939a7621f443c..5e64af6a81d123 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);
 }
 }

>From d96907f5fe11906e265c555f86d4e5de25e11fe6 Mon Sep 17 00:00:00 2001
From: Shriharsh Acharya <happy.irhs at gmail.com>
Date: Wed, 7 Oct 2026 15:42:47 +0545
Subject: [PATCH 07/10] fix: cmake configuration issue on aarch64 moved
 __cxa_thread_atexit_impl into LLVM_FULL_BUILD

---
 libc/config/linux/aarch64/entrypoints.txt        | 2 +-
 libc/test/integration/src/threads/CMakeLists.txt | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index b31ba2dc55d68c..4196a711c8d939 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -223,7 +223,6 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdbit.stdc_trailing_zeros_us
 
     # stdlib.h entrypoints
-    libc.src.stdlib.__cxa_thread_atexit_impl
     libc.src.stdlib.abs
     libc.src.stdlib.atof
     libc.src.stdlib.atoi
@@ -1327,6 +1326,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/test/integration/src/threads/CMakeLists.txt b/libc/test/integration/src/threads/CMakeLists.txt
index 3355a27a309cfd..93b7666d6ac7c7 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(

>From 918b11b0cd8e5333c252c77d1093a2205e1482af Mon Sep 17 00:00:00 2001
From: irhs0 <happy.irhs at gmail.com>
Date: Fri, 9 Oct 2026 16:18:10 +0545
Subject: [PATCH 08/10] Update libc/src/stdlib/__cxa_thread_atexit_impl.h

Co-authored-by: Pavel Labath <pavel at labath.sk>
---
 libc/src/stdlib/__cxa_thread_atexit_impl.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libc/src/stdlib/__cxa_thread_atexit_impl.h b/libc/src/stdlib/__cxa_thread_atexit_impl.h
index 0ee509d63f45c6..e8965facee3e02 100644
--- a/libc/src/stdlib/__cxa_thread_atexit_impl.h
+++ b/libc/src/stdlib/__cxa_thread_atexit_impl.h
@@ -14,6 +14,7 @@
 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

>From 429133276629d13c75507f0e6dd6171b6e826640 Mon Sep 17 00:00:00 2001
From: Shriharsh Acharya <happy.irhs at gmail.com>
Date: Fri, 9 Oct 2026 21:34:33 +0545
Subject: [PATCH 09/10] feat: adds relevant tests for cxa_thread_atexit_impl

---
 .../integration/src/stdlib/CMakeLists.txt     | 12 +++
 .../src/stdlib/atexit_thread_test.cpp         | 78 +++++++++++++++++++
 2 files changed, 90 insertions(+)
 create mode 100644 libc/test/integration/src/stdlib/atexit_thread_test.cpp

diff --git a/libc/test/integration/src/stdlib/CMakeLists.txt b/libc/test/integration/src/stdlib/CMakeLists.txt
index 7976e3833e282b..c179dba20615c0 100644
--- a/libc/test/integration/src/stdlib/CMakeLists.txt
+++ b/libc/test/integration/src/stdlib/CMakeLists.txt
@@ -95,4 +95,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 00000000000000..18a58d422c1387
--- /dev/null
+++ b/libc/test/integration/src/stdlib/atexit_thread_test.cpp
@@ -0,0 +1,78 @@
+//===-- Tests for __cxa_thread_atexit_impl --------------------------------===//
+//
+// 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 "src/stdlib/__cxa_thread_atexit_impl.h"
+#include "test/IntegrationTest/test.h"
+
+#include <pthread.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;
+}

>From 8c91434506017247a8d4ca02c79a3a7ccae06af4 Mon Sep 17 00:00:00 2001
From: Shriharsh Acharya <happy.irhs at gmail.com>
Date: Fri, 9 Oct 2026 21:41:26 +0545
Subject: [PATCH 10/10] chore: clang formatting

---
 libc/src/stdlib/CMakeLists.txt                |  1 -
 .../integration/src/stdlib/CMakeLists.txt     | 22 +++++++++----------
 2 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index 9e16bdfffc9de7..f2d9e050f8c61e 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -882,4 +882,3 @@ add_entrypoint_object(
   DEPENDS
     .${LIBC_TARGET_OS}.system
 )
-
diff --git a/libc/test/integration/src/stdlib/CMakeLists.txt b/libc/test/integration/src/stdlib/CMakeLists.txt
index c179dba20615c0..d715927d29435a 100644
--- a/libc/test/integration/src/stdlib/CMakeLists.txt
+++ b/libc/test/integration/src/stdlib/CMakeLists.txt
@@ -96,15 +96,15 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
       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
-)
+    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()



More information about the libc-commits mailing list