Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / libc / src / stdlib / atexit.cpp
blob10dff42b1be92546aa492e6a3b1ff827e92dac83
1 //===-- Implementation of atexit ------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "src/stdlib/atexit.h"
10 #include "src/__support/blockstore.h"
11 #include "src/__support/common.h"
12 #include "src/__support/fixedvector.h"
13 #include "src/__support/threads/mutex.h"
15 namespace LIBC_NAMESPACE {
17 namespace {
19 Mutex handler_list_mtx(false, false, false);
21 using AtExitCallback = void(void *);
22 using StdCAtExitCallback = void(void);
24 struct AtExitUnit {
25 AtExitCallback *callback = nullptr;
26 void *payload = nullptr;
27 constexpr AtExitUnit() = default;
28 constexpr AtExitUnit(AtExitCallback *c, void *p) : callback(c), payload(p) {}
31 #ifdef LIBC_COPT_PUBLIC_PACKAGING
32 using ExitCallbackList = cpp::ReverseOrderBlockStore<AtExitUnit, 32>;
33 #else
34 // BlockStore uses dynamic memory allocation. To avoid dynamic memory
35 // allocation in tests, we use a fixed size callback list when built for
36 // tests.
37 // If we use BlockStore, then we will have to pull in malloc etc into
38 // the tests. While this is not bad, the problem we have currently is
39 // that LLVM libc' allocator is SCUDO. So, we will end up pulling SCUDO's
40 // deps also (some of which are not yet available in LLVM libc) into the
41 // integration tests.
42 using ExitCallbackList = FixedVector<AtExitUnit, CALLBACK_LIST_SIZE_FOR_TESTS>;
43 #endif // LIBC_COPT_PUBLIC_PACKAGING
45 constinit ExitCallbackList exit_callbacks;
47 void stdc_at_exit_func(void *payload) {
48 reinterpret_cast<StdCAtExitCallback *>(payload)();
51 } // namespace
53 namespace internal {
55 void call_exit_callbacks() {
56 handler_list_mtx.lock();
57 while (!exit_callbacks.empty()) {
58 auto unit = exit_callbacks.back();
59 exit_callbacks.pop_back();
60 handler_list_mtx.unlock();
61 unit.callback(unit.payload);
62 handler_list_mtx.lock();
64 ExitCallbackList::destroy(&exit_callbacks);
67 } // namespace internal
69 static int add_atexit_unit(const AtExitUnit &unit) {
70 MutexLock lock(&handler_list_mtx);
71 if (!exit_callbacks.push_back(unit))
72 return -1;
73 return 0;
76 // TODO: Handle the last dso handle argument.
77 extern "C" int __cxa_atexit(AtExitCallback *callback, void *payload, void *) {
78 return add_atexit_unit({callback, payload});
81 LLVM_LIBC_FUNCTION(int, atexit, (StdCAtExitCallback * callback)) {
82 return add_atexit_unit(
83 {&stdc_at_exit_func, reinterpret_cast<void *>(callback)});
86 } // namespace LIBC_NAMESPACE