Timer File Descriptors#

#include <sys/timerfd.h>

int timerfd_create(int clockid, int flags);

int timerfd_settime(int fd, int flags,
                    const struct itimerspec *new_value,
                    struct itimerspec *_Nullable old_value);
int timerfd_gettime(int fd, struct itimerspec *curr_value);

The Idea#

  • File descriptors emit events

    • Incoming data ready

    • Signal is pending (see here)

    • Edge interrupt detected on IO pin

  • Timer expiration is an event too

  • ⟶ What if we could represent a timer by a file descriptor?

Timer Events: Far Superior#

Timer expiration delivery options, according to POSIX:

  • Signals are not a solution to anything

  • Threads might solve problems but are still hard

  • ⟶ This is not creative!

  • Event driven programming

    • Not simple, mentally (state machines all over)

    • Technically far superior

creation#

  • no callback specification (struct sigevent)

  • -> no callbacks

  • event multiplexing Group sysprog.eventloop

  • flags: “nonblocking”, “cloexec”

oneshot#

#include <sys/timerfd.h>
#include <stdio.h>
#include <unistd.h>
#include <print>

int main()
{
    /* timer creation */                               // <-- this replaces 20+ lines from POSIX timers
    int timer = timerfd_create(CLOCK_MONOTONIC, 0);
    if (timer == -1) {
        perror("timerfd_create");
        return 1;
    }

    struct itimerspec sp = {
        .it_interval = {.tv_sec=0, .tv_nsec=0},
        .it_value    = {.tv_sec=3, .tv_nsec=0},
    };
    int rv = timerfd_settime(timer, 0 /*relative*/,        // <-- almost exactly the same
                             &sp,
                             nullptr);
    if (rv == -1) {
        perror("timerfd_settime");
        return 1;
    }

    uint64_t num_expirations;
    ssize_t nread = read(timer, &num_expirations, sizeof(num_expirations));
    if (nread == -1) {
        perror("read");
        return 1;
    }
    if (nread != sizeof(num_expirations)) {
        std::println(stderr, "well, something's severely wrong");
        return 1;
    }

    std::println("#expirations: {}", num_expirations);

    return 0;
}

periodic#

#include <sys/timerfd.h>
#include <stdio.h>
#include <unistd.h>
#include <print>

int main()
{
    int timer = timerfd_create(CLOCK_MONOTONIC, 0);
    if (timer == -1) {
        perror("timerfd_create");
        return 1;
    }

    struct itimerspec sp = {
        {1,0},                                         // <-- 1 second interval
        {3,0},
    };
    int rv = timerfd_settime(timer, 0 /*relative*/,
                             &sp,
                             nullptr);
    if (rv == -1) {
        perror("timerfd_settime");
        return 1;
    }

    while (true) {
        uint64_t num_expirations;
        ssize_t nread = read(timer, &num_expirations, sizeof(num_expirations));
        if (nread == -1) {
            perror("read");
            return 1;
        }
        if (nread != sizeof(num_expirations)) {
            std::println(stderr, "well, something's severely wrong");
            return 1;
        }

        std::println("#expirations: {}", num_expirations);
    }

    return 0;
}

closing words#