POSIX Timers: Overrun#

Timer Overrun#

  • Timers may expire without the user noticing

    • Scheduling gives no guarantees ⟶ short-interval timers may expire multiple times before user notices

    • Timer signal may be blocked

  • At timer expiration, the user can query the “timer overrun count”

  • Important: only valid at timer expiration (in the signal handler or thread function)

  • Not valid during normal program flow

Code: Timer Overrun, Wrong#

  • Nanosecond timer

  • Signal (SIGRTMIN) blocked ⟶ no notification

  • No overrun detected after 3 seconds

  • Wrong

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

static void handler(int signal)
{
    static const char msg[] = "timer expired\n";
    write(STDOUT_FILENO, msg, sizeof(msg));
}

int main()
{
    timer_t timer;
    sigevent event = {0};
    event.sigev_notify = SIGEV_SIGNAL;
    event.sigev_signo = SIGRTMIN;

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

    struct sigaction sa = { 0 };
    sa.sa_handler = handler;

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

    struct itimerspec sp = {
        {0,1},                                         // <-- 1 nanosecond periodic
        {1,0},                                         // <-- start in one second
    };
    rv = timer_settime(timer, 0 /*relative*/,
                       &sp,
                       nullptr);
    if (rv == -1) {
        perror("timer_settime");
        return 1;
    }

    sigset_t blocked;
    sigemptyset(&blocked);
    sigaddset(&blocked, SIGRTMIN);
    rv = sigprocmask(SIG_BLOCK, &blocked, nullptr);    // <-- block SIGRTMIN
    if (rv == -1) {
        perror("sigprocmask");
        return 1;
    }

    sleep(3);                                          // <-- give timer a chance to overrun

    int overrun_count = timer_getoverrun(timer);
    if (overrun_count == -1) {
        perror("timer_getoverrun");
        return 1;
    }
    else
        std::println("{} overruns", overrun_count);

    return 0;
}

Code: Timer Overrun, Right#

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

static timer_t timer;

static void handler(int signal)
{
    int overrun_count = timer_getoverrun(timer);
    if (overrun_count == -1) {
        perror("timer_getoverrun");
        exit(1);
    }
    else
        std::println("{} overruns", overrun_count);    // <-- undefined behavior: not async signal safe
}

int main()
{
    sigevent event = {0};
    event.sigev_notify = SIGEV_SIGNAL;
    event.sigev_signo = SIGRTMIN;

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

    struct sigaction sa = { 0 };
    sa.sa_handler = handler;

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

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

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

    return 0;
}

Code: Timer Overrun, Linux Specific#

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

static void handler(int signal, siginfo_t* info,       // <-- SA_SIGINFO handler
                    void* ctx)
{
    std::println("{} overruns", info->si_overrun);
}

int main()
{
    timer_t timer;

    sigevent event = {0};
    event.sigev_notify = SIGEV_SIGNAL;
    event.sigev_signo = SIGRTMIN;

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

    struct sigaction sa = { 0 };
    sa.sa_sigaction = handler;                         // <-- SA_SIGINFO handler
    sa.sa_flags = SA_SIGINFO;                          // <-- 

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

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

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

    return 0;
}