Index: libc/config/linux/api.td =================================================================== --- libc/config/linux/api.td +++ libc/config/linux/api.td @@ -135,11 +135,10 @@ } def SignalAPI : PublicAPI<"signal.h"> { - let TypeDeclarations = [ - SizeT, // This is needed by . - ]; - let Functions = [ "raise", + "sigprocmask", + "sigemptyset", + "sigaddset", ]; } Index: libc/include/signal.h.def =================================================================== --- libc/include/signal.h.def +++ libc/include/signal.h.def @@ -11,8 +11,11 @@ #include <__llvm-libc-common.h> -%%public_api() +#define __need_size_t +#include %%include_file(${platform_signal}) +%%public_api() + #endif // LLVM_LIBC_SIGNAL_H Index: libc/spec/posix.td =================================================================== --- libc/spec/posix.td +++ libc/spec/posix.td @@ -1,3 +1,9 @@ +def SigSetType : NamedType<"sigset_t">; +def SigSetPtrType : PtrType; +def ConstSigSetPtrType : ConstType; +def RestrictSigSetType : RestrictedPtrType; +def ConstRestrictSigSetType : ConstType; + def POSIX : StandardSpec<"POSIX"> { NamedType OffTType = NamedType<"off_t">; @@ -126,8 +132,32 @@ ] >; + HeaderSpec Signal = HeaderSpec< + "signal.h", + [], // Macros + [], // Types + [ + FunctionSpec< + "sigprocmask", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, + FunctionSpec< + "sigemptyset", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "sigaddset", + RetValSpec, + [ArgSpec] + >, + ] + >; + let Headers = [ Errno, SysMMan, + Signal, ]; } Index: libc/src/signal/linux/CMakeLists.txt =================================================================== --- libc/src/signal/linux/CMakeLists.txt +++ libc/src/signal/linux/CMakeLists.txt @@ -9,6 +9,43 @@ DEPENDS sys_syscall_h linux_syscall_h + signal_h +) + +add_entrypoint_object( + sigprocmask + SRCS + sigprocmask.cpp + HDRS + signal.h + ../sigprocmask.h + DEPENDS + sys_syscall_h + linux_syscall_h + __errno_location + signal_h +) + +add_entrypoint_object( + sigemptyset + SRCS + sigemptyset.cpp + HDRS + signal.h + ../sigemptyset.h + DEPENDS + __errno_location + signal_h +) + +add_entrypoint_object( + sigaddset + SRCS + sigaddset.cpp + HDRS + signal.h + ../sigaddset.h + DEPENDS __errno_location signal_h ) Index: libc/src/signal/linux/sigaddset.cpp =================================================================== --- /dev/null +++ libc/src/signal/linux/sigaddset.cpp @@ -0,0 +1,28 @@ +//===----------------- Linux implementation of sigaddset ------------------===// +// +// 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/signal/sigaddset.h" +#include "include/errno.h" // For E* macros. +#include "src/errno/llvmlibc_errno.h" +#include "src/signal/linux/signal.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +int LLVM_LIBC_ENTRYPOINT(sigaddset)(sigset_t *set, int signum) { + if (!set || (unsigned)signum > (8 * sizeof(sigset_t))) { + llvmlibc_errno = EINVAL; + return -1; + } + auto *sigset = reinterpret_cast<__llvm_libc::Sigset *>(set); + sigset->addset(signum); + return 0; +} + +} // namespace __llvm_libc Index: libc/src/signal/linux/sigemptyset.cpp =================================================================== --- /dev/null +++ libc/src/signal/linux/sigemptyset.cpp @@ -0,0 +1,27 @@ +//===--------------- Linux implementation of sigemptyset ------------------===// +// +// 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/signal/sigemptyset.h" +#include "include/errno.h" // For E* macros. +#include "src/errno/llvmlibc_errno.h" +#include "src/signal/linux/signal.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +int LLVM_LIBC_ENTRYPOINT(sigemptyset)(sigset_t *set) { + if (!set) { + llvmlibc_errno = EINVAL; + return -1; + } + *set = __llvm_libc::Sigset::emptySet(); + return 0; +} + +} // namespace __llvm_libc Index: libc/src/signal/linux/signal.h =================================================================== --- libc/src/signal/linux/signal.h +++ libc/src/signal/linux/signal.h @@ -24,6 +24,15 @@ sigset_t nativeSigset; constexpr static Sigset fullset() { return {-1UL}; } + constexpr static Sigset emptySet() { return {0}; } + + constexpr void delset(int signal) { + nativeSigset &= ~(1 << (signal - 1)); + } + + constexpr void addset(int signal) { + nativeSigset |= (1 << (signal - 1)); + } operator sigset_t() const { return nativeSigset; } }; @@ -33,8 +42,10 @@ static inline int block_all_signals(Sigset &set) { sigset_t nativeSigset = all; sigset_t oldSet = set; - return __llvm_libc::syscall(SYS_rt_sigprocmask, SIG_BLOCK, &nativeSigset, + int ret = __llvm_libc::syscall(SYS_rt_sigprocmask, SIG_BLOCK, &nativeSigset, &oldSet, sizeof(sigset_t)); + set = {oldSet}; + return ret; } static inline int restore_signals(const Sigset &set) { Index: libc/src/signal/linux/sigprocmask.cpp =================================================================== --- /dev/null +++ libc/src/signal/linux/sigprocmask.cpp @@ -0,0 +1,28 @@ +//===--------------- Linux implementation of sigprocmask ------------------===// +// +// 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/signal/sigprocmask.h" +#include "src/errno/llvmlibc_errno.h" +#include "src/signal/linux/signal.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +int LLVM_LIBC_ENTRYPOINT(sigprocmask)(int how, const sigset_t *__restrict set, + sigset_t *__restrict oldset) { + int ret = __llvm_libc::syscall(SYS_rt_sigprocmask, how, set, oldset, + sizeof(sigset_t)); + if (!ret) + return 0; + + llvmlibc_errno = -ret; + return -1; +} + +} // namespace __llvm_libc Index: libc/src/signal/sigaddset.h =================================================================== --- /dev/null +++ libc/src/signal/sigaddset.h @@ -0,0 +1,20 @@ +//===------------- Implementation header for sigaddset ---------*- 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_SIGEADDSET_H +#define LLVM_LIBC_SRC_SIGNAL_SIGEADDSET_H + +#include "include/signal.h" + +namespace __llvm_libc { + +int sigaddset(sigset_t *set, int signum); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SIGNAL_SIGEADDSET_H Index: libc/src/signal/sigemptyset.h =================================================================== --- /dev/null +++ libc/src/signal/sigemptyset.h @@ -0,0 +1,20 @@ +//===------------ Implementation header for sigemptyset --------*- 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_SIGEMPTYSET_H +#define LLVM_LIBC_SRC_SIGNAL_SIGEMPTYSET_H + +#include "include/signal.h" + +namespace __llvm_libc { + +int sigemptyset(sigset_t *set); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SIGNAL_SIGEMPTYSET_H Index: libc/src/signal/sigprocmask.h =================================================================== --- /dev/null +++ libc/src/signal/sigprocmask.h @@ -0,0 +1,21 @@ +//===------------ Implementation header for sigprocmask --------*- 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_SIGPROCMASK_H +#define LLVM_LIBC_SRC_SIGNAL_SIGPROCMASK_H + +#include "include/signal.h" + +namespace __llvm_libc { + +int sigprocmask(int how, const sigset_t *__restrict set, + sigset_t *__restrict oldset); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SIGNAL_SIGPROCMASK_H Index: libc/test/src/signal/CMakeLists.txt =================================================================== --- libc/test/src/signal/CMakeLists.txt +++ libc/test/src/signal/CMakeLists.txt @@ -1,5 +1,16 @@ add_libc_testsuite(libc_signal_unittests) + +add_header_library( + signal_test + HDRS + SignalTest.h + DEPENDS + sigprocmask + __errno_location +) + + add_libc_unittest( raise_test SUITE @@ -9,4 +20,23 @@ DEPENDS raise signal_h + sigprocmask + __errno_location + signal_test +) + +add_libc_unittest( + sigprocmask_test + SUITE + libc_signal_unittests + SRCS + sigprocmask_test.cpp + DEPENDS + raise + sigprocmask + sigaddset + sigemptyset + signal_h + __errno_location + signal_test ) Index: libc/test/src/signal/SignalTest.h =================================================================== --- /dev/null +++ libc/test/src/signal/SignalTest.h @@ -0,0 +1,27 @@ +//===------------------ Test fixture for signal tests ---------------------===// +// +// 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_TEST_SRC_SIGNAL_SIGNALTEST_H +#define LLVM_LIBC_TEST_SRC_SIGNAL_SIGNALTEST_H + +#include "include/signal.h" +#include "src/signal/sigprocmask.h" +#include "utils/UnitTest/Test.h" + +class SignalTest : public __llvm_libc::testing::Test { + sigset_t oldSet; + +public: + void SetUp() override { __llvm_libc::sigprocmask(0, nullptr, &oldSet); } + + void TearDown() override { + __llvm_libc::sigprocmask(SIG_SETMASK, &oldSet, nullptr); + } +}; + +#endif // LLVM_LIBC_TEST_SRC_SIGNAL_SIGNALTEST_H Index: libc/test/src/signal/raise_test.cpp =================================================================== --- libc/test/src/signal/raise_test.cpp +++ libc/test/src/signal/raise_test.cpp @@ -8,9 +8,11 @@ #include "include/signal.h" #include "src/signal/raise.h" + +#include "SignalTest.h" #include "utils/UnitTest/Test.h" -TEST(SignalTest, Raise) { +TEST_F(SignalTest, Raise) { // SIGCONT is ingored unless stopped, so we can use it to check the return // value of raise without needing to block. EXPECT_EQ(__llvm_libc::raise(SIGCONT), 0); Index: libc/test/src/signal/sigprocmask_test.cpp =================================================================== --- /dev/null +++ libc/test/src/signal/sigprocmask_test.cpp @@ -0,0 +1,54 @@ +//===--------------------- Unittests for sigprocmask ----------------------===// +// +// 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/sigaddset.h" +#include "src/signal/sigemptyset.h" +#include "src/signal/sigprocmask.h" + +#include "SignalTest.h" +#include "utils/UnitTest/Test.h" + +TEST_F(SignalTest, SigprocmaskInvalid) { + sigset_t valid; + llvmlibc_errno = 0; + EXPECT_EQ(__llvm_libc::sigprocmask(17, &valid, nullptr), -1); + EXPECT_EQ(llvmlibc_errno, EINVAL); + + llvmlibc_errno = 0; + EXPECT_EQ(__llvm_libc::sigprocmask(-4, &valid, nullptr), -1); + EXPECT_EQ(llvmlibc_errno, EINVAL); + + sigset_t *invalid = reinterpret_cast(-1); + + llvmlibc_errno = 0; + EXPECT_EQ(__llvm_libc::sigprocmask(SIG_SETMASK, invalid, nullptr), -1); + EXPECT_EQ(llvmlibc_errno, EFAULT); + + llvmlibc_errno = 0; + EXPECT_EQ(__llvm_libc::sigprocmask(-4, nullptr, invalid), -1); + EXPECT_EQ(llvmlibc_errno, EFAULT); +} + +TEST_F(SignalTest, BlockUnblock) { + sigset_t sigset; + EXPECT_EQ(__llvm_libc::sigemptyset(&sigset), 0); + EXPECT_EQ(__llvm_libc::sigprocmask(SIG_SETMASK, &sigset, nullptr), 0); + EXPECT_DEATH( + [] { + __llvm_libc::raise(SIGUSR1); + __llvm_libc::raise(SIGKILL); + }, + SIGUSR1); + EXPECT_EQ(__llvm_libc::sigaddset(&sigset, SIGUSR1), 0); + EXPECT_EQ(__llvm_libc::sigprocmask(SIG_SETMASK, &sigset, nullptr), 0); + EXPECT_EXITS([] { __llvm_libc::raise(SIGUSR1); }, 0); +} Index: libc/utils/UnitTest/Test.h =================================================================== --- libc/utils/UnitTest/Test.h +++ libc/utils/UnitTest/Test.h @@ -6,6 +6,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_LIBC_UTILS_UNITTEST_H +#define LLVM_LIBC_UTILS_UNITTEST_H + // This file can only include headers from utils/CPP/ or utils/testutils. No // other headers should be included. @@ -226,3 +229,5 @@ #define ASSERT_DEATH(FUNC, EXIT) \ if (!EXPECT_DEATH(FUNC, EXIT)) \ return + +#endif // LLVM_LIBC_UTILS_UNITTEST_H