coder
coder

Reputation: 73

boost::asio signal_set handler only executes after first signal is caught and ignores consecutive signals of the same type

I have a program and would like to stop it by sending SIGINT for writing some data to a file instead of exiting immediately. However, if the user of the program sends SIGINT again, then the program should quit immediately and forget about writing data to a file.

For portability reason I would like to use boost::asio for this purpose.

My initial (simplified) approach (see below) did not work. Is this not possible or am I missing something?

The handler seems to be called only once (printing out the message) and the program always stops when the loop has reached the max iteration number.

void handler(
const boost::system::error_code& error,
int signal_number) {
    if (!error) {
        static bool first = true;
        if(first) {
            std::cout << " A signal(SIGINT) occurred." << std::endl;
            // do something like writing data to a file
            first = false;
        }
        else {
            std::cout << " A signal(SIGINT) occurred, exiting...." << std::endl;
            exit(0);
        }
    }
}

int main() {
    // Construct a signal set registered for process termination.
    boost::asio::io_service io;
    boost::asio::signal_set signals(io, SIGINT);

    // Start an asynchronous wait for one of the signals to occur.
    signals.async_wait(handler);
    io.run();
    size_t i;

    for(i=0;i<std::numeric_limits<size_t>::max();++i){
        // time stepping loop, do some computations
    }

    std::cout << i << std::endl;
    return 0;
}

Upvotes: 6

Views: 6271

Answers (1)

sehe
sehe

Reputation: 393134

When your first event is handled, you don't post any new work on the service object, so it exits.

This means that then (after the ioservice exited) the tight loop is started. This may not be what you expected.

  1. If you want to listen for SIGINT again, you have to wait for the signal set again from the handler:

    #include <boost/asio.hpp>
    #include <boost/asio/signal_set.hpp>
    #include <boost/bind.hpp>
    #include <boost/atomic.hpp>
    #include <iostream>
    
    void handler(boost::asio::signal_set& this_, boost::system::error_code error, int signal_number) {
        if (!error) {
            static boost::atomic_bool first(true);
            if(first) {
                // do something like writing data to a file
                std::cout << " A signal(SIGINT) occurred." << std::endl;
                first = false;
    
                this_.async_wait(boost::bind(handler, boost::ref(this_), _1, _2));
            }
            else {
                std::cout << " A second signal(SIGINT) occurred, exiting...." << std::endl;
                exit(1);
            }
        }
    }
    
    int main() {
        // Construct a signal set registered for process termination.
        boost::asio::io_service io;
        boost::asio::signal_set signals(io, SIGINT);
    
        // Start an asynchronous wait for one of the signals to occur.
        signals.async_wait(boost::bind(handler, boost::ref(signals), _1, _2));
        io.run();
        return 2;
    }
    

    As you can see I bound the signal_set& reference to the handler in order to be able to async_wait on it after receiving the first signal. Also, as a matter of principle, I made first an atomic (although that's not necessary until you run the io_service on multiple threads).

  2. Did you actually wish to run the io_service in the background? In that case, make it look like so:

    signals.async_wait(boost::bind(handler, boost::ref(signals), _1, _2));
    
    boost::thread(boost::bind(&boost::asio::io_service::run, boost::ref(io))).detach();
    
    while (true)
    {
        std::cout << "Some work on the main thread...\n";
        boost::this_thread::sleep_for(boost::chrono::seconds(1));
    }
    

    With typical output:

    Some work on the main thread...
    Some work on the main thread...
    Some work on the main thread...
    ^CSome work on the main thread...
     A signal(SIGINT) occurred.
    Some work on the main thread...
    Some work on the main thread...
    ^CSome work on the main thread...
     A second signal(SIGINT) occurred, exiting....
    

Upvotes: 6

Related Questions