POSIX Timers: Additional Information (siginfo_t)#

siginfo_t: A Bag Of Further Information#

  • Look into man -s 2 sigaction

  • That structure contains a whole lot of information across all signals (for example, SIGSEGV populates a number of hardware specific fields)

  • What remains for our purposes: union sigval containing either an int or a void*

  • Specified in timer_create(..., struct sigevent*, ...)

  • Echoed back in signal handler

    • sigaction: SA_SIGINFO

    • Together with a different handler

#include <signal.h>

siginfo_t {
    ...
    union sigval si_value;
    ...
};

union sigval {
    int             sival_int;
    void           *sival_ptr;
};

Code: Pass Additional Expiry Information#

#include <signal.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <print>

static void handler(int signal, 
                    siginfo_t *info,                   // <-- signal *parameter*
                    void *ucontext)                    // <-- unused
{
    char msg[64] = {0};
    sprintf(msg, "signal handler: %d\n", info->si_value.sival_int);
    write(STDOUT_FILENO, msg, strlen(msg));
}

int main()
{
    timer_t timer;
    sigevent event = {0};
    event.sigev_notify = SIGEV_SIGNAL;
    event.sigev_signo = SIGRTMIN;
    event.sigev_value.sival_int = 42;                  // <-- additional info to be echoed back at expiration

    int rv = timer_create(CLOCK_MONOTONIC, &event, &timer);
    if (rv == -1) {
        perror("timer_create");
        return 1;
    }

    struct sigaction sa = { 0 };
    sa.sa_flags = SA_SIGINFO;                          // <-- request additional info with signal
    sa.sa_sigaction = handler;                         // <-- different handler!

    rv = sigaction(SIGRTMIN, &sa, nullptr);
    if (rv == -1) {
        perror("sigaction");
        return 1;
    }

    struct itimerspec sp = {
        .it_interval = {.tv_sec=0, .tv_nsec=0},
        .it_value    = {.tv_sec=3, .tv_nsec=0},
    };
    rv = timer_settime(timer, 0 /*relative*/,
                       &sp,
                       nullptr);
    if (rv == -1) {
        perror("timer_settime");
        return 1;
    }

    rv = pause();
    if (rv == -1) {
        if (errno == EINTR)
            std::println("interrupted");
        else {
            perror("pause");
            return 1;
        }
    }

    return 0;
}

Use Case: Multiple Timers, Single Signal Handler#

  • Can create more timers than signals available

  • ⟶ Use one signal for multiple timers

#include <signal.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <print>
#include <iostream>

static timer_t timer1, timer2;                         // <-- global (to share with handler)

static void handler(int signal, siginfo_t* info, void* ctx)
{
    const timer_t* timer = (const timer_t*)info->si_value.sival_ptr;
    const char* msg;
    if (timer == &timer1)                              // <-- passed via event1 below
        msg = "timer1 expired\n";
    else if (timer == &timer2)                         // <-- passed via event2 below
        msg = "timer2 expired\n";
    else
        abort();

   write(STDOUT_FILENO, msg, strlen(msg));
}

int main()
{
    /* timer creation */                               // <-- create two timers
    sigevent event1 = {0};
    event1.sigev_notify = SIGEV_SIGNAL;
    event1.sigev_signo = SIGRTMIN;
    event1.sigev_value.sival_ptr = &timer1;            // <-- pass timer1 into handler

    int rv = timer_create(CLOCK_MONOTONIC, &event1, &timer1);
    if (rv == -1) {
        perror("timer_create (1)");
        return 1;
    }

    sigevent event2 = {0};
    event2.sigev_notify = SIGEV_SIGNAL;
    event2.sigev_signo = SIGRTMIN;
    event2.sigev_value.sival_ptr = &timer2;            // <-- pass timer2 into handler

    rv = timer_create(CLOCK_MONOTONIC, &event2, &timer2);
    if (rv == -1) {
        perror("timer_create (2)");
        return 1;
    }

    /* timer signal handling */ 
    struct sigaction sa = { 0 };
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = handler;

    rv = sigaction(SIGRTMIN, &sa, nullptr);
    if (rv == -1) {
        perror("sigaction");
        return 1;
    }

    /* timer1 activation */
    struct itimerspec sp1 = {
        {1,0},                                         // <-- 1s period
        {3,0},                                         // <-- initial expiration in 3s
    };
    rv = timer_settime(timer1, 0 /*relative*/,
                       &sp1,
                       nullptr);
    if (rv == -1) {
        perror("timer_settime (1)");
        return 1;
    }

    /* timer2 activation */
    struct itimerspec sp2 = {
        {1,500*1000*1000},                             // <-- 1.5s period
        {1,0},                                         // <-- initial expiration in 1s
    };
    rv = timer_settime(timer2, 0 /*relative*/,
                       &sp2,
                       nullptr);
    if (rv == -1) {
        perror("timer_settime (2)");
        return 1;
    }

    while (true) {
        rv = pause();
        if (rv == -1) { 
            if (errno != EINTR) {
                perror("pause");
                return 1;
            }
        }
    }

    return 0;
}