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 notificationNo 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;
}