Tolerant and high-resolution timers
You can use timer tolerance to specify how strictly the kernel should enforce a timer's expiry time.
Tolerance | Effect |
---|---|
0 | Use the process's default tolerance (see procmgr_timer_tolerance()). If the process's default tolerance is 0, use the default tolerance of 1 tick (see ClockPeriod()). |
Greater than 0 and less than one tick | The timer is considered to be a high-resolution timer; the kernel adjusts the hardware timer so that the clock tick interrupt occurs at the requested tolerance |
Greater than or equal to one tick | The kernel uses the expiry time plus the tolerance to decide if and for how long
it should enter tickless operation or a low-power mode; see
Clocks, timers, and power managementin this chapter |
Amounts beyond any value the current time can have (e.g., ~0ULL) | Use infinite tolerance; a CLOCK_SOFTTIME timer is really a timer with infinite tolerance |
After creating the timer by calling timer_create() or TimerCreate(), you can set the tolerance by specifying the TIMER_TOLERANCE flag when you call timer_settime() or TimerSettime(). Call one of these functions again (without TIMER_TOLERANCE) to set the expiry time and start the timer. The tolerance persists until you set it again or destroy the timer.
Timer tolerance also applies to the timeouts that you can use for kernel calls that block. You can set the tolerance by specifying the TIMER_TOLERANCE flag when you call timer_timeout() or TimerTimeout(). Call one of these functions again (without TIMER_TOLERANCE) to start the timeout. In this case, the tolerance is used only for the current timeout.
- The tolerance may lengthen, but never shortens, the time before the timer expires.
- Setting the tolerance doesn't affect the active/inactive status of the timer.
- Timer tolerance is a QNX OS extension to the POSIX timer_settime() function. If you want to specify infinite timer tolerance, call TimerSettime() instead of timer_settime().
- The startup code tries to determine the frequency of the hardware timer, but for greater accuracy you should specify the frequency by using the -f option; see startup-* options in the Utilities Reference.
- Using high-resolution timers causes extra timer interrupts; as you use more high-resolution timers, the impact on system performance increases. The total impact will vary with interrupt frequency and the hardware characteristics of the system.
- In order to set the tolerance to a value between 0 and the clock period, you need to have the PROCMGR_AID_HIGH_RESOLUTION_TIMER ability enabled. For more information, see procmgr_ability(). The flags for a high-resolution timer (which you can get by calling TimerInfo()) include _NTO_TI_HIGH_RESOLUTION.
struct sigevent event;
timer_t timerId;
int tolerance = 1; // Use a high-resolution timer.
struct itimerspec newTimerTolerance, newTimerSpec;
int rc;
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGUSR1;
rc = timer_create(CLOCK_MONOTONIC, &event, &timerId);
if (rc == -1) {
// Handle the error
}
// Set the tolerance on the timer first because
// setting the time activates the timer.
memset(&newTimerTolerance, 0, sizeof(newTimerTolerance));
newTimerTolerance.it_value.tv_sec = 0;
newTimerTolerance.it_value.tv_nsec = tolerance;
newTimerTolerance.it_interval.tv_sec = 0;
newTimerTolerance.it_interval.tv_nsec = 0;
rc = timer_settime(timerId, TIMER_TOLERANCE, &newTimerTolerance, NULL);
if (rc == -1) {
// Handle the error
}
memset(&newTimerSpec, 0, sizeof(newTimerSpec));
newTimerSpec.it_value.tv_sec = sec;
newTimerSpec.it_value.tv_nsec = usec * 1000;
newTimerSpec.it_interval.tv_sec = 0;
newTimerSpec.it_interval.tv_nsec = 0;
rc = timer_settime(timerId, 0, &newTimerSpec, NULL);
if (rc == -1) {
// Handle the error
}
TIMER_PRECISE | Process tolerance | Timer tolerance | Effective tolerance |
---|---|---|---|
Not set | Default (0) | Default (0) | 1 tick |
Not set | Default (0) | 100 ms | 100 ms |
Not set | 200 ms | Default (0) | 200 ms |
Not set | 200 ms | 100 ms | 100 ms |
Set | Default (0) | Default (0) | 1 tick |
Set | Default (0) | 100 ms | 100 ms |
Set | 200 ms | Default (0) | 1 tick |
Set | 200 ms | 100 ms | 100 ms |
struct _timer_info tinfo;
int tolerance, p_tolerance;
memset(&tinfo, 0, sizeof(struct _timer_info));
rc = TimerInfo (getpid(), timerId, _NTO_TI_REPORT_TOLERANCE, &tinfo);
if (rc == -1) {
// Handle the error
}
// Get information about the timer.
if (tinfo.flags &_NTO_TI_PROCESS_TOLERANT) {
rc = procmgr_timer_tolerance (0, NULL, &p_tolerance);
if (rc == -1) {
// Handle the error
}
printf ("PROCESS_TOLERANT: %ld ns.\n", p_tolerance);
}
if (tinfo.flags &_NTO_TI_TOLERANT) {
printf ("TOLERANT");
tolerance = tinfo.otime.interval_nsec;
// If the tolerance is less than the output of ClockPeriod(),
// the timer is a high-resolution one.
if ((tolerance > 0) && (tolerance < period.nsec)) {
printf (" (high-resolution): %d ns.\n", tolerance);
} else {
printf (": %d ns.\n", tolerance);
}
}
For a more detailed example, see the entry for TimerInfo() in the C Library Reference.