1

I have to handle SIGINT and SIGTERM in my program that uses Boost.Asio. I use boost::asio::signal_set::async_wait() for this.

The problem is that the signal handler gets the control only when I simply run the app but does not when I debug it.

Here is some code:

Proxy::Proxy():
    signals_(ioContext_, SIGINT, SIGTERM)
{
    signals_.async_wait(
        [this](const boost::system::error_code& error, int)
        {
            if (!error)
                ioContext_.stop();
        }
    );
}

Proxy::run()
{
    ioContext_.run();
}

When we run() the Proxy, ioContext_ starts processing events. If we just simply run the program and do Ctrl+C in terminal, the signal handler (that is lambda) stops ioContext_ (as we expect) and io_context::run gives the control back. But in debug mode the program reacts to Ctrl+C but the execution stops somewhere in epoll_wait(). If we continue the execution, it hangs on somewhere in epoll_wait() and so on.

Here is a stack trace of where the execution stops:

epoll_wait
boost::asio::detail::epoll_reactor::run
boost::asio::detail::scheduler::do_one_run
boost::asio::detail::scheduler::run
boost::asio::io_context::run
Proxy::run
main

Why does it happen in debug mode but does not otherwise?

1 Answers1

4

The issue here is GDB uses SIGINT as the mechanism to interrupt the program and allow you to start your debugging.

(gdb) info signals SIGINT
Signal        Stop      Print   Pass to program Description
SIGINT        Yes       Yes     No              Interrupt

This is saying that GDB should not pass SIGINTs to the program, but should use that to stop the program and drop you into the GDB prompt. The easiest mechanism to send it onward to your program is to send the signal from GDB at this point:

(gdb) signal SIGINT

Now your program should continue on its way as expected.


Depending on how often you do this, typing signal SIGINT might get inconvenient. Luckily, GDB lets you modify the way it handles signals. You want SIGINT to not stop the program (drop you into the GDB prompt) and pass it on to the program.

(gdb) handle SIGINT nostop pass
SIGINT is used by the debugger.
Are you sure you want to change it? (y or n) y
Signal        Stop      Print   Pass to program Description
SIGINT        No        Yes     Yes             Interrupt

We are now in the land of "slightly inadvisable," in that we can no longer use Ctrl+C to jump into our GDB prompt. You'll have to rely on preset breakpoints and other mechanisms.

If you want to get more advanced, you can use catch and commands to determine the source of the SIGINT (lifted from Debugging a program that uses SIGINT with gdb):

catch signal SIGINT
commands
  if $_siginfo._sifields._kill.si_pid == 0
    print "Received SIGINT from tty"
  else
    printf "Received SIGINT from %d; continuing\n", $_siginfo._sifields._kill.si_pid
    signal SIGINT
  end
end
Travis Gockel
  • 26,877
  • 14
  • 89
  • 116