Search code examples
csignalsposixinfinite-loopsigint

Re-registering signal handler causes infinite loop


With the following signal handler, pressing Ctrl-C causes the handler to be called repetitively, and I don't know why:

(Warning: if you type Ctrl-C, you'll have to use kill <pid> to exit the program)

static void handler(int sig) {
        // do something interesting

        // write so I can see if I'm in an infinite loop
        write(1, "A", 1);

        // raise normal signal behavior
        // this ensures normal signal behavior
        // for example, exiting with code 130 if Ctrl-C is used
        signal(sig, SIG_DFL);
        raise(sig);

        // re-register handler
        // that way, if signal doesn't exit the program and the signal
        //   is sent again in the future, this handler will run again
        signal(sig, handler);
}

So I know that I don't understand something about signal handlers. I assumed that only SIGCONT would ever make it to the last line, because SIGINT (for example), when it is raised with the default behavior (SIG_DFL) should exit and not return.

I also know I could wrap the last line in an if statement that only does it for SIGCONT, but I really want to know why it isn't behaving the way I expect.

Question(s)

  1. Why does the signal handler get called in an infinite loop?
  2. How can I have a handler that does something interesting, calls the default behavior, and then re-registers itself in case the default behavior doesn't exit the program?

MCVE

This can be run by writing it to a file sigtest.c and doing make sigtest. Use Ctrl-C to raise SIGINT, and in another terminal, be prepared to do ps | grep sigtest and then kill <pid>.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void handler(int sig) {
        // do something interesting

        // write so I can see if I'm in an infinite loop
        write(1, "A", 1);

        // raise normal signal behavior
        // this ensures normal signal behavior
        // for example, exiting with code 130 if Ctrl-C is used
        signal(sig, SIG_DFL);
        raise(sig);

        // re-register handler
        // that way, if signal doesn't exit the program and the signal
        //   is sent again in the future, this handler will run again
        signal(sig, handler);
}

int main(void) {
        char c;
        signal(SIGINT, handler);
        signal(SIGTSTP, handler);
        signal(SIGCONT, handler);
        printf("type 'x' and 'Enter' to exit\n");
        while (1) {
                c = getchar();
                printf("got char: %c\n", c);
                if (c == 'x') { break; }
        }
}

Solution

  • I blanked and forgot that a signal is blocked while it is being handled. Which means that SIGINT wouldnt' actually be raised until after the handler returns, and after I've set the action back from SIG_DFL to my custom handler. Therefore, it is looped.

    A working handler for my example looks like:

    static void handler(int sig) {
            if (sig == SIGCONT) {
                    // do something interesting
                    signal(sig, handler);
                    signal(SIGTSTP, handler);
            } else {
                    // do something else interesting
                    signal(sig, SIG_DFL);
                    raise(sig);
            }
    }