Event Loop Support: signalfd()#

Basic Operation#

#include <sys/signalfd.h>

int signalfd(int fd, const sigset_t *mask, int flags);
  • Signals received as events, via file descriptor

    • ⟶ Perfect fit for event loop programming

  • If fd == -1, a new file descriptor is created

  • If fd >= 0 (another signal FD), its mask is replaced with the supplied mask

  • flags: SFD_NONBLOCK, SFD_CLOEXEC

  • read() on the fd into an array of signalfd_siginfo structures

  • Best to choose array size according to number of handled signals ⟶ read all possible pending signals in one swoop

    • Different for realtime signals: these are queued

Example: A Better sigwaitinfo()#

  • Show normal operation

  • Suspend process

  • Demonstrate how SIGUSR1 and SIGUSR2 are received together with one call to read()

  • Two instances of say SIGUSR1 overwrite each other (standard signals vs. realtime signals)

#include <unistd.h>
#include <sys/signalfd.h>
#include <signal.h>
#include <print>

int main()
{
    std::println("PID={}", getpid());

    sigset_t interest;
    sigemptyset(&interest);
    sigaddset(&interest, SIGTERM);
    sigaddset(&interest, SIGINT);
    sigaddset(&interest, SIGUSR1);
    sigaddset(&interest, SIGUSR2);

    int rv = sigprocmask(SIG_BLOCK, &interest, nullptr);
    if (rv == -1) {
        perror("sigprocmask");
        return 1;
    }

    int fd = signalfd(-1, &interest, 0);               // <-- new fd, no flags

    bool quit = false;
    while (!quit) {
        signalfd_siginfo signals[4];                   // <-- we handle 4 different signals
        ssize_t nread = read(fd,                       // <-- read max 4 signals
                             signals, sizeof(signals));
        if (nread == -1) {
            perror("read");
            return 1;
        }

        for (size_t i=0; i<sizeof(signals)/sizeof(signalfd_siginfo); i++) {
            switch (signals[i].ssi_signo) {            // <-- disregard other structure members
                case SIGTERM:
                case SIGINT:
                    std::println("terminating");
                    quit = true;
                    break;
                case SIGUSR1:
                    std::println("doing this");
                    break;
                case SIGUSR2:
                    std::println("doing that");
                    break;
            }
        }
    }
    return 0;
}