Skip to content

Commit 2abf5a3

Browse files
committed
[libc] Implement 'atexit' on the GPU correctly
Summary: This function was never marked at supported because it was fundamentally broken when called with multiple threads. The patch in #83026 introduces a lock-free stack that can be used to correctly handle enqueuing callbacks from multiple threads. Although the previous interface tried to provide a consistent API, this was not feasible with the needs for a lock-free stack so I have elected to just use ifdefs. The size is fixed to whatever we use for testing, which currently amounts to about 8KiB dedicated for this thing, which isn't enough to be concenred about. Depends on #83026
1 parent 69c0b2f commit 2abf5a3

File tree

2 files changed

+18
-7
lines changed

2 files changed

+18
-7
lines changed

libc/docs/gpu/support.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ atol |check|
102102
atoll |check|
103103
exit |check| |check|
104104
abort |check| |check|
105+
atexit |check|
105106
labs |check|
106107
llabs |check|
107108
div |check|

libc/src/stdlib/atexit.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
#include "src/stdlib/atexit.h"
1010
#include "src/__support/blockstore.h"
1111
#include "src/__support/common.h"
12+
#include "src/__support/fixedstack.h"
1213
#include "src/__support/fixedvector.h"
1314
#include "src/__support/threads/mutex.h"
1415

1516
namespace LIBC_NAMESPACE {
1617

1718
namespace {
1819

19-
Mutex handler_list_mtx(false, false, false);
20+
[[maybe_unused]] Mutex handler_list_mtx(false, false, false);
2021

2122
using AtExitCallback = void(void *);
2223
using StdCAtExitCallback = void(void);
@@ -29,12 +30,10 @@ struct AtExitUnit {
2930
};
3031

3132
#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.
37-
using ExitCallbackList = FixedVector<AtExitUnit, 64>;
33+
// The GPU interface cannot use the standard implementation because it does not
34+
// support the Mutex type. Instead we use a lock free stack with a sufficiently
35+
// large size.
36+
using ExitCallbackList = FixedStack<AtExitUnit, CALLBACK_LIST_SIZE_FOR_TESTS>;
3837
#elif defined(LIBC_COPT_PUBLIC_PACKAGING)
3938
using ExitCallbackList = cpp::ReverseOrderBlockStore<AtExitUnit, 32>;
4039
#else
@@ -60,6 +59,11 @@ void stdc_at_exit_func(void *payload) {
6059
namespace internal {
6160

6261
void call_exit_callbacks() {
62+
#if defined(LIBC_TARGET_ARCH_IS_GPU)
63+
AtExitUnit unit;
64+
while (exit_callbacks.pop(unit))
65+
unit.callback(unit.payload);
66+
#else
6367
handler_list_mtx.lock();
6468
while (!exit_callbacks.empty()) {
6569
auto unit = exit_callbacks.back();
@@ -69,14 +73,20 @@ void call_exit_callbacks() {
6973
handler_list_mtx.lock();
7074
}
7175
ExitCallbackList::destroy(&exit_callbacks);
76+
#endif
7277
}
7378

7479
} // namespace internal
7580

7681
static int add_atexit_unit(const AtExitUnit &unit) {
82+
#if defined(LIBC_TARGET_ARCH_IS_GPU)
83+
if (!exit_callbacks.push(unit))
84+
return -1;
85+
#else
7786
MutexLock lock(&handler_list_mtx);
7887
if (!exit_callbacks.push_back(unit))
7988
return -1;
89+
#endif
8090
return 0;
8191
}
8292

0 commit comments

Comments
 (0)