Program Listing for File SigtermGuard.hpp
↰ Return to documentation for file (src/utils/SigtermGuard.hpp)
#ifndef SIGTERMGUARD_HPP
#define SIGTERMGUARD_HPP
#include <csignal>
#include <stdexcept>
#include <string>
namespace utils
{
// Set (only) by signalHandler() below; read by sigtermWasReceived().
// volatile sig_atomic_t is the one type the C++ standard guarantees
// safe to write from inside a signal handler without risking
// undefined behavior -- but that guarantee is about signal-handler
// safety on whichever single thread the signal happened to be
// delivered to (POSIX picks one, not necessarily this process's
// main thread -- see SigtermGuard's own comment for why that
// doesn't matter here), not about the value becoming visible to
// every OpenMP worker thread's own read of it: that cross-thread
// visibility is what sigtermWasReceived()'s own "#pragma omp
// atomic read" actually provides. inline (a C++17 inline variable)
// so SimCluster::run() and SimGalaxy::run() -- the only two
// callers, never both running at once within a single process (see
// SigtermGuard's own comment) -- share exactly one instance of
// this flag, matching how a signal disposition installed via
// std::signal() is itself process-wide, not per-translation-unit.
inline volatile std::sig_atomic_t sigtermReceived = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -- a signal handler can only communicate through global/static state; see this variable's own comment
inline void signalHandler(int /*signum*/)
{
sigtermReceived = 1;
}
inline auto sigtermWasReceived() -> bool
{
std::sig_atomic_t value = 0;
#ifdef _OPENMP
#pragma omp atomic read
#endif
value = sigtermReceived;
return value != 0;
}
class SigtermGuard
{
public:
explicit SigtermGuard(const std::string& installErrorMessage)
{
// Reset the flag before installing the handler, not after
// (and so deliberately not via a member initializer for
// previousHandler_, despite what clang-tidy's
// cppcoreguidelines-prefer-member-initializer suggests --
// that would run the install first): a SIGTERM arriving
// between the two would otherwise have this immediately
// clear the very flag it just set, silently losing the
// signal.
sigtermReceived = 0;
previousHandler_ = std::signal(SIGTERM, signalHandler); // NOLINT(cppcoreguidelines-prefer-member-initializer)
if (previousHandler_ == SIG_ERR)
{
throw std::runtime_error(installErrorMessage);
}
}
~SigtermGuard()
{
// Best-effort restore -- this runs from a destructor, so
// it must not throw, and there is nothing more useful to
// do with a failure here anyway
(void)std::signal(SIGTERM, previousHandler_); // NOLINT(cert-err33-c)
}
SigtermGuard(const SigtermGuard&) = delete;
auto operator=(const SigtermGuard&) -> SigtermGuard& = delete;
SigtermGuard(SigtermGuard&&) = delete;
auto operator=(SigtermGuard&&) -> SigtermGuard& = delete;
private:
using Handler = void (*)(int);
Handler previousHandler_ = nullptr;
};
} // namespace utils
#endif // SIGTERMGUARD_HPP