[1.] One line summary of the problem:
poll() timeout always takes 10ms too long
[2.] Full description of the problem/report:
Select() timeouts work fine. A timeout between 10n-9 and 10n ms times
out after 10n ms on average. Poll() timeouts between 10n-9 and 10n ms,
on the other hand, time out after 10(n+1) ms on average. It's always a
jiffy too long. This means it's impossible to set a 10ms timeout using
poll() even though it's possible using select(). The programs and their
output below [6] demonstrate this. The same behavious occurs with
linux-2.2 and linux-2.4.
[3.] Keywords (i.e., modules, networking, kernel):
poll, select, timer, timeout
[4.] Kernel version (from /proc/version):
$ cat /proc/version
Linux version 2.4.0 ([EMAIL PROTECTED]) (gcc version 2.95.2 19991024 (release)) #16
Sat Jan 20 07:45:58 EST 2001
[5.] Output of Oops.. message (if applicable) with symbolic information
resolved (see Documentation/oops-tracing.txt)
N/A
[6.] A small shell script or example program which triggers the
problem (if possible)
--- select.c --
#include
#include
#include
#include
#include
void timeval_diff(struct timeval *start, struct timeval *end, struct timeval *diff)
{
diff->tv_sec = end->tv_sec - start->tv_sec;
if (end->tv_usec < start->tv_usec)
diff->tv_usec = 100 + end->tv_usec - start->tv_usec,
--diff->tv_sec;
else
diff->tv_usec = end->tv_usec - start->tv_usec;
}
double time_select(int msec)
{
struct timeval timeout[1], start[1], end[1], elapsed[1];
timeout->tv_sec = 0;
timeout->tv_usec = msec * 1000;
gettimeofday(start, NULL);
select(1, NULL, NULL, NULL, timeout);
gettimeofday(end, NULL);
timeval_diff(start, end, elapsed);
return ((double)elapsed->tv_sec * 100.0 + (double)elapsed->tv_usec) / 1000;
}
void test_select(int msec)
{
double min = DBL_MAX;
double max = DBL_MIN;
double sum = 0.0;
int i;
for (i = 0; i < 1000; ++i)
{
double elapsed = time_select(msec);
if (elapsed < min)
min = elapsed;
if (elapsed > max)
max = elapsed;
sum += elapsed;
}
printf("select(%d ms) min %g ms, max %g ms, avg %g ms\n", msec, min, max, sum
/ 1000);
}
int main(int ac, char **av)
{
int msec = av[1] ? atoi(av[1]) : 1;
test_select(msec);
return EXIT_SUCCESS;
}
---
--- poll.c
#include
#include
#include
#include
#include
void timeval_diff(struct timeval *start, struct timeval *end, struct timeval *diff)
{
diff->tv_sec = end->tv_sec - start->tv_sec;
if (end->tv_usec < start->tv_usec)
diff->tv_usec = 100 + end->tv_usec - start->tv_usec,
--diff->tv_sec;
else
diff->tv_usec = end->tv_usec - start->tv_usec;
}
double time_poll(int msec)
{
struct timeval start[1], end[1], elapsed[1];
gettimeofday(start, NULL);
poll(NULL, 0, msec);
gettimeofday(end, NULL);
timeval_diff(start, end, elapsed);
return ((double)elapsed->tv_sec * 100.0 + (double)elapsed->tv_usec) / 1000;
}
void test_poll(int msec)
{
double min = DBL_MAX;
double max = DBL_MIN;
double sum = 0.0;
int i;
for (i = 0; i < 1000; ++i)
{
double elapsed = time_poll(msec);
if (elapsed < min)
min = elapsed;
if (elapsed > max)
max = elapsed;
sum += elapsed;
}
printf("poll(%d ms) min %g ms, max %g ms, avg %g ms\n", msec, min, max, sum /
1000);
}
int main(int ac, char **av)
{
int msec = (av[1]) ? atoi(av[1]) : 1;
test_poll(msec);
return EXIT_SUCCESS;
}
---
--- select-output -
$ for i in 1 5 9 10 11 15 19 20 21 25 29 30 31 35 39 40 41 45 49 50 51 1000
do
./select $i
done
select(1 ms) min 5.624 ms, max 10.299 ms, avg 9.99298 ms
select(5 ms) min 5.668 ms, max 10.357 ms, avg 9.99301 ms
select(9 ms) min 5.635 ms, max 10.034 ms, avg 9.993 ms
select(10 ms) min 5.683 ms, max 10.347 ms, avg 9.99306 ms
select(11 ms) min 15.663 ms, max 20.627 ms, avg 19.993 ms
select(15 ms) min 15.664 ms, max 20.331 ms, avg 19.993 ms
select(19 ms) min 15.632 ms, max 20.04 ms, avg 19.993 ms
select(20 ms) min 15.652 ms, max 20.029 ms, avg 19.993 ms
select(21 ms) min 25.661 ms, max 30.299 ms, avg 29.993 ms
select(25 ms) min 25.663 ms, max 30.085 ms, avg 29.993 ms
sele