[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