Updated: April 19, 2023 |
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 | (QNX Neutrino 7.0 or later) 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 management in 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.
Here's an example of setting up a high-resolution timer:
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 }
You can use procmgr_timer_tolerance() to set a default timer tolerance for a process, to be used for timers that don't have a specific tolerance. The default timer tolerance is inherited across a fork(), but not an exec*() or a spawn*(). You can prevent the process's default tolerance from being used for a timer by specifying the TIMER_PRECISE flag when you call timer_settime() or TimerSettime() to set the timer's expiry time. For example:
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 |
To determine the tolerance for a timer, call TimerInfo(), specifying the _NTO_TI_REPORT_TOLERANCE flag; the function puts the tolerance in the otime.interval_nsec field. For example:
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.