From 76497403e0ac0c4b285c1a86b5b488e6a98f1ae4 Mon Sep 17 00:00:00 2001 From: "Mark Landis (N6AZX)" Date: Sun, 1 Jan 2023 16:53:57 -0800 Subject: [PATCH] Use signals for program termination This commit is the first in a small series to improve the shutdown behavior of xlxd. (1) Ensures worker threads block signals by masking all signals prior to the main reflector startup via CReflector::Start(). (2) Uses sigwaitinfo() in the main thread to wait on a few normal termination signals. This works for both daemon and non-daemon operation. --- src/main.cpp | 67 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 723ab5b..017af80 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,7 @@ #include "creflector.h" #include "syslog.h" +#include #include @@ -39,6 +40,37 @@ CReflector g_Reflector; #include "cusers.h" +// Returns caught termination signal or -1 on error +static int wait_for_termination() +{ + sigset_t waitset; + siginfo_t siginfo; + + sigemptyset(&waitset); + sigaddset(&waitset, SIGTERM); + sigaddset(&waitset, SIGINT); + sigaddset(&waitset, SIGQUIT); + sigaddset(&waitset, SIGHUP); + pthread_sigmask(SIG_BLOCK, &waitset, nullptr); + + // Now wait for termination signal + int result = -1; + while (result < 0) + { + result = sigwaitinfo(&waitset, &siginfo); + if (result == -1 && errno == EINTR) + { + // try again + if (errno == EINTR) + continue; + + // an unexpected error occurred, consider it fatal + break; + } + } + return result; +} + int main(int argc, const char * argv[]) { #ifdef RUN_AS_DAEMON @@ -101,37 +133,30 @@ int main(int argc, const char * argv[]) g_Reflector.SetCallsign(argv[1]); g_Reflector.SetListenIp(CIp(argv[2])); g_Reflector.SetTranscoderIp(CIp(CIp(argv[3]))); - + + // Block all signals while starting up the reflector -- we don't + // want any of the worker threads handling them. + sigset_t sigblockall, sigorig; + sigfillset(&sigblockall); + pthread_sigmask(SIG_SETMASK, &sigblockall, &sigorig); + // and let it run if ( !g_Reflector.Start() ) { std::cout << "Error starting reflector" << std::endl; exit(EXIT_FAILURE); } + + // Restore main thread default signal state + pthread_sigmask(SIG_SETMASK, &sigorig, nullptr); + std::cout << "Reflector " << g_Reflector.GetCallsign() << "started and listening on " << g_Reflector.GetListenIp() << std::endl; -#ifdef RUN_AS_DAEMON - // run forever - while ( true ) - { - // sleep 60 seconds - CTimePoint::TaskSleepFor(60000); - } -#else - // wait any key - for (;;) - { - // sleep 60 seconds - CTimePoint::TaskSleepFor(60000); -#ifdef DEBUG_DUMPFILE - g_Reflector.m_DebugFile.close(); -#endif - } -#endif - // and wait for end - g_Reflector.Stop(); + wait_for_termination(); + + g_Reflector->Stop(); std::cout << "Reflector stopped" << std::endl; // done