.. _program_listing_file_src_utils_SigtermGuard.hpp: Program Listing for File SigtermGuard.hpp ========================================= |exhale_lsh| :ref:`Return to documentation for file ` (``src/utils/SigtermGuard.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #ifndef SIGTERMGUARD_HPP #define SIGTERMGUARD_HPP #include #include #include 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