4

In C++11, what is the safest (and perferrably most efficient) way to execute unsafe code on a signal being caught, given a type of request-loop (as part of a web request loop)? For example, on catching a SIGUSR1 from a linux command line: kill -30 <process pid>

It is acceptable for the 'unsafe code' to be run on the next request being fired, and no information is lost if the signal is fired multiple times before the unsafe code is run.

For example, my current code is:

static bool globalFlag = false;

void signalHandler(int sig_num, siginfo_t * info, void * context) {
    globalFlag = true;
}
void doUnsafeThings() {
    // thigns like std::vector push_back, new char[1024], set global vars, etc.
}
void doRegularThings() {
    // read filesystem, read global variables, etc.
}

void main(void) {

    // set up signal handler (for SIGUSR1) ...
    struct sigaction sigact;
    sigact.sa_sigaction = onSyncSignal;
    sigact.sa_flags = SA_RESTART | SA_SIGINFO;
    sigaction(SIGUSR1, &sigact, (struct sigaction *)NULL);

    // main loop ...
    while(acceptMoreRequests()) { // blocks until new request received
        if (globalFlag) {
            globalFlag = false;
            doUnsafeThings();
        }
        doRegularThings();
    }
}

where I know there could be problems in the main loop testing+setting the globalFlag boolean.

Edit: The if (globalFlag) test will be run in a fairly tight loop, and an 'occasional' false negative is acceptable. However, I suspect there's no optimisation over Basile Starynkevitch's solution anyway?

patmanpato
  • 823
  • 1
  • 11
  • 15
  • I don't think that `c++11` knows about signals. It is an operating system thing (so Posix or Linux). – Basile Starynkevitch Sep 20 '13 at 01:38
  • @BasileStarynkevitch very true. I just tagged this as c++11 in case there are some cleaner workarounds (for managing the flag) specific to c++11. Seems the problem had been solved in POSIX long ago :-) – patmanpato Sep 20 '13 at 02:17
  • You should explain what `acceptMoreRequests` does. Where are you calling `poll` (or the nearly obsolete `select`) to multiplex? – Basile Starynkevitch Sep 20 '13 at 05:39

1 Answers1

9

You should declare your flag

  static volatile sig_atomic_t globalFlag = 0;

See e.g. sig_atomic_t, this question and don't forget the volatile qualifier. (It may have been spelled sigatomic_t for C).

On Linux (specifically) you could use signalfd(2) to get a filedescriptor for the signal, and that fd can be poll(2)-ed by your event loop.

Some event loop libraries (libevent, libev ...) know how to handle signals.

And there is also the trick of setting up a pipe (see pipe(2) and pipe(7) for more) at initialization, and just write(2)-ing some byte on it in the signal handler. The event loop would poll and read that pipe. Such a trick is recommended by Qt.

Read also signal(7) and signal-safety(7) (it explains what are the limited set of functions or syscalls usable inside a signal handler)....

BTW, correctness is more important than efficiency. In general, you get few signals (e.g. most programs get a signal once every second at most, not every millisecond).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • The 'efficiency' requirement was because the flag will be tested in a fairly tight loop, set only occasionally (as you mentioned) in the signal handler. But you are right, generally correctness > performance, except in my case an occasional false negative is acceptable. – patmanpato Sep 20 '13 at 02:20