Updated: October 28, 2024 |
Set a timeout on a blocking state
#include <sys/neutrino.h> int TimerTimeout( clockid_t id, int flags, const struct sigevent * notify, const uint64_t * ntime, uint64_t * otime ); int TimerTimeout_r( clockid_t id, int flags, const struct sigevent * notify, const uint64_t * ntime, uint64_t * otime );
While the processor isn't in a power-saving mode, CLOCK_SOFTTIME behaves the same as CLOCK_REALTIME.
For more information about the different clocks, see Other clock sources in the Clocks, Timers, and Getting a Kick Every So Often chapter of Getting Started with QNX Neutrino.
If you specify _NTO_TIMEOUT_NANOSLEEP or a combination of timeout states, you can also OR in the following bits:
If you specify TIMER_TOLERANCE in the flags, this argument must be NULL.
libc
Use the -l c option to qcc to link against this library. This library is usually included automatically.
The TimerTimeout() and TimerTimeout_r() kernel calls set a timeout on any kernel blocking state, block for a specified time, or set the timer tolerance for these timeouts, depending on the flags. These functions are identical except in the way they indicate errors. See the Returns section for details.
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.
(QNX Neutrino 7.0.1 or later) 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 Tolerant and high-resolution timers in the Understanding the Microkernel's Concept of Time chapter of the QNX Neutrino Programmer's Guide.
The following table shows the blocking states that are entered as a result of certain 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(), MsgReceivePulsev() | STATE_RECEIVE | _NTO_TIMEOUT_RECEIVE |
MsgDeliverEvent(), MsgError(), MsgRead(), MsgReadv(), MsgReply(), MsgReplyv(), MsgWrite(), MsgWritev() | STATE_REPLY | _NTO_TIMEOUT_REPLY |
MsgSend(), MsgSendnc(), MsgSendsv(), MsgSendsvnc(), MsgSendv(), MsgSendvnc(), MsgSendvs(), MsgSendvsnc() | STATE_SEND or STATE_REPLY | _NTO_TIMEOUT_SEND or _NTO_TIMEOUT_REPLY |
MsgSendPulse(), MsgSendPulsePtr() | STATE_NET_SEND or STATE_NET_REPLY | Timing out on these states isn't supported |
SignalKill(), SignalKillSigval() | STATE_NET_SEND or STATE_NET_REPLY | Timing out on these states isn't supported |
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 |
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:
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.
Always call TimerTimeout() just before the function that you wish to time out. For example:
... 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'll almost always want to 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.
The id argument specifies the clock to use to implement the timeout. The timeout:
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.
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.
You can use this to implement an efficient kernel sleep as follows:
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.
These calls don't block unless you specify _NTO_TIMEOUT_NANOSLEEP in flags. In this case, the calls block as follows:
On success, these functions return:
If an error occurs:
Safety: | |
---|---|
Cancellation point | Read the Caveats |
Interrupt handler | No |
Signal handler | Yes |
Thread | Yes |
The TimerTimeout() or TimerTimeout_r() function can serve as a cancellation point only if _NTO_TIMEOUT_NANOSLEEP is given in flags.