timer_getoverrun()
SIGEV_THREAD
si_overrun
What Is a Timer Overrun?
A timer overrun happens when a periodic timer expires multiple times before its notification signal is caught or the notification thread is invoked.
Example: your timer fires every 100ms. If your process is preempted for 350ms, the timer has expired 3 times but you only receive 1 signal. The overrun count is 2 (the extra 2 expirations).
T=100ms
T=200ms
T=300ms
T=350ms
#define _POSIX_C_SOURCE 199309
#include <time.h>
/* Returns the overrun count for the specified timer.
Async-signal-safe β safe to call from signal handler.
Returns: count (0 if no overruns), or -1 on error.
Overrun count = extra expirations since last signal.
If timer expired 5 times but 1 signal was delivered,
overrun count = 4. */
int timer_getoverrun(timer_t timerid);
/* Linux extension: si_overrun field in siginfo_t
Available in signal handler when SA_SIGINFO is set:
int overrun = si->si_overrun;
(saves a syscall, but non-portable) */
Instead of delivering a signal, the kernel invokes a user function in a new thread on each timer expiration. This avoids signal-safety restrictions.
- No async-signal restrictions
- Can call any function (printf, mallocβ¦)
- Thread gets sigval data
- Thread creation overhead per expiry
- Need mutex to protect shared state
- Requires linking with -lpthread
/* Setting up SIGEV_THREAD notification */
struct sigevent sev;
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = my_thread_func; /* Called on expiry */
sev.sigev_notify_attributes = NULL; /* Or pthread_attr_t* */
sev.sigev_value.sival_ptr = &timerid; /* Passed to function */
/* Thread function signature: */
void my_thread_func(union sigval sv) {
timer_t *tid = sv.sival_ptr;
printf("Timer %ld expired, overrun=%d\n",
(long)*tid, timer_getoverrun(*tid));
}
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
static timer_t timerid;
static int total_expirations = 0;
void timer_handler(int sig, siginfo_t *si, void *uc) {
(void)sig; (void)uc;
int overrun = timer_getoverrun(timerid);
/* Also available as si->si_overrun (Linux extension) */
total_expirations += 1 + overrun; /* This delivery + overruns */
printf("Signal received: +1 delivery, +%d overruns (total=%d)\n",
overrun, total_expirations);
}
int main(void) {
struct sigaction sa;
struct sigevent sev;
struct itimerspec ts;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_handler;
sigemptyset(&sa.sa_mask);
sigaction(SIGRTMIN, &sa, NULL);
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_ptr = &timerid;
timer_create(CLOCK_REALTIME, &sev, &timerid);
/* 200ms interval timer */
ts.it_value.tv_sec = 0;
ts.it_value.tv_nsec = 200000000L;
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 200000000L;
timer_settime(timerid, 0, &ts, NULL);
printf("Timer running at 200ms. Sleeping 1s to accumulate overruns...\n");
/* Block signals for 1 second to force overruns */
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGRTMIN);
sigprocmask(SIG_BLOCK, &mask, NULL);
sleep(1); /* Timer fires ~5 times, all queued */
/* Unblock β handler called once, overrun=4 */
sigprocmask(SIG_UNBLOCK, &mask, NULL);
pause(); /* Wait for the one deferred signal */
timer_delete(timerid);
printf("Total expirations accounted for: %d\n", total_expirations);
return 0;
}
/* Expected output:
Timer running at 200ms. Sleeping 1s to accumulate overruns...
Signal received: +1 delivery, +4 overruns (total=5)
Total expirations accounted for: 5
*/
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
static timer_t timerid;
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int total = 0;
/* This function runs in a NEW THREAD on each expiry */
void thread_notify(union sigval sv) {
int overrun = timer_getoverrun(*(timer_t *)sv.sival_ptr);
pthread_mutex_lock(&mtx);
total += 1 + overrun;
printf("[Thread] Timer expired! overrun=%d, total=%d\n",
overrun, total);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mtx);
}
int main(void) {
struct sigevent sev;
struct itimerspec ts;
/* SIGEV_THREAD setup */
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = thread_notify;
sev.sigev_notify_attributes = NULL; /* Use default thread attrs */
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
perror("timer_create"); return 1;
}
/* Arm: 500ms first, 500ms interval */
ts.it_value.tv_sec = 0;
ts.it_value.tv_nsec = 500000000L;
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 500000000L;
timer_settime(timerid, 0, &ts, NULL);
/* Main thread waits for 5 expirations */
pthread_mutex_lock(&mtx);
while (total < 5) {
pthread_cond_wait(&cond, &mtx);
printf("[Main] total so far: %d\n", total);
}
pthread_mutex_unlock(&mtx);
timer_delete(timerid);
printf("Done. Total expirations: %d\n", total);
return 0;
}
/* Compile: gcc -o sigev_thread sigev_thread.c -lrt -lpthread
No signal handling needed β thread function is called directly! */
Access overrun count directly from siginfo_t without a syscall.
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
static timer_t g_tid;
static int done = 0;
void handler(int sig, siginfo_t *si, void *uc) {
(void)sig; (void)uc;
/* Portable way: timer_getoverrun() */
int overrun_portable = timer_getoverrun(g_tid);
/* Linux-specific: si_overrun directly in siginfo_t
(saves the syscall overhead) */
int overrun_linux = si->si_overrun;
printf("si_code=%d (SI_TIMER=%d), si_overrun=%d, "
"timer_getoverrun()=%d\n",
si->si_code, SI_TIMER,
overrun_linux, overrun_portable);
/* si_value holds what we set in sev.sigev_value */
printf("si_value.sival_ptr = %p (timer addr = %p)\n",
si->si_value.sival_ptr, (void *)&g_tid);
done = 1;
}
int main(void) {
struct sigaction sa;
struct sigevent sev;
struct itimerspec ts;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
sigaction(SIGRTMIN, &sa, NULL);
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_ptr = &g_tid;
timer_create(CLOCK_REALTIME, &sev, &g_tid);
/* One-shot 500ms timer */
ts.it_value.tv_sec = 0;
ts.it_value.tv_nsec = 500000000L;
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 0;
timer_settime(g_tid, 0, &ts, NULL);
while (!done) pause();
timer_delete(g_tid);
return 0;
}
/* Output:
si_code=SI_TIMER (==SI_TIMER), si_overrun=0, timer_getoverrun()=0
si_value.sival_ptr = 0x... (timer addr = 0x...)
*/
Extra timer expirations that occurred between the time the notification signal was generated and when it was handled. If timer expires 5 times but 1 signal is delivered, overrun count = 4.
There are system limits on queued realtime signals. The kernel guarantees at most one instance per timer, using the overrun count instead of queuing multiple copies.
SIGEV_SIGNAL delivers a signal to the process (restricted async-signal context). SIGEV_THREAD invokes a function in a new thread (full C runtime available β can call printf, malloc, etc.).
total += 1 + timer_getoverrun(timerid) β count 1 for the delivery itself plus the overrun count for missed ones.
Both -lrt (POSIX timers) and -lpthread (POSIX threads) are required.
