TimerTimeout(), TimerTimeout_r()
Set a timeout on a blocking state
Synopsis:
#include <sys/neutrino.h>
int TimerTimeout( clockid_t id,
uint32_t flags,
const struct sigevent * notify,
const uint64_t * ntime,
uint64_t * otime );
int TimerTimeout_r( clockid_t id,
uint32_t flags,
const struct sigevent * notify,
const uint64_t * ntime,
uint64_t * otime );
Arguments:
- id
- The clock to use to implement the timeout; one of the following:
- CLOCK_REALTIME — the standard POSIX-defined clock. Timers based on this clock wake up the processor if it's in a power-saving mode.
- CLOCK_SOFTTIME — (a QNX OS extension)
this clock is active only when
the processor isn't in a power-saving mode.
For example, an application using a CLOCK_SOFTTIME timer
to sleep wouldn't wake up
the processor when the application was due to wake up.
This will allow the processor to enter a power-saving mode.
While the processor isn't in a power-saving mode, CLOCK_SOFTTIME behaves the same as CLOCK_REALTIME.
- CLOCK_MONOTONIC — this clock always increases at a constant rate and can't be adjusted.
- flags
- Flags that control the function's behavior; one of the following:
- TIMER_TOLERANCE to specify the amount of the tolerance to allow the kernel with regard to the expiry time
- _NTO_TIMEOUT_NANOSLEEP to block in the STATE_NANOSLEEP state until the timeout occurs or a signal unblocks the thread
- a combination of zero or more bits that specify which states the timeout applies to (see below)
If you specify _NTO_TIMEOUT_NANOSLEEP or a combination of timeout states, you can also OR in the following bits:
- TIMER_ABSTIME — set an absolute expiration time. If you don't set this bit, the function sets a relative expiration time.
- notify
- NULL, or a pointer to a
sigevent
structure of type SIGEV_UNBLOCK that contains the event to act on when the timeout expires; see below.
If you specify TIMER_TOLERANCE in the flags, this argument must be NULL.
- ntime
- NULL, or a pointer to a location where the function can get some
data, depending on the flags:
- TIMER_TOLERANCE — a pointer to the timer tolerance, in nanoseconds. If this argument is NULL, the function doesn't change the timer tolerance.
- _NTO_TIMEOUT_NANOSLEEP or a combination of timeout states — a pointer to the timeout, in nanoseconds. If this argument is NULL, the timeout occurs immediately (see below).
- otime
- NULL, or a pointer to a location where the function can store some
data, depending on the flags:
- TIMER_TOLERANCE — the previous timer tolerance
- _NTO_TIMEOUT_NANOSLEEP — the time remaining in the sleep
- a combination of timeout states — not used
Library:
libc
Use the -l c option to qcc to link against this library. This library is usually included automatically.
Description:
The TimerTimeout() and TimerTimeout_r() kernel calls either set the timer tolerance for timeouts, set a thread-local timeout on any kernel blocking state, or block for a specified time, depending on the flags setting.
These functions are identical except in the way they indicate errors; see the Returns section for details.
Setting the tolerance for a timeout
To set the timer tolerance for a timeout, set flags to TIMER_TOLERANCE. In this case, TimerTimeout() doesn't start a timeout; you need to call it twice, once with flags set to TIMER_TOLERANCE, and once to set the timeout. If you're setting a timeout on a kernel blocking state, it doesn't matter which order you do this in. For a _NTO_TIMEOUT_NANOSLEEP timeout, you should set the tolerance first.
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().
For more information about timer tolerance, see
High-resolution timers
in the Understanding the Microkernel's Concept of Time
chapter of the QNX OS Programmer's Guide.
Setting a timeout on a kernel blocking state
Setting a timeout on a kernel blocking state sets a time boundary that will unblock the calling thread if it has not completed a call within the elapsed time and the thread is blocked in one of the states indicated by the _NTO_TIMEOUT_* flags passed to TimerTimeout().
The following table shows the blocking states that are entered as a result of certain kernel and non-kernel calls, along with the corresponding timeout state bits that you can use in the flags argument:
| Call | Blocking state | Timeout state |
|---|---|---|
| InterruptWait() | STATE_INTR | _NTO_TIMEOUT_INTR |
| MsgReceive(), MsgReceivev(), MsgReceivePulse(), | STATE_RECEIVE | _NTO_TIMEOUT_RECEIVE |
| MsgSend(), MsgSendnc(), MsgSendsv(), MsgSendsvnc(), MsgSendv(), MsgSendvnc(), MsgSendvs(), MsgSendvsnc() | STATE_SEND or STATE_REPLY | _NTO_TIMEOUT_SEND or _NTO_TIMEOUT_REPLY |
| SignalSuspend() | STATE_SIGSUSPEND | _NTO_TIMEOUT_SIGSUSPEND |
| SignalWaitinfo(), SignalWaitinfoMask() | STATE_SIGWAITINFO | _NTO_TIMEOUT_SIGWAITINFO |
| SyncCondvarWait() | STATE_CONDVAR or STATE_MUTEX | _NTO_TIMEOUT_CONDVAR or _NTO_TIMEOUT_MUTEX |
| SyncMutexLock() | STATE_MUTEX | _NTO_TIMEOUT_MUTEX |
| SyncSemWait() | STATE_SEM | _NTO_TIMEOUT_SEM |
| ThreadCtl() | STATE_WAITPAGE | Timing out on this state isn't supported |
| ThreadJoin() | STATE_JOIN | _NTO_TIMEOUT_JOIN |
| pthread_barrier_wait() | STATE_BARRIER | _NTO_TIMEOUT_BARRIER |
For example, to set a timeout on MsgSendv(), specify
_NTO_TIMEOUT_SEND | _NTO_TIMEOUT_REPLY for the flags argument.
Here's what happens to the timer:
- When TimerTimeout() is used to set a timeout, a thread-local timer is automatically preallocated, a relative timeout is converted into an absolute one, and the timer is then armed.
- When the thread that called TimerTimeout() then calls into the kernel and attempts to enter a blocking state specified in flags, the timer is activated. That is, the timer is placed on the active timer list, and the kernel starts to check the timeout against the current time; if it has already passed, the kernel delivers the event right away. If the event is an explicit UNBLOCK event or an implicit one due to a NULL event pointer, the calling thread doesn't enter any of the blocking states specified in flags but returns immediately with an error.
- After TimerTimeout() is called, the next kernel call made by the same
thread, regardless of whether this call blocks in one of the specified timeout states
(or any at all), disarms the timeout when it exits from the kernel.
CAUTION:
After setting a timeout with TimerTimeout(), to ensure the timeout will work, you must call one of the routines listed in the
Setting a timeout on a kernel blocking state
table that corresponds to the timeout state you specified. You must not set a timeout and then call a function that's not listed there because you can't be sure that the timeout will work for the specified conditions (i.e., timeout states).Suppose you issue the following sequence of calls:
The InterruptAttachThread() call does not block and, thus, can't go into any of the blocking states listed for _NTO_TIMEOUT_SEND and _NTO_TIMEOUT_REPLY. But this call still clears the list of blocking states and thus, the subsequent MsgSend() operation won't have a timeout applied.TimerTimeout(CLOCK_MONOTONIC,_NTO_TIMEOUT_SEND|_NTO_TIMEOUT_REPLY,...); InterruptAttachThread(...); MsgSend(...);
TimerTimeout() always operates on a one-shot basis. When one of the above kernel calls returns (or is interrupted by a signal), the timeout request is removed from the system. Only one timeout per thread may be in effect at a time. A second call to TimerTimeout(), without calling one of the above kernel functions, replaces the existing timeout on that thread. A call with flags set to zero ensures that a timeout won't occur on any state. This is the default when a thread is created.
...
event.sigev_notify = SIGEV_UNBLOCK;
timeout = 10 * 1000000000;
TimerTimeout( CLOCK_MONOTONIC, _NTO_TIMEOUT_SEND | _NTO_TIMEOUT_REPLY,
&event, &timeout, NULL );
MsgSendv( coid, NULL, 0, NULL, 0 );
...
There's one exception to this rule: as described above, if you want to set the timer tolerance, you need to call TimerTimeout() twice, once with flags set to TIMER_TOLERANCE, and once to set the timeout. It doesn't matter which order you do this in.
If a signal handler is called between the call to TimerTimeout() and the kernel call (MsgSendv() in this example), the TimerTimeout() values are saved during the signal handler and then are restored when the signal handler exits.
If the timeout expires, the kernel acts on the event specified in the sigevent structure pointed to by the notify argument. You can only use an event of type SIGEV_UNBLOCK, which makes the timed-out kernel call fail with an error of ETIMEDOUT. The event doesn't need to be registered. If you pass NULL for notify, SIGEV_UNBLOCK is assumed.
_NTO_CHF_UNBLOCKin the
Message Passingchapter of Getting Started with the QNX OS.
The id argument specifies the clock to use to implement the timeout. The timeout:
- is specified by the ntime argument (the number of nanoseconds)
- is relative to the current time (when TimerTimeout() is called), unless flags includes TIMER_ABSTIME, which makes the timeout occur at the absolute time set in ntime
- occurs on a clock tick (see ClockPeriod())
Understanding the Microkernel's Concept of Timechapter of the QNX OS Programmer's Guide.
If you don't wish to block for any time, you can pass a NULL for ntime, in which case no timer is used, the event is assumed to be SIGEV_UNBLOCK, and an attempt to enter a blocking state as set by flags immediately returns with ETIMEDOUT. Although a questionable practice, you can use it to poll potential blocking kernel calls. For example, you can poll for messages using MsgReceivev() with an immediate timeout. A much better approach is to use multiple threads and have one block while waiting for messages.
Blocking for a specified time
If you set _NTO_TIMEOUT_NANOSLEEP in the flags, then these calls block in the STATE_NANOSLEEP state until the timeout occurs or a signal unblocks the thread. If you want to set the timer tolerance for _NTO_TIMEOUT_NANOSLEEP, you should do so first. The timeout is as described above.
TimerTimeout( CLOCK_MONOTONIC, _NTO_TIMEOUT_NANOSLEEP, NULL, &ntime, &otime );
If otime isn't NULL, the kernel sets *otime to the time remaining in the sleep, in nanoseconds.
Blocking states
These calls don't block unless you specify _NTO_TIMEOUT_NANOSLEEP in flags. In this case, the calls block as follows:
- STATE_NANOSLEEP
- The calling thread blocks for the requested time period.
Returns:
On success, these functions return:
- 0 if you specified TIMER_TOLERANCE in the flags argument
- 0 if you specified _NTO_TIMEOUT_NANOSLEEP and the sleep was successful
- the timeout states from the last time that the calling thread used one of these functions to set a timeout, or 0 if the previous call specified _NTO_TIMEOUT_NANOSLEEP (calls with TIMER_TOLERANCE don't affect the saved timeout states)
If an error occurs:
- TimerTimeout() returns -1 and sets errno.
- TimerTimeout_r() returns the negative of a value from the Errors section and doesn't set errno.
Errors:
- EAGAIN
- All kernel timer entries are in use.
- EFAULT
- A fault occurred when the kernel tried to access ntime, otime, or notify.
- EINTR
- The call was interrupted by a signal.
- EINVAL
- At least one of the following is true:
- The ntime argument specifies a nanosecond value less than 0 or greater than or equal to 1000 million.
- The id argument doesn't specify a valid clock ID or specifies the CPU-time clock of the calling thread.
- The notify argument is neither NULL nor a sigevent of type SIGEV_UNBLOCK.
- The TIMER_ABSTIME flag is set in flags and the ntime argument is outside the range for the clock specified by id.
- The TIMER_TOLERANCE flag is set in flags and either other bits are set in flags.
- ENOTSUP
- The id argument specifies a clock for which TimerTimeout() or TimerTimeout_r() isn't supported, such as a CPU-time clock.
- EOVERFLOW
- The sum of the current time plus the provided relative time results in a value that exceeds UINT64_MAX.
- EPERM
- The calling process doesn't have the required permission; see procmgr_ability().
Classification:
| Safety: | |
|---|---|
| Cancellation point | Read the Caveats |
| Signal handler | Yes |
| Thread | Yes |
Caveats:
The TimerTimeout() or TimerTimeout_r() function can serve as a cancellation point only if _NTO_TIMEOUT_NANOSLEEP is given in flags.
