Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

infra: setup signal handling with SA_ONSTACK flag #1943

Merged
merged 4 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions silkworm/infra/concurrency/signal_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,31 @@ std::atomic_int SignalHandler::sig_code_{0};
std::atomic_bool SignalHandler::signalled_{false};
std::function<void(int)> SignalHandler::custom_handler_;
bool SignalHandler::silent_{false};
std::map<int, void (*)(int)> previous_signal_handlers;

using SignalHandlerFunc = void (*)(int);
std::map<int, SignalHandlerFunc> previous_signal_handlers;

static SignalHandlerFunc register_signal_action(const int signal_number, SignalHandlerFunc handler_func) {
canepat marked this conversation as resolved.
Show resolved Hide resolved
#ifdef _WIN32
return signal(signal_number, handler_func);
#else
struct sigaction sa {};
sa.sa_handler = handler_func;
sa.sa_flags = SA_ONSTACK;
sigfillset(&sa.sa_mask);
struct sigaction previous_sa {};
const int result = ::sigaction(signal_number, &sa, &previous_sa);
if (result == -1) {
return SIG_ERR;
}
return previous_sa.sa_handler;
#endif // _WIN32
}

void SignalHandler::init(std::function<void(int)> custom_handler, bool silent) {
for (const int sig_code : kHandleableCodes) {
// Register our signal handler and remember the existing ones
auto previous_handler{signal(sig_code, &SignalHandler::handle)};
auto previous_handler{register_signal_action(sig_code, &SignalHandler::handle)};
if (previous_handler != SIG_ERR) {
previous_signal_handlers[sig_code] = previous_handler;
}
Expand Down Expand Up @@ -145,18 +164,19 @@ void SignalHandler::handle(int sig_code) {
if (custom_handler_) {
custom_handler_(sig_code);
}
if (signal(sig_code, &SignalHandler::handle) == SIG_ERR) { // Re-enable the hook
if (register_signal_action(sig_code, &SignalHandler::handle) == SIG_ERR) { // Re-enable the hook
(void)std::fputs("Failed to re-enable signal hook :(", stderr);
}
}

void SignalHandler::reset() {
signalled_ = false;
sig_count_ = 0;

// Restore any previous signal handlers
for (const int sig_code : kHandleableCodes) {
if (previous_signal_handlers.contains(sig_code)) {
if (signal(sig_code, previous_signal_handlers[sig_code]) == SIG_ERR) {
if (register_signal_action(sig_code, previous_signal_handlers[sig_code]) == SIG_ERR) {
(void)std::fputs("Failed to restore previous signal handlers :(", stderr);
}
}
Expand Down
38 changes: 28 additions & 10 deletions silkworm/infra/concurrency/signal_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,38 @@

namespace silkworm {

//! \brief Handler with static storage for signals sig
//! \brief Handler for system signals using static storage
class SignalHandler {
public:
static void init(std::function<void(int)> custom_handler = {}, bool silent = false); // Enable the hooks
static void handle(int sig_code); // Handles incoming signal
[[nodiscard]] static bool signalled() { return signalled_; } // Whether a signal has been intercepted
static void reset(); // Reset to un-signalled (see tests coverage)
static void throw_if_signalled(); // Throws std::runtime_error if signalled() == true
//! Register its own signal handling hook (previous handlers are saved)
static void init(std::function<void(int)> custom_handler = {}, bool silent = false);

//! Handle incoming signal
static void handle(int sig_code);

//! Whether any signal has been intercepted or not
[[nodiscard]] static bool signalled() { return signalled_; }

//! Reset to un-signalled state (restore previous signal handlers)
static void reset();

//! Throw std::runtime_error if in signalled state
static void throw_if_signalled();

private:
static std::atomic_int sig_code_; // Last sig_code which raised the signalled_ state
static std::atomic_uint32_t sig_count_; // Number of signals intercepted
static std::atomic_bool signalled_; // Whether a signal has been intercepted
static std::function<void(int)> custom_handler_; // Custom handling
//! Last signal code which raised the signalled state
static std::atomic_int sig_code_;

//! Number of signals intercepted
static std::atomic_uint32_t sig_count_;

//! Whether a signal has been intercepted
static std::atomic_bool signalled_;

//! Custom handling
static std::function<void(int)> custom_handler_;

//! Flag indicating if signal handler can write on standard streams or not
static bool silent_;
};

Expand Down
34 changes: 28 additions & 6 deletions silkworm/infra/concurrency/signal_handler_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

#include <csignal>
#include <vector>

#include <catch2/catch.hpp>

Expand All @@ -23,12 +24,33 @@
namespace silkworm {

#if !defined(__APPLE__) || defined(NDEBUG)
TEST_CASE("Signal Handler") {
SignalHandler::init({}, /*silent=*/true);
CHECK(std::raise(SIGINT) == 0);
CHECK(SignalHandler::signalled());
SignalHandler::reset();
CHECK(SignalHandler::signalled() == false);
static const std::vector<int> kSignalNumbers{SIGINT, SIGTERM};

TEST_CASE("SignalHandler") {
for (const auto sig_number : kSignalNumbers) {
SECTION("signal number: " + std::to_string(sig_number)) {
SignalHandler::init({}, /*silent=*/true);
REQUIRE(std::raise(sig_number) == 0);
CHECK(SignalHandler::signalled());
SignalHandler::reset();
CHECK_FALSE(SignalHandler::signalled());
}
}
}

TEST_CASE("SignalHandler: custom handler") {
for (const auto sig_number : kSignalNumbers) {
SECTION("signal number: " + std::to_string(sig_number)) {
auto custom_handler = [sig_number](int sig_code) {
CHECK(sig_code == sig_number);
};
SignalHandler::init(custom_handler, /*silent=*/true);
REQUIRE(std::raise(sig_number) == 0);
CHECK(SignalHandler::signalled());
SignalHandler::reset();
CHECK_FALSE(SignalHandler::signalled());
}
}
}
#endif // !defined(__APPLE__) || defined(NDEBUG)

Expand Down
Loading