blob: 8c06199233ef48c951f02143c8d71a3b60887f33 [file]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Internal Linux signal mask helpers.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SIGMASK_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SIGMASK_H
#include "hdr/errno_macros.h"
#include "hdr/signal_macros.h"
#include "hdr/types/sigset_t.h"
#include "hdr/types/size_t.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h"
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
namespace signals {
static constexpr size_t BITS_PER_SIGWORD = sizeof(unsigned long) * 8;
/// Return a signal set with all signals set.
LIBC_INLINE constexpr sigset_t full_set() {
sigset_t set{};
for (unsigned long &sigword : set.__signals)
sigword = -1UL;
return set;
}
/// Return an empty signal set with no signals set.
LIBC_INLINE constexpr sigset_t empty_set() { return sigset_t{}; }
/// Set or clear the bit corresponding to |signal| in |set| according to
/// |value|. Return true on success and false if |signal| is out of range.
LIBC_INLINE constexpr bool set_bit(sigset_t &set, int signal, bool value) {
if (signal >= NSIG || signal <= 0)
return false;
size_t n = static_cast<size_t>(signal) - 1;
size_t word = n / BITS_PER_SIGWORD;
size_t bit = n % BITS_PER_SIGWORD;
if (value)
set.__signals[word] |= (1UL << bit);
else
set.__signals[word] &= ~(1UL << bit);
return true;
}
/// Set the bit corresponding to |signal| in |set|. Return true on success
/// and false on failure. |signal| must be less than NSIG and be positive.
LIBC_INLINE constexpr bool add(sigset_t &set, int signal) {
return set_bit(set, signal, true);
}
/// Clear the bit corresponding to |signal| in |set|. Return true on success
/// and false on failure. |signal| must be less than NSIG and be positive.
LIBC_INLINE constexpr bool remove(sigset_t &set, int signal) {
return set_bit(set, signal, false);
}
/// Test if the bit corresponding to |signal| in |set| is set.
/// Returns 1 if set, 0 if not set, or Error(EINVAL) if |signal| is invalid.
LIBC_INLINE constexpr ErrorOr<int> is_member(const sigset_t &set, int signal) {
if (signal >= NSIG || signal <= 0)
return Error(EINVAL);
size_t n = static_cast<size_t>(signal) - 1;
size_t word = n / BITS_PER_SIGWORD;
size_t bit = n % BITS_PER_SIGWORD;
return (set.__signals[word] & (1UL << bit)) ? 1 : 0;
}
/// Block the signals in |set|. If |old_set| is non-null, store the previous
/// signal mask in |*old_set|.
LIBC_INLINE ErrorOr<int> block(const sigset_t &set,
sigset_t *old_set = nullptr) {
return linux_syscalls::rt_sigprocmask(SIG_BLOCK, &set, old_set);
}
/// Block a single |signal|. If |old_set| is non-null, store the previous
/// signal mask in |*old_set|.
LIBC_INLINE ErrorOr<int> block(int signal, sigset_t *old_set = nullptr) {
sigset_t set = empty_set();
if (!add(set, signal))
return Error(EINVAL);
return block(set, old_set);
}
/// Block all signals. If |old_set| is non-null, store the previous signal mask
/// in |*old_set|.
LIBC_INLINE ErrorOr<int> block_all(sigset_t *old_set = nullptr) {
return block(full_set(), old_set);
}
/// Set the signal mask to |set|. If |old_set| is non-null, store the previous
/// signal mask in |*old_set|.
LIBC_INLINE ErrorOr<int> set_mask(const sigset_t &set,
sigset_t *old_set = nullptr) {
return linux_syscalls::rt_sigprocmask(SIG_SETMASK, &set, old_set);
}
/// Store the current signal mask in |set|.
LIBC_INLINE ErrorOr<int> get_mask(sigset_t &set) {
return linux_syscalls::rt_sigprocmask(SIG_SETMASK, nullptr, &set);
}
/// Unblock the signals in |set|. If |old_set| is non-null, store the previous
/// signal mask in |*old_set|.
LIBC_INLINE ErrorOr<int> unblock(const sigset_t &set,
sigset_t *old_set = nullptr) {
return linux_syscalls::rt_sigprocmask(SIG_UNBLOCK, &set, old_set);
}
/// Unblock a single |signal|. If |old_set| is non-null, store the previous
/// signal mask in |*old_set|.
LIBC_INLINE ErrorOr<int> unblock(int signal, sigset_t *old_set = nullptr) {
sigset_t set = empty_set();
if (!add(set, signal))
return Error(EINVAL);
return unblock(set, old_set);
}
} // namespace signals
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SIGMASK_H