diff --git a/fpsdk_common/include/fpsdk_common/app.hpp b/fpsdk_common/include/fpsdk_common/app.hpp index e811308..44a300c 100644 --- a/fpsdk_common/include/fpsdk_common/app.hpp +++ b/fpsdk_common/include/fpsdk_common/app.hpp @@ -92,10 +92,48 @@ class SigIntHelper bool WaitAbort(const uint32_t millis = 0); }; +/** + * @brief Helper to catch SIGTERM + * + * On construction this installs a handler for SIGTERM. On destruction it sets the handler back to its previous state. + * Note that signal handlers are global and therefore you can only use one SigTermHelper in a app. + */ +class SigTermHelper +{ + public: + /** + * @brief Constructor + * + * @param[in] warn Print a WARNING() (true, default) or a DEBUG() (false) on signal + */ + SigTermHelper(const bool warn = true); + + /** + * @brief Destructor + */ + ~SigTermHelper(); + + /** + * @brief Check if signal was raised and we should abort + * + * @returns true if signal was raised and we should abort, false otherwise + */ + bool ShouldAbort(); + + /** + * @brief Wait (block) until signal is raised and we should abort + * + * @param[in] millis Wait at most this long [ms], 0 = forever + * + * @returns true if the signal was raised, false if timeout expired + */ + bool WaitAbort(const uint32_t millis = 0); +}; + /** * @brief Helper to catch SIGPIPE * - * On construction this installs a handler for SIGPIE. On destruction it sets the handler back to its previous state. + * On construction this installs a handler for SIGPIPE. On destruction it sets the handler back to its previous state. * Note that signal handlers are global and therefore you can only use one SigPipeHelper in a app. */ class SigPipeHelper diff --git a/fpsdk_common/src/app.cpp b/fpsdk_common/src/app.cpp index 111df31..ad5f272 100644 --- a/fpsdk_common/src/app.cpp +++ b/fpsdk_common/src/app.cpp @@ -94,6 +94,67 @@ bool SigIntHelper::WaitAbort(const uint32_t millis) // --------------------------------------------------------------------------------------------------------------------- +static bool g_sigterm_abort = false; +static bool g_sigterm_warn = false; +static sighandler_t g_sigterm_old_handler = SIG_IGN; +static fpsdk::common::thread::BinarySemaphore g_sigterm_sem; + +static void SigTermHandler(int signum) +{ + if ((signum == SIGTERM) && !g_sigterm_abort) { + if (g_sigterm_warn) { + WARNING("Caught SIGTERM, aborting..."); + } else { + DEBUG("Caught SIGTERM, aborting..."); + } + g_sigterm_abort = true; + + // Handle signal only once, next time let the original handler deal with it + std::signal(SIGTERM, g_sigterm_old_handler == SIG_IGN ? SIG_DFL : g_sigterm_old_handler); + g_sigterm_old_handler = SIG_IGN; + + g_sigterm_sem.Notify(); + } +} + +SigTermHelper::SigTermHelper(const bool warn) +{ + if (g_sigterm_old_handler == SIG_IGN) { + g_sigterm_old_handler = std::signal(SIGTERM, SigTermHandler); + g_sigterm_warn = warn; + } +} + +SigTermHelper::~SigTermHelper() +{ + std::signal(SIGTERM, g_sigterm_old_handler == SIG_IGN ? SIG_DFL : g_sigterm_old_handler); + g_sigterm_sem.Notify(); +} + +bool SigTermHelper::ShouldAbort() +{ + return g_sigterm_abort; +} + +bool SigTermHelper::WaitAbort(const uint32_t millis) +{ + // Wait with timeout + if (millis > 0) { + return g_sigterm_sem.WaitFor(millis) == thread::WaitRes::WOKEN; + } + // Wait forever + else { + while (true) { + if (g_sigterm_sem.WaitFor(1234) == thread::WaitRes::WOKEN) { + return true; + } + } + } + return false; +} + +// --------------------------------------------------------------------------------------------------------------------- + static bool g_sigpipe_raised = false; static bool g_sigpipe_warn = false; static sighandler_t g_sigpipe_old_handler = SIG_IGN;