Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libc/src/__support/threads/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ void call_atexit_callbacks(ThreadAttributes *attrib) {
}
}

extern "C" void __cxa_thread_finalize() { call_atexit_callbacks(self.attrib); }

} // namespace internal

cpp::optional<unsigned int> new_tss_key(TSSDtor *dtor) {
Expand Down
2 changes: 1 addition & 1 deletion libc/src/__support/threads/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ struct alignas(STACK_ALIGNMENT) ThreadAttributes {
ThreadAtExitCallbackMgr *atexit_callback_mgr;
void *platform_data;

constexpr ThreadAttributes()
LIBC_INLINE constexpr ThreadAttributes()
: detach_state(uint32_t(DetachState::DETACHED)), stack(nullptr),
stacksize(0), guardsize(0), tls(0), tls_size(0), owned_stack(false),
tid(-1), style(ThreadStyle::POSIX), retval(),
Expand Down
10 changes: 10 additions & 0 deletions libc/src/stdlib/exit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,17 @@ namespace LIBC_NAMESPACE_DECL {

extern "C" void __cxa_finalize(void *);

// exit needs to clean up TLS and call associated destructors.
// TODO: Strictly speaking, it is not valid to call exit in overlay mode
// as we have no way to ensure system libc will call the TLS destructors.
// We should run exit related tests in hermetic mode but this is currently
// blocked by https://github.com/llvm/llvm-project/issues/133925.
extern "C" [[gnu::weak]] void __cxa_thread_finalize();

// TODO: use recursive mutex to protect this routine.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering earlier if it would be possible to just make these routines lock free, since I'm pretty sure it's an append-only data structure, pretty trivial to, I could probably add that functionality to the existing blockstore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, the weak symbol approach is also used in __cxa_finalize (for tearing down TLS).

I remember the reason is that some code is supposed to be used in overlay mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering earlier if it would be possible to just make these routines lock free, since I'm pretty sure it's an append-only data structure, pretty trivial to, I could probably add that functionality to the existing blockstore.

There are possibilities but I will need to check. The semantic of recursively adding exit hook or adding exit hook in parallel of running the hook is a grey area in the specification. We will need to work out the details.

[[noreturn]] LLVM_LIBC_FUNCTION(void, exit, (int status)) {
if (__cxa_thread_finalize)
Copy link

Copilot AI Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to __cxa_thread_finalize is not protected by any locking mechanism; consider using a recursive mutex as noted in the TODO to prevent potential race conditions during exit.

Copilot uses AI. Check for mistakes.
__cxa_thread_finalize();
__cxa_finalize(nullptr);
internal::exit(status);
}
Expand Down
21 changes: 21 additions & 0 deletions libc/test/integration/src/__support/threads/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,24 @@ add_integration_test(
DEPENDS
libc.src.__support.threads.thread
)

add_integration_test(
main_exit_test
SUITE
libc-support-threads-integration-tests
SRCS
main_exit_test.cpp
DEPENDS
libc.src.__support.threads.thread
)

add_integration_test(
double_exit_test
SUITE
libc-support-threads-integration-tests
SRCS
double_exit_test.cpp
DEPENDS
libc.src.__support.threads.thread
libc.src.stdlib.exit
)
33 changes: 33 additions & 0 deletions libc/test/integration/src/__support/threads/double_exit_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===-- Test handling of thread local data --------------------------------===//
//
// 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/__support/threads/thread.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;

[[gnu::destructor]]
void check() {
// This destructor should be called only once.
if (call_num != 1)
__builtin_trap();
}

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::exit(1);
}
30 changes: 30 additions & 0 deletions libc/test/integration/src/__support/threads/main_exit_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===-- Test handling of thread local data --------------------------------===//
//
// 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/__support/threads/thread.h"
#include "test/IntegrationTest/test.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]]
void destructor() {
if (!called)
__builtin_trap();
}

TEST_MAIN() {
__cxa_thread_atexit_impl([](void *) { called = true; }, nullptr,
__dso_handle);
return 0;
}
Loading