Search code examples
c++boostboost-asio

Using boost::asio::signal_set with SIGTERM or SIGSEGV


There are examples for the signal_set to catch SIGTERM or SIGINT. Is it considered safe to also register SIGABRT or even SIGSEGV? Because the signal_set needs an io_context I'm not sure if that context is still valid when SIGSEGV happened.

Also I'm not sure about registering SIGTERM or SIGINT with the signal_set and SIGSEGV with normal std::signal. The documentation states that multiple registering of signals is not possible. Is it forbidden to call std::signal after any invocation of signal_set?


Solution

  • You correctly hold the intuition that SEGV cannot be recovered from. At the very least, the program cannot resume (best case, attempts to do so will result in repeated SEGV).

    You also correctly noted that you should not be meddling with the POSIX signals interface directly if you're using Asio's support already.

    All the rest is pretty well-defined:

    • having multiple signal-sets
    • handling many more than 2 signals per set
    • handling signals multiple times in different sets
    • awaiting the same set multiple times

    Here's demo that forces a SEGV 6s in:

    Live On Coliru

    #include <boost/asio.hpp>
    namespace asio = boost::asio;
    using namespace std::chrono_literals;
    using std::this_thread::sleep_for;
    
    struct Demo {
        Demo(asio::any_io_executor ex, char const* name) : set(ex), name(name) {
            for (int num : {SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGHUP, SIGPIPE, SIGSEGV})
                set.add(num);
            wait_loop();
        }
    
      private:
        asio::signal_set set;
        char const*      name;
    
        using error_code = boost::system::error_code;
    
      public:
        void wait_loop() {
            set.async_wait([this](error_code ec, int num) {
                printf("%6s signal %s (%s)\n", name, ec.message().c_str(), ::strsignal(num));
                sleep_for(200ms);
    
                if (num != SIGTERM)
                    wait_loop();
            });
        }
    };
    
    int main() {
        asio::thread_pool p;
    
        auto ex = p.get_executor();
        Demo first(ex, "first");
        Demo second(ex, "second");
    
        second.wait_loop(); // doubly await the second set for good measure
    
        sleep_for(6s);
        static void (*force_segv)() = nullptr;
        force_segv();
    
        p.join();
    }
    

    With interactive live demo:

    enter image description here