POSIX Timers: Arming (Oneshot/Periodic)#

To show how to setup (“arm”) oneshot and periodic timers, this section uses signals as a timer expiration delivery mechanism. There is also a “threaded” delivery, this will be covered in POSIX Timers: Threaded Notification. The mechanism how timer are armed stays the same, no matter what delivery option is used.

Basics#

  • POSIX timers: timer_create() (and timer_delete(), mostly unused in examples)

  • Highly configurable (via struct sigevent)

  • Choose signal to use

    • Signals that are used for timer expiry notification are never queued - not even realtime signals (see Realtime Signals)

event.sigev_notify = SIGEV_SIGNAL;       // <-- notification type (SIGEV_THREAD?)
event.sigev_signo = SIGRTMIN;            // <-- send SIGRTMIN at expiry

Arming: struct itimerspec#

#include <time.h>

struct timespec {
    time_t     tv_sec;   /* Seconds */
    /* ... */  tv_nsec;  /* Nanoseconds [0, 999'999'999] */
};

struct itimerspec {
    struct timespec  it_interval;  /* Interval for periodic timer */
    struct timespec  it_value;     /* Initial expiration */
};
  • it_interval: periodic timer

  • {0,0} for one-shot timer

Code: One-shot Timers#

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

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

int main()
{
    /* timer creation */
    timer_t timer;                                     // <-- timer handle
    sigevent event = {0};
    event.sigev_notify = SIGEV_SIGNAL;                 // <-- expiry by signal ...
    event.sigev_signo = SIGRTMIN;                      // <-- ... namely SIGRTMIN

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

    /* timer signal handling */                        // <-- (nothing new)
    struct sigaction sa = { 0 };
    sa.sa_handler = handler;

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

    /* timer activation */
    struct itimerspec sp = {
        .it_interval = {.tv_sec=0, .tv_nsec=0},        // <-- zero interval -> one-shot
        .it_value    = {.tv_sec=3, .tv_nsec=0},        // <-- expire after {sec,nsec}
    };
    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;
}

Code: Periodic Timers#

#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 creation */
    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;
    }

    /* timer signal handling */ 
    struct sigaction sa = { 0 };
    sa.sa_handler = handler;

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

    /* timer activation */
    struct itimerspec sp = {
        {1,0},                                         // <-- periodic interval
        {3,0},                                         // <-- first expiration
    };
    rv = timer_settime(timer, 0 /*relative*/,
                       &sp,
                       nullptr);
    if (rv == -1) {
        perror("timer_settime");
        return 1;
    }

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

    return 0;
}