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#
-
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:
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#
Be aware that there is a limit to open files which is much lower than the limit to POSIX timers