Reputation: 486
Here is a program that uses a POSIX per-process timer alongside the sleep()
subroutine. The signal sent when the timer expires has been set to SIGUSR1
rather than SIGALRM
, since SIGALRM
may be used internally by sleep()
, but it still doesn't seem to work.
I have run the program using the command line timer-overruns -d 1 -n 10000000
(1 cs interval) so, in theory, we should expect 100 overruns between calls to sigwaitinfo()
. However, timer_getoverrun()
returns 0.
I have also tried a version using a time-consuming for
loop to introduce the delay. In this case, overruns are recorded.
Does anyone know why this happens? I am running a 3.4 Linux kernel.
/*
* timer-overruns.c
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
// Signal to be used for timer expirations
#define TIMER_SIGNAL SIGUSR1
int main(int argc, char **argv) {
int opt;
int d = 0;
int r = 0; // Repeat indefinitely
struct itimerspec its;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
// Parse arguments
while ((opt = getopt(argc, argv, "d:r:s:n:")) != -1) {
switch (opt) {
case 'd': // Delay before calling sigwaitinfo()
d = atoi(optarg);
break;
case 'r': // Number of times to call sigwaitinfo()
r = atoi(optarg);
break;
case 's': // Timer interval (seconds)
its.it_interval.tv_sec = its.it_value.tv_sec = atoi(optarg);
break;
case 'n': // Timer interval (nanoseconds)
its.it_interval.tv_nsec = its.it_value.tv_nsec = atoi(optarg);
break;
default: /* '?' */
fprintf(stderr,
"Usage: %s [-d signal_accept_delay] [-r repetitions] [-s interval_seconds] [-n interval_nanoseconds]\n",
argv[0]);
exit(EXIT_FAILURE);
}
}
// Check sanity of command line arguments
short e = 0;
if (d < 0) {
fprintf(stderr, "Delay (-d) cannot be negative!\n");
e++;
}
if (r < 0) {
fprintf(stderr, "Number of repetitions (-r) cannot be negative!\n");
e++;
}
if (its.it_interval.tv_sec < 0) {
fprintf(stderr, "Interval seconds value (-s) cannot be negative!\n");
e++;
}
if (its.it_interval.tv_nsec < 0) {
fprintf(stderr, "Interval nanoseconds value (-n) cannot be negative!\n");
e++;
}
if (its.it_interval.tv_nsec > 999999999) {
fprintf(stderr, "Interval nanoseconds value (-n) must be < 1 second.\n");
e++;
}
if (e > 0)
exit(EXIT_FAILURE);
// Set default values if not specified
if (its.it_interval.tv_sec == 0 && its.it_interval.tv_nsec == 0) {
its.it_interval.tv_sec = its.it_value.tv_sec = 1;
its.it_value.tv_nsec = 0;
}
printf("Running with timer delay %d.%09d seconds\n",
(int) its.it_interval.tv_sec, (int) its.it_interval.tv_nsec);
// Will be waiting for signals synchronously, so block the one in use.
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, TIMER_SIGNAL);
sigprocmask(SIG_BLOCK, &sigset, NULL );
// Create and arm the timer
struct sigevent sev;
timer_t timer;
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = TIMER_SIGNAL;
sev.sigev_value.sival_ptr = timer;
timer_create(CLOCK_REALTIME, &sev, &timer);
timer_settime(timer, TIMER_ABSTIME, &its, NULL );
// Signal handling loop
int overruns;
siginfo_t si;
// Make the loop infinite if r = 0
if (r == 0)
r = -1;
while (r != 0) {
// Sleeping should cause overruns
if (d > 0)
sleep(d);
sigwaitinfo(&sigset, &si);
// Check that the signal is from the timer
if (si.si_code != SI_TIMER)
continue;
overruns = timer_getoverrun(timer);
if (overruns > 0) {
printf("Timer overrun occurred for %d expirations.\n", overruns);
}
// Decrement r if not repeating indefinitely
if (r > 0)
r--;
}
return EXIT_SUCCESS;
}
Upvotes: 2
Views: 944
Reputation: 39386
You fail to set its.it_value.tv_sec
when only nanoseconds are set, i.e. -n 10000000
but no -s 0
. Properly initialize its.it_value
, or use -s 0
parameter, and your code should work -- after a fashion.
Due to sigemptyset(&sigset); sigaddset(&sigset, TIMER_SIGNAL); sigprocmask(SIG_BLOCK, &sigset, NULL);
I do not think there is a race condition, sigwaitinfo()
should reliably obtain the pending signal. Check the sigwaitinfo() man page for details; it does describe this very same use case in the Notes section.
It seems that the value returned by timer_getoverrun() the first time after the timer fires, is bogus, at least on a 3.5.0 kernel on an x86-64. In other words, adding this just before the loop,
printf("Overruns: %d\n",
timer_getoverrun(timer));
sigwaitinfo(&sigset, &si);
printf("Overruns: %d (%d)\n",
timer_getoverrun(timer), si.si_overrun);
sigwaitinfo(&sigset, &si);
printf("Overruns: %d (%d)\n",
timer_getoverrun(timer), si.si_overrun);
will output zero, a seemingly random overrun count, then the expected overrun count. (Both timer and siginfo overrun counts do match.) I suspect there is a problem in kernel/posix-timers.c, but I might be wrong.. the problem seems to be that the first timer event will use a random value for the previous overrun count. I think I'll investigate that further.
I would appreciate it a lot if you could tell me whether you see the same on your kernel. (Please also state the exact version and architecture. I'm running Ubuntu linux-image-3.5.0-3-generic, version 3.5.0-3.3, on amd64 (x86-64).
Upvotes: 1