[libc-commits] [libc] d669838 - [libc] Add exit and atexit
Alex Brachet via libc-commits
libc-commits at lists.llvm.org
Thu Feb 17 09:23:50 PST 2022
Author: Alex Brachet
Date: 2022-02-17T17:21:55Z
New Revision: d66983861a66b1eb9ff6d08b57a9cdd8e2a35932
URL: https://github.com/llvm/llvm-project/commit/d66983861a66b1eb9ff6d08b57a9cdd8e2a35932
DIFF: https://github.com/llvm/llvm-project/commit/d66983861a66b1eb9ff6d08b57a9cdd8e2a35932.diff
LOG: [libc] Add exit and atexit
Often atexit is implemented using __cxa_atexit. I have not implemented __cxa_atexit here because it potentially requires more discussion. It is unique for llvm-libc (I think) that it is an exported symbol that wouldn’t be defined in any spec file because it doesn’t have a header. Implementing it will be trivial given what is here already, but I figured it would be more contentious so it can be implemented later.
Reviewed By: lntue
Differential Revision: https://reviews.llvm.org/D119512
Added:
libc/include/llvm-libc-types/__atexithandler_t.h
libc/src/stdlib/atexit.cpp
libc/src/stdlib/atexit.h
libc/src/stdlib/exit.cpp
libc/src/stdlib/exit.h
libc/test/src/stdlib/atexit_test.cpp
Modified:
libc/config/linux/api.td
libc/config/linux/x86_64/entrypoints.txt
libc/include/CMakeLists.txt
libc/include/llvm-libc-types/CMakeLists.txt
libc/spec/spec.td
libc/spec/stdc.td
libc/src/stdlib/CMakeLists.txt
libc/test/src/stdlib/CMakeLists.txt
libc/test/src/stdlib/_Exit_test.cpp
Removed:
################################################################################
diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 69c663f388b2c..d584de545f22b 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -159,7 +159,8 @@ def StdlibAPI : PublicAPI<"stdlib.h"> {
"lldiv_t",
"size_t",
"__bsearchcompare_t",
- "__qsortcompare_t"
+ "__qsortcompare_t",
+ "__atexithandler_t",
];
}
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index f21d032464dc4..500e9cc3a43be 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -226,6 +226,8 @@ if(LLVM_LIBC_FULL_BUILD)
# stdlib.h entrypoints
libc.src.stdlib._Exit
# libc.src.stdlib.abort
+ libc.src.stdlib.atexit
+ libc.src.stdlib.exit
# signal.h entrypoints
# TODO: Enable signal.h entrypoints after fixing signal.h
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 6198e90e3fd0d..9370b5a37705e 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -141,6 +141,7 @@ add_gen_header(
.llvm-libc-types.ldiv_t
.llvm-libc-types.lldiv_t
.llvm-libc-types.size_t
+ .llvm-libc-types.__atexithandler_t
)
add_gen_header(
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index cf70d4472c8f3..dfb34c0696773 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -23,3 +23,4 @@ add_header(struct_tm HDR struct_tm.h)
add_header(thrd_start_t HDR thrd_start_t.h)
add_header(thrd_t HDR thrd_t.h)
add_header(time_t HDR time_t.h)
+add_header(__atexithandler_t HDR __atexithandler_t.h)
diff --git a/libc/include/llvm-libc-types/__atexithandler_t.h b/libc/include/llvm-libc-types/__atexithandler_t.h
new file mode 100644
index 0000000000000..a9887b6abf708
--- /dev/null
+++ b/libc/include/llvm-libc-types/__atexithandler_t.h
@@ -0,0 +1,14 @@
+//===-- Definition of type __atexithandler_t ------------------------------===//
+//
+// 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_TYPES_ATEXITHANDLER_T_H__
+#define __LLVM_LIBC_TYPES_ATEXITHANDLER_T_H__
+
+typedef void (*__atexithandler_t)(void);
+
+#endif // __LLVM_LIBC_TYPES_ATEXITHANDLER_T_H__
diff --git a/libc/spec/spec.td b/libc/spec/spec.td
index d2f194bd966f6..183e1b7d0ecc4 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -94,6 +94,8 @@ def TimeTType : NamedType<"time_t">;
def BSearchCompareT : NamedType<"__bsearchcompare_t">;
def QSortCompareT : NamedType<"__qsortcompare_t">;
+def AtexitHandlerT : NamedType<"__atexithandler_t">;
+
//added because __assert_fail needs it.
def UnsignedType : NamedType<"unsigned">;
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 14b2d4d9f2eb7..653a8a14de37f 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -502,6 +502,7 @@ def StdC : StandardSpec<"stdc"> {
SizeTType,
BSearchCompareT,
QSortCompareT,
+ AtexitHandlerT,
], // Types
[], // Enumerations
[
@@ -538,6 +539,8 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"free", RetValSpec<VoidType>, [ArgSpec<VoidPtr>]>,
FunctionSpec<"_Exit", RetValSpec<NoReturn>, [ArgSpec<IntType>]>,
+ FunctionSpec<"exit", RetValSpec<NoReturn>, [ArgSpec<IntType>]>,
+ FunctionSpec<"atexit", RetValSpec<IntType>, [ArgSpec<AtexitHandlerT>]>,
]
>;
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index d679b068ee313..2472466a086dc 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -270,6 +270,30 @@ add_entrypoint_object(
.${LIBC_TARGET_OS}._Exit
)
+add_entrypoint_object(
+ atexit
+ SRCS
+ atexit.cpp
+ HDRS
+ atexit.h
+ DEPENDS
+ libc.src.__support.CPP.vector
+ libc.src.threads.mtx_init
+ libc.src.threads.mtx_lock
+ libc.src.threads.mtx_unlock
+)
+
+add_entrypoint_object(
+ exit
+ SRCS
+ exit.cpp
+ HDRS
+ exit.h
+ DEPENDS
+ ._Exit
+ .atexit
+)
+
# add_entrypoint_object(
# abort
# ALIAS
diff --git a/libc/src/stdlib/atexit.cpp b/libc/src/stdlib/atexit.cpp
new file mode 100644
index 0000000000000..9d18cd9c3cece
--- /dev/null
+++ b/libc/src/stdlib/atexit.cpp
@@ -0,0 +1,54 @@
+//===-- Implementation of atexit ------------------------------------------===//
+//
+// 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/stdlib/atexit.h"
+#include "src/__support/CPP/vector.h"
+#include "src/__support/common.h"
+#include "src/threads/mtx_init.h"
+#include "src/threads/mtx_lock.h"
+#include "src/threads/mtx_unlock.h"
+
+namespace __llvm_libc {
+
+namespace {
+
+mtx_t lock;
+// TODO need an easier way to use mtx_t internally, or use pthread_mutex_t
+// with PTHREAD_MUTEX_INITIALIZER when it lands.
+struct Init {
+ Init() { __llvm_libc::mtx_init(&lock, mtx_plain); }
+} init;
+
+// TOOD should we make cpp::vector like llvm::SmallVector<T, N> where it will
+// allocate at least N before needing dynamic allocation?
+static cpp::vector<void (*)(void)> handlers;
+
+} // namespace
+
+namespace internal {
+
+void call_exit_handlers() {
+ __llvm_libc::mtx_lock(&lock);
+ // TODO: implement rbegin() + rend() for cpp::vector
+ for (int i = handlers.size() - 1; i >= 0; i--) {
+ __llvm_libc::mtx_unlock(&lock);
+ handlers[i]();
+ __llvm_libc::mtx_lock(&lock);
+ }
+}
+
+} // namespace internal
+
+LLVM_LIBC_FUNCTION(int, atexit, (void (*function)())) {
+ __llvm_libc::mtx_lock(&lock);
+ handlers.push_back(function);
+ __llvm_libc::mtx_unlock(&lock);
+ return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdlib/atexit.h b/libc/src/stdlib/atexit.h
new file mode 100644
index 0000000000000..574549e1f32bb
--- /dev/null
+++ b/libc/src/stdlib/atexit.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for atexit ------------------------*- 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_STDLIB_ATEXIT_H
+#define LLVM_LIBC_SRC_STDLIB_ATEXIT_H
+
+namespace __llvm_libc {
+
+int atexit(void (*function)());
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDLIB_ATEXIT_H
diff --git a/libc/src/stdlib/exit.cpp b/libc/src/stdlib/exit.cpp
new file mode 100644
index 0000000000000..5a02a45d4f18e
--- /dev/null
+++ b/libc/src/stdlib/exit.cpp
@@ -0,0 +1,24 @@
+//===-- Implementation of exit --------------------------------------------===//
+//
+// 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/stdlib/exit.h"
+#include "src/__support/common.h"
+#include "src/stdlib/_Exit.h"
+
+namespace __llvm_libc {
+
+namespace internal {
+void call_exit_handlers();
+}
+
+LLVM_LIBC_FUNCTION(void, exit, (int status)) {
+ internal::call_exit_handlers();
+ _Exit(status);
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdlib/exit.h b/libc/src/stdlib/exit.h
new file mode 100644
index 0000000000000..7c015e3c0dae3
--- /dev/null
+++ b/libc/src/stdlib/exit.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for exit --------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdlib.h>
+
+#ifndef LLVM_LIBC_SRC_STDLIB_EXIT_H
+#define LLVM_LIBC_SRC_STDLIB_EXIT_H
+
+namespace __llvm_libc {
+
+void exit(int status);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDLIB_EXIT_H
diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index 8e1948399123a..8534692b342b1 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -110,35 +110,6 @@ add_libc_unittest(
libc.src.stdlib.strtoull
)
-if(NOT LLVM_LIBC_FULL_BUILD)
- return()
-endif()
-
-add_libc_unittest(
- _Exit_test
- SUITE
- libc_stdlib_unittests
- SRCS
- _Exit_test.cpp
- DEPENDS
- libc.include.stdlib
- libc.src.stdlib._Exit
-)
-
-# add_libc_unittest(
-# abort_test
-# SUITE
-# libc_stdlib_unittests
-# SRCS
-# abort_test.cpp
-# DEPENDS
-# libc.include.stdlib
-# libc.include.signal
-# libc.src.stdlib.abort
-# libc.src.stdlib._Exit
-# libc.src.signal.raise
-# )
-
add_libc_unittest(
abs_test
SUITE
@@ -229,3 +200,47 @@ add_libc_unittest(
libc.include.stdlib
libc.src.stdlib.qsort
)
+
+if(LLVM_LIBC_FULL_BUILD)
+
+ add_libc_unittest(
+ _Exit_test
+ SUITE
+ libc_stdlib_unittests
+ SRCS
+ _Exit_test.cpp
+ DEPENDS
+ libc.include.stdlib
+ libc.src.stdlib._Exit
+ libc.src.stdlib.exit
+ )
+
+ add_libc_unittest(
+ atexit_test
+ SUITE
+ libc_stdlib_unittests
+ SRCS
+ atexit_test.cpp
+ DEPENDS
+ libc.include.stdlib
+ libc.src.stdlib._Exit
+ libc.src.stdlib.exit
+ libc.src.stdlib.atexit
+ libc.src.__support.CPP.standalone_cpp
+ )
+
+ # add_libc_unittest(
+ # abort_test
+ # SUITE
+ # libc_stdlib_unittests
+ # SRCS
+ # abort_test.cpp
+ # DEPENDS
+ # libc.include.stdlib
+ # libc.include.signal
+ # libc.src.stdlib.abort
+ # libc.src.stdlib._Exit
+ # libc.src.signal.raise
+ # )
+
+endif()
diff --git a/libc/test/src/stdlib/_Exit_test.cpp b/libc/test/src/stdlib/_Exit_test.cpp
index 03d5ffcc22366..a07034dbf310e 100644
--- a/libc/test/src/stdlib/_Exit_test.cpp
+++ b/libc/test/src/stdlib/_Exit_test.cpp
@@ -8,9 +8,13 @@
#include "include/stdlib.h"
#include "src/stdlib/_Exit.h"
+#include "src/stdlib/exit.h"
#include "utils/UnitTest/Test.h"
TEST(LlvmLibcStdlib, _Exit) {
EXPECT_EXITS([] { __llvm_libc::_Exit(1); }, 1);
EXPECT_EXITS([] { __llvm_libc::_Exit(65); }, 65);
+
+ EXPECT_EXITS([] { __llvm_libc::exit(1); }, 1);
+ EXPECT_EXITS([] { __llvm_libc::exit(65); }, 65);
}
diff --git a/libc/test/src/stdlib/atexit_test.cpp b/libc/test/src/stdlib/atexit_test.cpp
new file mode 100644
index 0000000000000..bce69d88dc26e
--- /dev/null
+++ b/libc/test/src/stdlib/atexit_test.cpp
@@ -0,0 +1,94 @@
+//===-- Unittests for atexit ----------------------------------------------===//
+//
+// 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/CPP/Array.h"
+#include "src/__support/CPP/Utility.h"
+#include "src/stdlib/atexit.h"
+#include "src/stdlib/exit.h"
+#include "utils/UnitTest/Test.h"
+
+static int a;
+TEST(LlvmLibcAtExit, Basic) {
+ // In case tests ever run multiple times.
+ a = 0;
+
+ auto test = [] {
+ int status = __llvm_libc::atexit(+[] {
+ if (a != 1)
+ __builtin_trap();
+ });
+ status |= __llvm_libc::atexit(+[] { a++; });
+ if (status)
+ __builtin_trap();
+
+ __llvm_libc::exit(0);
+ };
+ EXPECT_EXITS(test, 0);
+}
+
+TEST(LlvmLibcAtExit, AtExitCallsSysExit) {
+ auto test = [] {
+ __llvm_libc::atexit(+[] { _Exit(1); });
+ __llvm_libc::exit(0);
+ };
+ EXPECT_EXITS(test, 1);
+}
+
+static int size;
+static __llvm_libc::cpp::Array<int, 256> arr;
+
+template <int... Ts>
+void register_atexit_handlers(__llvm_libc::cpp::IntegerSequence<int, Ts...>) {
+ (__llvm_libc::atexit(+[] { arr[size++] = Ts; }), ...);
+}
+
+template <int count> constexpr auto getTest() {
+ return [] {
+ __llvm_libc::atexit(+[] {
+ if (size != count)
+ __builtin_trap();
+ for (int i = 0; i < count; i++)
+ if (arr[i] != count - 1 - i)
+ __builtin_trap();
+ });
+ register_atexit_handlers(
+ __llvm_libc::cpp::MakeIntegerSequence<int, count>{});
+ __llvm_libc::exit(0);
+ };
+}
+
+TEST(LlvmLibcAtExit, ReverseOrder) {
+ // In case tests ever run multiple times.
+ size = 0;
+
+ auto test = getTest<32>();
+ EXPECT_EXITS(test, 0);
+}
+
+TEST(LlvmLibcAtExit, Many) {
+ // In case tests ever run multiple times.
+ size = 0;
+
+ auto test = getTest<256>();
+ EXPECT_EXITS(test, 0);
+}
+
+// POSIX doesn't specify if an atexit handler can call atexit, it only says it
+// is undefined for a handler to call exit(3). The current implementation will
+// end up invoking the newly registered function, although glibc does, other
+// libc's do not. This just tests that we don't deadlock when an exit handler
+// calls atexit.
+TEST(LlvmLibcAtExit, HandlerCallsAtExit) {
+ auto test = [] {
+ __llvm_libc::atexit(+[] {
+ __llvm_libc::atexit(+[] { __builtin_trap(); });
+ __llvm_libc::exit(0);
+ });
+ };
+ EXPECT_EXITS(test, 0);
+}
More information about the libc-commits
mailing list