From c13fcab060a9243199154058105b8663f85ba89e Mon Sep 17 00:00:00 2001 From: Michael Kerrisk Date: Mon, 13 Jun 2005 09:01:49 +0000 Subject: [PATCH] Salut Olivier (and Nishanth), Regarding man page documentation of the problem of short sleeps for setiteimer(2)... > > -- pointers to those threads > > http://bugzilla.kernel.org/show_bug.cgi?id=4569 > http://lkml.org/lkml/2005/4/29/163 > > > -- indications of which kernel versions show this bahaviour > > AFAIK, all versions as far as x86 is concerned. > Dunno if it is hardware specific. > > > -- a (short) test program to demonstrate it, if you have one. > > See the bugzilla bug's attachments Sorry for the long delay in following this up, but I've got to it now. I tweaked your suggestions slightly: {{ Timers will never expire before the requested time, -instead expiring some short, constant time afterwards, dependent -on the system timer resolution (currently 10ms). +but may expire some (short) time afterwards, which depends +on the system timer resolution and on the system load. +Upon expiration, a signal will be generated and the timer reset. +If the timer expires while the process is active (always true for +On certain systems (including x86), the Linux kernel has a bug which will +produce premature timer expirations of up to one jiffy under some +circumstances. }} Thanks for this bug reporet, Nishanth: if and when your changes are accepted, and the problem is thus fixed, could you please send me a notification of that fact, and I can then further amend the manual pages. Cheers, Michael /* itimer_short_interval_bug.c June 2005 In current Linux kernels, an interval timer set using setitimer() can sometimes sleep *less* than the specified interval. This program demonstrates the behaviour by looping through all itimer values from 1 microsecond upwards, in one microsecond steps. */ /* Adapted from a program by Olivier Croquette, June 2005 */ #include #include #include #include #include #include typedef unsigned long long int u_time_t; /* in microsecs */ static int handler_flag; /* return time as a number of microsecs */ static u_time_t gettime(void ) { struct timeval tv; if ( gettimeofday(&tv, NULL) == -1) { perror("gettimeofday()"); return 0; } return (tv.tv_usec + tv.tv_sec * 1000000LL); } static void handler (int sig, siginfo_t *siginfo, void *context) { handler_flag++; return ; } /* Sleep for 'time' microsecs. */ static int isleep(u_time_t time) { struct itimerval newtv; sigset_t sigset; struct sigaction sigact; if (time == 0) return 0; /* block SIGALRM */ sigemptyset (&sigset); sigaddset (&sigset, SIGALRM); sigprocmask (SIG_BLOCK, &sigset, NULL); /* set up our handler */ sigact.sa_sigaction = handler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_SIGINFO; sigaction (SIGALRM, &sigact, NULL); newtv.it_interval.tv_sec = 0; newtv.it_interval.tv_usec = 0; newtv.it_value.tv_sec = time / 1000000; newtv.it_value.tv_usec = time % 1000000; if (setitimer(ITIMER_REAL,&newtv,NULL) == -1) { perror("setitimer(set)"); return 1; } sigemptyset (&sigset); sigsuspend (&sigset); return 0; } int main(int argc, char *argv[]) { u_time_t wait; int loop, numLoops; u_time_t t1, t2; u_time_t actual; long long minDiff, maxDiff, totDiff, diff; int numFail = 0; if (argc != 2) { fprintf(stderr, "Usage: %s num-loops\n", argv[0]); exit(EXIT_FAILURE); } /* if */ numLoops = atoi(argv[1]); setbuf(stdout, NULL); for (wait = 1; ; wait++) { maxDiff = 0; numFail = 0; totDiff = 0; minDiff = -wait; if (wait % 10000 == 0) printf("%llu\n", wait); for (loop = 0; loop < numLoops; loop++) { t1 = gettime(); handler_flag = 0; isleep(wait); if ( handler_flag != 1 ) printf("Problem with the handler flag (%d)!\n", handler_flag); t2 = gettime(); actual = t2 - t1; if ( actual < wait ) { diff = actual - wait; if (diff < maxDiff) maxDiff = diff; if (diff > minDiff) minDiff = diff; totDiff += diff; numFail++; } /* if */ } /* for */ if (numFail > 0) printf("%llu: %3d fail (%4lld %4lld; avg=%6.1f)\n", wait, numFail, minDiff, maxDiff, (double) totDiff / numFail); } /* for */ return 0; } /* main */ --- man2/getitimer.2 | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/man2/getitimer.2 b/man2/getitimer.2 index 38e5d399b..e966c8a1a 100644 --- a/man2/getitimer.2 +++ b/man2/getitimer.2 @@ -53,6 +53,7 @@ Timer values are defined by the following structures: .PD 0 .RS .5i .nf + struct itimerval { struct timeval it_interval; /* next value */ struct timeval it_value; /* current value */ @@ -107,10 +108,10 @@ and are significant in determining the duration of a timer. .LP Timers will never expire before the requested time, -instead expiring some short, constant time afterwards, dependent -on the system timer resolution (currently 10ms). Upon expiration, a -signal will be generated and the timer reset. If the timer expires -while the process is active (always true for +but may expire some (short) time afterwards, which depends +on the system timer resolution and on the system load. +Upon expiration, a signal will be generated and the timer reset. +If the timer expires while the process is active (always true for .BR ITIMER_VIRT ) the signal will be delivered immediately when generated. Otherwise the delivery will be offset by a small time dependent on the system loading. @@ -141,11 +142,13 @@ POSIX.1-2001, SVr4, 4.4BSD (this call first appeared in 4.2BSD). .BR sigaction (2), .BR signal (2) .SH BUGS -Under Linux, the generation and delivery of a signal are distinct, and -there each signal is permitted only one outstanding event. It's therefore -conceivable that under pathologically heavy loading, +The generation and delivery of a signal are distinct, and +only one instance of each of the signals listed above may be pending +for a process. +Under very heavy loading, an .B ITIMER_REAL -will expire before the signal from a previous expiration has been delivered. +timer may expire before the signal from a previous expiration +has been delivered. The second signal in such an event will be lost. On Linux, timer values are represented in jiffies. @@ -158,6 +161,11 @@ On Linux 2.6 on x86 (where a jiffy is 0.001 seconds), this means that the ceiling value for a timer is approximately 24.86 days. +On certain systems (including x86), the Linux kernel has a bug which will +produce premature timer expirations of up to one jiffy under some +circumstances. +.\" As at June 2005, the above holds in 2.4.x and 2.6.c (e.g., 2.6.12.) + POSIX.1 says that .B setitimer should fail if a