Subject: | glibc ualarm on Linux won't accept usec values outside 0-999999 |
Date: | Thu, 03 Apr 2008 17:49:24 -0400 |
To: | bug-Time-HiRes [...] rt.cpan.org |
From: | "William J. Horka" <whorka [...] hmdc.harvard.edu> |
The perl Time::HiRes module alarm() function takes a time value in
seconds and sets an alarm by calling the glibc ualarm() function and
passing it the time value * 1000000 (microseconds). This results in
warnings being generated by the syscall setitimer() which is called by
glibc ualarm().
The call from Time::HiRes::alarm() to glibc ualarm() is contrary to the
documented [non-]functionality of glibc ualarm() in two ways, as noted
in the ualarm manpage:
1)
ERRORS
EINVAL usecs or interval is not smaller than 1000000. (On
systems where that is considered an error.)
2)
NOTES
This function is obsolete. Use nanosleep(2) or setitimer(2) instead.
The source of the problem is that the linux kernel did not used to
consider usecs or interval >= 1000000 to be an error in a call to
setitimer(). This behavior changed as noted in
linux-2.6.18/Documentation/feature-removal-schedule.txt:
What: Usage of invalid timevals in setitimer
When: March 2007
Why: POSIX requires to validate timevals in the setitimer call. This
was never done by Linux. The invalid (e.g. negative timevals) were
silently converted to more or less random timeouts and intervals.
Until the removal a per boot limited number of warnings is printed
and the timevals are sanitized.
Who: Thomas Gleixner <tglx@linutronix.de>
This is also noted in the setitimer() manpage:
BUGS
POSIX.1-2001 says that setitimer() should fail if a tv_usec
value is specified that is outside of the range 0 to
999999. However, Linux does not give an error, but instead
silently adjusts the corresponding seconds value for the
timer. In the future (scheduled for March 2007), this
non-conformance will be repaired: existing applications should
be fixed now to ensure that they supply a properly formed
tv_usec value.
In kernel 2.6.18 from September 2006 (the default kernel for RHEL5),
kernel/itimer.c contains code to sanitize arguments to setitimer(), and
will print out warnings (10 per boot) when it receives invalid
arguments. In the latest kernel (2.6.24.4 from March 2008) this code has
been replaced by code that will return an error when invalid arguments
are passed to setitimer().
This problem was apparently addressed in Time::HiRes 1.91, but the
change that was made appears to address the symptom rather than the
source of the problem. The Changes file notes:
1.91 [2006-09-29]
- ualarm() in SuSE 10.1 was overflowing after ~4.2 seconds,
possibly due to a glibc bug/feature (suspected overflow at
2**32 microseconds?), workaround by using the setitimer()
implementation of ualarm() if either useconds or
interval > 999_999 (this case seems to vary between systems:
are useconds more than 999_999 for ualarm() defined or not)
Added more ualarm() tests to catch various overflow points,
hopefully no problems in various platforms.
(The problem report by Mark Seger and Jon Paul Sullivan of HP.)
However, the alarm() function in Time::HiRes 1.9712 still contains the
bug where it tries to pass a value of seconds * 1000000 to ualarm(), so
any nonzero value for seconds will be result in the argument to ualarm()
being out of bounds.
Proposed fix: Per the ualarm() manpage, don't pass it values outside the
range of 0 to 999999 since glibc ualarm() passes the value to
setitimer() which requires a value in this range according to
POSIX.1-2001 and the Linux kernel now considers a value outside this
range to be an error. Use setitimer() instead.
--
William Horka
UNIX Systems Administrator
Harvard-MIT Data Center