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//===----------------------------------------------------------------------===//
8
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"
14
15namespace LIBC_NAMESPACE {
16
17namespace {
18
19Mutex handler_list_mtx(false, false, false);
20
21using AtExitCallback = void(void *);
22using StdCAtExitCallback = void(void);
23
24struct AtExitUnit {
25 AtExitCallback *callback = nullptr;
26 void *payload = nullptr;
27 constexpr AtExitUnit() = default;
28 constexpr AtExitUnit(AtExitCallback *c, void *p) : callback(c), payload(p) {}
29};
30
31#if defined(LIBC_TARGET_ARCH_IS_GPU)
32// The GPU build cannot handle the potentially recursive definitions required by
33// the BlockStore class. Additionally, the liklihood that someone exceeds this
34// while executing on the GPU is extremely small.
35// FIXME: It is not generally safe to use 'atexit' on the GPU because the
36// mutexes simply passthrough. We will need a lock free stack.
37using ExitCallbackList = FixedVector<AtExitUnit, 64>;
38#elif defined(LIBC_COPT_PUBLIC_PACKAGING)
39using ExitCallbackList = ReverseOrderBlockStore<AtExitUnit, 32>;
40#else
41// BlockStore uses dynamic memory allocation. To avoid dynamic memory
42// allocation in tests, we use a fixed size callback list when built for
43// tests.
44// If we use BlockStore, then we will have to pull in malloc etc into
45// the tests. While this is not bad, the problem we have currently is
46// that LLVM libc' allocator is SCUDO. So, we will end up pulling SCUDO's
47// deps also (some of which are not yet available in LLVM libc) into the
48// integration tests.
49using ExitCallbackList = FixedVector<AtExitUnit, CALLBACK_LIST_SIZE_FOR_TESTS>;
50#endif // LIBC_COPT_PUBLIC_PACKAGING
51
52constinit ExitCallbackList exit_callbacks;
53
54void stdc_at_exit_func(void *payload) {
55 reinterpret_cast<StdCAtExitCallback *>(payload)();
56}
57
58void call_exit_callbacks() {
59 handler_list_mtx.lock();
60 while (!exit_callbacks.empty()) {
61 AtExitUnit &unit = exit_callbacks.back();
62 exit_callbacks.pop_back();
63 handler_list_mtx.unlock();
64 unit.callback(unit.payload);
65 handler_list_mtx.lock();
66 }
67 ExitCallbackList::destroy(store: &exit_callbacks);
68}
69
70int add_atexit_unit(const AtExitUnit &unit) {
71 MutexLock lock(&handler_list_mtx);
72 if (exit_callbacks.push_back(unit))
73 return 0;
74 return -1;
75}
76
77} // namespace
78
79extern "C" {
80
81// TODO: Handle the last dso handle argument.
82int __cxa_atexit(AtExitCallback *callback, void *payload, void *) {
83 return add_atexit_unit(unit: {callback, payload});
84}
85
86// TODO: Handle the dso handle argument. call_exit_callbacks should only invoke
87// the callbacks from this DSO. Requires adding support for __dso_handle.
88void __cxa_finalize(void *dso) {
89 if (!dso)
90 call_exit_callbacks();
91}
92
93} // extern "C"
94
95LLVM_LIBC_FUNCTION(int, atexit, (StdCAtExitCallback * callback)) {
96 return add_atexit_unit(
97 unit: {&stdc_at_exit_func, reinterpret_cast<void *>(callback)});
98}
99
100} // namespace LIBC_NAMESPACE
101

source code of libc/src/stdlib/atexit.cpp