Question

My signal handler for signals SIGTERM and SIGINT causes a poll error when receiving either one. I am not sure why it does that and I think I am unaware of some behavior of sigaction that I have missed.

while(!signalHandler.gotExitSignal()) {
    switch (poll(&ufds[0], NUM_FDS, POLL_TIMEOUT)) {
        case -1: {
            throw std::runtime_error("poll error (-1)"); /* ABORT */
        }
        .
        .

My signalHandler is a class which contains the sigaction handling.

struct sigaction killAction, termAction;

memset(&killAction, 0, sizeof(struct sigaction));
killAction.sa_handler = SignalHandler::ExitHandler;
sigemptyset(&killAction.sa_mask);
killAction.sa_flags = 0;
if(sigaction(SIGTERM, &killAction, NULL) < 0)
    throw SignalException("sigaction failed for killAction");

memset(&termAction, 0, sizeof(struct sigaction));
termAction.sa_handler = SignalHandler::ExitHandler;
sigemptyset(&termAction.sa_mask);
termAction.sa_flags = 0;
if(sigaction(SIGTERM, &termAction, NULL) < 0)
    throw SignalException("sigaction failed for termAction");

So my expected behavior of the snippet loop I gave should be:

  1. while(!signalHandler.gotExitSignal()) {
  2. switch (poll(&ufds[0], NUM_FDS, POLL_TIMEOUT)) {
  3. After polling do some cleanup stuff
  4. Goto 1
  5. while(!signalHandler.gotExitSignal()) {
  6. INTERRUPT
  7. switch (poll(&ufds[0], NUM_FDS, POLL_TIMEOUT)) {
  8. After polling do some cleanup stuff
  9. Goto 1
  10. Break loop

What is happening:

  1. while(!signalHandler.gotExitSignal()) {
  2. switch (poll(&ufds[0], NUM_FDS, POLL_TIMEOUT)) {
  3. After polling do some cleanup stuff
  4. Goto 1
  5. while(!signalHandler.gotExitSignal()) {
  6. INTERRUPT
  7. throw std::runtime_error("poll error (-1)");

I tried a scenario where after the poll switch I slept for 2seconds and then terminated the program - it exited gracefully with the loop condition. The problem exists while I poll and then receive a signal. I don't use the SIGHUP signal, which is used for telling a program to reload its config, but I setup a signal for SIGHUP to test and it terminated my program with the poll error like my other signals. But I can't have my program crashing on any signal, I'm wondering if I am forgetting something.

Was it helpful?

Solution

This is expected. While blocking in poll() and a signal arrives, poll will fail and errno will be set to EINTR. You will get the same behavior for most system calls.

You can and should detect this (you now break out of the poll loop sooner too, as poll() will return when a signal is caught.)

while(!signalHandler.gotExitSignal()) {
    switch (poll(&ufds[0], NUM_FDS, POLL_TIMEOUT)) {
        case -1: {
           if (errno == EINTR) { 
                continue;
           }
           throw std::runtime_error("poll error (-1)"); /* ABORT */
        }

You would most likely want to set the SA_RESTART flag for sigaction too.

 killAction.sa_flags = SA_RESTART;

This will cause most system calls to NOT return an error when a signal is caught. Read the manpage for sigaction() for more info, and read the "Interruption of system calls and library functions by signal handlers" section in the man 7 signal.

Notably, poll() will still be interrupted(return an error immediately) by a signal regardless of setting the SA_RESTART flag.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top