diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -194,9 +194,16 @@ }]; } +def SighandlerTDefn : TypeDecl<"__sighandler_t"> { + let Decl = [{ + typedef void(*__sighandler_t)(int); + }]; +} + def SignalAPI : PublicAPI<"signal.h"> { let TypeDeclarations = [ StructSigactionDefn, + SighandlerTDefn, ]; let Functions = [ @@ -205,6 +212,7 @@ "sigprocmask", "sigemptyset", "sigaddset", + "signal", ]; } diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -23,6 +23,7 @@ sigaddset sigemptyset sigprocmask + signal # stdlib.h entrypoints _Exit diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -16,6 +16,8 @@ PtrType IntPtr = PtrType; + NamedType SigHandlerT = NamedType<"__sighandler_t">; + HeaderSpec Assert = HeaderSpec< "assert.h", [ @@ -226,10 +228,16 @@ ], [ SizeTType, + SigHandlerT, ], [], // Enumerations [ FunctionSpec<"raise", RetValSpec, [ArgSpec]>, + FunctionSpec< + "signal", + RetValSpec, + [ArgSpec, ArgSpec] + >, ] >; diff --git a/libc/src/signal/linux/CMakeLists.txt b/libc/src/signal/linux/CMakeLists.txt --- a/libc/src/signal/linux/CMakeLists.txt +++ b/libc/src/signal/linux/CMakeLists.txt @@ -84,3 +84,15 @@ errno_h signal_h ) + +add_entrypoint_object( + signal + SRCS + signal.cpp + HDRS + signal.h + ../signal.h + DEPENDS + sigaction + signal_h +) diff --git a/libc/src/signal/linux/signal.cpp b/libc/src/signal/linux/signal.cpp new file mode 100644 --- /dev/null +++ b/libc/src/signal/linux/signal.cpp @@ -0,0 +1,26 @@ +//===------------------ Linux implementation of signal --------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#define __LLVM_LIBC_INTERNAL_SIGACTION +#include "src/signal/signal.h" +#include "src/signal/sigaction.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +sighandler_t LLVM_LIBC_ENTRYPOINT(signal)(int signum, sighandler_t handler) { + struct __sigaction action, old; + action.sa_handler = handler; + action.sa_flags = SA_RESTART; + // Errno will already be set so no need to worry about changing errno here. + return __llvm_libc::sigaction(signum, &action, &old) == -1 ? SIG_ERR + : old.sa_handler; +} + +} // namespace __llvm_libc diff --git a/libc/src/signal/signal.h b/libc/src/signal/signal.h new file mode 100644 --- /dev/null +++ b/libc/src/signal/signal.h @@ -0,0 +1,22 @@ +//===------------- Implementation header for signal ------------*- 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_SIGNAL_SIGNAL_H +#define LLVM_LIBC_SRC_SIGNAL_SIGNAL_H + +#include "include/signal.h" + +namespace __llvm_libc { + +using sighandler_t = __sighandler_t; + +sighandler_t signal(int signum, sighandler_t handler); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SIGNAL_SIGNAL_H diff --git a/libc/test/src/signal/CMakeLists.txt b/libc/test/src/signal/CMakeLists.txt --- a/libc/test/src/signal/CMakeLists.txt +++ b/libc/test/src/signal/CMakeLists.txt @@ -52,3 +52,18 @@ signal_h __errno_location ) + +add_libc_unittest( + signal_test + SUITE + libc_signal_unittests + SRCS + signal_test.cpp + DEPENDS + signal + signal_h + sigaction + raise + __errno_location + errno_h +) diff --git a/libc/test/src/signal/signal_test.cpp b/libc/test/src/signal/signal_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/signal/signal_test.cpp @@ -0,0 +1,41 @@ +//===------------------------ Unittests for signal ------------------------===// +// +// 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 "include/errno.h" +#include "include/signal.h" +#include "src/errno/llvmlibc_errno.h" +#include "src/signal/raise.h" +#include "src/signal/signal.h" + +#include "utils/UnitTest/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +using __llvm_libc::testing::ErrnoSetterMatcher::Fails; +using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + +TEST(Signal, Invalid) { + llvmlibc_errno = 0; + __llvm_libc::sighandler_t valid = +[](int) {}; + EXPECT_THAT((void *)__llvm_libc::signal(0, valid), + Fails(EINVAL, (void *)SIG_ERR)); + EXPECT_THAT((void *)__llvm_libc::signal(65, valid), + Fails(EINVAL, (void *)SIG_ERR)); +} + +static int sum; +TEST(Signal, Basic) { + // In case test get run multiple times. + sum = 0; + ASSERT_NE(__llvm_libc::signal(SIGUSR1, +[](int) { sum++; }), + SIG_ERR); + ASSERT_THAT(__llvm_libc::raise(SIGUSR1), Succeeds()); + EXPECT_EQ(sum, 1); + for (int i = 0; i < 10; i++) + ASSERT_THAT(__llvm_libc::raise(SIGUSR1), Succeeds()); + EXPECT_EQ(sum, 11); +} diff --git a/libc/utils/UnitTest/Test.h b/libc/utils/UnitTest/Test.h --- a/libc/utils/UnitTest/Test.h +++ b/libc/utils/UnitTest/Test.h @@ -249,13 +249,20 @@ #define UNIQUE_VAR(prefix) __CAT(prefix, __LINE__) #define EXPECT_THAT(MATCH, MATCHER) \ - auto UNIQUE_VAR(__matcher) = (MATCHER); \ - __llvm_libc::testing::Test::testMatch( \ - Ctx, UNIQUE_VAR(__matcher).match((MATCH)), UNIQUE_VAR(__matcher), \ - #MATCH, #MATCHER, __FILE__, __LINE__) + do { \ + auto UNIQUE_VAR(__matcher) = (MATCHER); \ + __llvm_libc::testing::Test::testMatch( \ + Ctx, UNIQUE_VAR(__matcher).match((MATCH)), UNIQUE_VAR(__matcher), \ + #MATCH, #MATCHER, __FILE__, __LINE__); \ + } while (0) #define ASSERT_THAT(MATCH, MATCHER) \ - if (!EXPECT_THAT(MATCH, MATCHER)) \ - return + do { \ + auto UNIQUE_VAR(__matcher) = (MATCHER); \ + if (!__llvm_libc::testing::Test::testMatch( \ + Ctx, UNIQUE_VAR(__matcher).match((MATCH)), UNIQUE_VAR(__matcher), \ + #MATCH, #MATCHER, __FILE__, __LINE__)) \ + return; \ + } while (0) #endif // LLVM_LIBC_UTILS_UNITTEST_H