Prevent double termination signals causing crashes
Summary: Use sigaction to register signal handlers. This is preferred over `signal` function, according to `man 3p signal`. Add global sig_atomic_t flag when shutting down. Block other signal handlers when shutting down. Reviewers: mislav.bradac, mferencevic Reviewed By: mferencevic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D943
This commit is contained in:
parent
ab2f7c3288
commit
b36386cfe9
@ -1,3 +1,4 @@
|
|||||||
|
#include <csignal>
|
||||||
#include <experimental/filesystem>
|
#include <experimental/filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@ -40,6 +41,13 @@ DEFINE_uint64(memory_warning_threshold, 1024,
|
|||||||
"less available RAM available it will log a warning. Set to 0 to "
|
"less available RAM available it will log a warning. Set to 0 to "
|
||||||
"disable.");
|
"disable.");
|
||||||
|
|
||||||
|
// Needed to correctly handle memgraph destruction from a signal handler.
|
||||||
|
// Without having some sort of a flag, it is possible that a signal is handled
|
||||||
|
// when we are exiting main, inside destructors of GraphDb and similar. The
|
||||||
|
// signal handler may then initiate another shutdown on memgraph which is in
|
||||||
|
// half destructed state, causing invalid memory access and crash.
|
||||||
|
volatile sig_atomic_t is_shutting_down = 0;
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
google::SetUsageMessage("Memgraph database server");
|
google::SetUsageMessage("Memgraph database server");
|
||||||
gflags::SetVersionString(version_string);
|
gflags::SetVersionString(version_string);
|
||||||
@ -56,20 +64,6 @@ int main(int argc, char **argv) {
|
|||||||
// Unhandled exception handler init.
|
// Unhandled exception handler init.
|
||||||
std::set_terminate(&terminate_handler);
|
std::set_terminate(&terminate_handler);
|
||||||
|
|
||||||
// Signal handling init.
|
|
||||||
SignalHandler::register_handler(Signal::SegmentationFault, []() {
|
|
||||||
// Log that we got SIGSEGV and abort the program, because returning from
|
|
||||||
// SIGSEGV handler is undefined behaviour.
|
|
||||||
std::cerr << "SegmentationFault signal raised" << std::endl;
|
|
||||||
std::abort(); // This will continue into our SIGABRT handler.
|
|
||||||
});
|
|
||||||
SignalHandler::register_handler(Signal::Abort, []() {
|
|
||||||
// Log the stacktrace and let the abort continue.
|
|
||||||
Stacktrace stacktrace;
|
|
||||||
std::cerr << "Abort signal raised" << std::endl
|
|
||||||
<< stacktrace.dump() << std::endl;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize bolt session data (GraphDb and Interpreter).
|
// Initialize bolt session data (GraphDb and Interpreter).
|
||||||
SessionData session_data;
|
SessionData session_data;
|
||||||
|
|
||||||
@ -85,17 +79,30 @@ int main(int argc, char **argv) {
|
|||||||
// Initialize server.
|
// Initialize server.
|
||||||
ServerT server(endpoint, session_data);
|
ServerT server(endpoint, session_data);
|
||||||
|
|
||||||
|
// Handler for regular termination signals
|
||||||
auto shutdown = [&server, &session_data]() {
|
auto shutdown = [&server, &session_data]() {
|
||||||
|
if (is_shutting_down) return;
|
||||||
|
is_shutting_down = 1;
|
||||||
// Server needs to be shutdown first and then the database. This prevents a
|
// Server needs to be shutdown first and then the database. This prevents a
|
||||||
// race condition when a transaction is accepted during server shutdown.
|
// race condition when a transaction is accepted during server shutdown.
|
||||||
server.Shutdown();
|
server.Shutdown();
|
||||||
session_data.db.Shutdown();
|
session_data.db.Shutdown();
|
||||||
};
|
};
|
||||||
// register SIGTERM handler
|
|
||||||
SignalHandler::register_handler(Signal::Terminate, shutdown);
|
|
||||||
|
|
||||||
// register SIGINT handler
|
// Prevent handling shutdown inside a shutdown. For example, SIGINT handler
|
||||||
SignalHandler::register_handler(Signal::Interupt, shutdown);
|
// being interrupted by SIGTERM before is_shutting_down is set, thus causing
|
||||||
|
// double shutdown.
|
||||||
|
sigset_t block_shutdown_signals;
|
||||||
|
sigemptyset(&block_shutdown_signals);
|
||||||
|
sigaddset(&block_shutdown_signals, SIGTERM);
|
||||||
|
sigaddset(&block_shutdown_signals, SIGINT);
|
||||||
|
|
||||||
|
CHECK(SignalHandler::RegisterHandler(Signal::Terminate, shutdown,
|
||||||
|
block_shutdown_signals))
|
||||||
|
<< "Unable to register SIGTERM handler!";
|
||||||
|
CHECK(SignalHandler::RegisterHandler(Signal::Interupt, shutdown,
|
||||||
|
block_shutdown_signals))
|
||||||
|
<< "Unable to register SIGINT handler!";
|
||||||
|
|
||||||
// Start memory warning logger.
|
// Start memory warning logger.
|
||||||
Scheduler mem_log_scheduler;
|
Scheduler mem_log_scheduler;
|
||||||
|
@ -23,22 +23,30 @@ class SignalHandler {
|
|||||||
private:
|
private:
|
||||||
static std::map<int, std::function<void()>> handlers_;
|
static std::map<int, std::function<void()>> handlers_;
|
||||||
|
|
||||||
static void handle(int signal) { handlers_[signal](); }
|
static void Handle(int signal) { handlers_[signal](); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void register_handler(Signal signal, Function func) {
|
/// Install a signal handler.
|
||||||
int signal_number = static_cast<int>(signal);
|
static bool RegisterHandler(Signal signal, Function func) {
|
||||||
handlers_[signal_number] = func;
|
sigset_t signal_mask;
|
||||||
std::signal(signal_number, SignalHandler::handle);
|
sigemptyset(&signal_mask);
|
||||||
|
return RegisterHandler(signal, func, signal_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO possible changes if signelton needed later
|
/// Like RegisterHandler, but takes a `signal_mask` argument for blocking
|
||||||
/*
|
/// signals during execution of the handler. `signal_mask` should be created
|
||||||
static SignalHandler& instance() {
|
/// using `sigemptyset` and `sigaddset` functions from `<signal.h>`.
|
||||||
static SignalHandler instance;
|
static bool RegisterHandler(Signal signal, Function func,
|
||||||
return instance;
|
sigset_t signal_mask) {
|
||||||
}
|
int signal_number = static_cast<int>(signal);
|
||||||
*/
|
handlers_[signal_number] = func;
|
||||||
|
struct sigaction action;
|
||||||
|
action.sa_handler = SignalHandler::Handle;
|
||||||
|
action.sa_mask = signal_mask;
|
||||||
|
action.sa_flags = SA_RESTART;
|
||||||
|
if (sigaction(signal_number, &action, NULL) == -1) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::map<int, std::function<void()>> SignalHandler::handlers_ = {};
|
std::map<int, std::function<void()>> SignalHandler::handlers_ = {};
|
||||||
|
@ -89,13 +89,13 @@ int main(int argc, char **argv) {
|
|||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
|
||||||
// Signal handling init.
|
// Signal handling init.
|
||||||
SignalHandler::register_handler(Signal::SegmentationFault, []() {
|
SignalHandler::RegisterHandler(Signal::SegmentationFault, []() {
|
||||||
// Log that we got SIGSEGV and abort the program, because returning from
|
// Log that we got SIGSEGV and abort the program, because returning from
|
||||||
// SIGSEGV handler is undefined behaviour.
|
// SIGSEGV handler is undefined behaviour.
|
||||||
std::cerr << "SegmentationFault signal raised" << std::endl;
|
std::cerr << "SegmentationFault signal raised" << std::endl;
|
||||||
std::abort(); // This will continue into our SIGABRT handler.
|
std::abort(); // This will continue into our SIGABRT handler.
|
||||||
});
|
});
|
||||||
SignalHandler::register_handler(Signal::Abort, []() {
|
SignalHandler::RegisterHandler(Signal::Abort, []() {
|
||||||
// Log the stacktrace and let the abort continue.
|
// Log the stacktrace and let the abort continue.
|
||||||
Stacktrace stacktrace;
|
Stacktrace stacktrace;
|
||||||
std::cerr << "Abort signal raised" << std::endl
|
std::cerr << "Abort signal raised" << std::endl
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include "utils/stacktrace.hpp"
|
#include "utils/stacktrace.hpp"
|
||||||
|
|
||||||
TEST(SignalHandler, SegmentationFaultTest) {
|
TEST(SignalHandler, SegmentationFaultTest) {
|
||||||
SignalHandler::register_handler(Signal::SegmentationFault, []() {
|
SignalHandler::RegisterHandler(Signal::SegmentationFault, []() {
|
||||||
std::cout << "Segmentation Fault" << std::endl;
|
std::cout << "Segmentation Fault" << std::endl;
|
||||||
Stacktrace stacktrace;
|
Stacktrace stacktrace;
|
||||||
std::cout << stacktrace.dump() << std::endl;
|
std::cout << stacktrace.dump() << std::endl;
|
||||||
|
Loading…
Reference in New Issue
Block a user