Set a timeout on a blocking state
#include <time.h> int timer_timeout( clockid_t id, int flags, const struct sigevent* notify, const struct timespec* ntime, struct timespec* otime ); int timer_timeout_r( clockid_t id, int flags, const struct sigevent* notify, const struct timespec* ntime, struct timespec* 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 of Getting Started with QNX Neutrino.
or:
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 timer_timeout() and timer_timeout_r() functions set the timeout ntime on any kernel blocking state. These functions are identical except in the way they indicate errors. See the Returns section for details.
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() | STATE_NET_SEND or STATE_NET_REPLY | Timing out on these states isn't supported |
SignalKill() | STATE_NET_SEND or STATE_NET_REPLY | Timing out on these states isn't supported |
SignalSuspend() | STATE_SIGSUSPEND | _NTO_TIMEOUT_SIGSUSPEND |
SignalWaitinfo() | 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:
The timer_timeout() function 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 timer_timeout(), 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 timer_timeout() just before the function that you wish to time out. For example:
... event.sigev_notify = SIGEV_UNBLOCK; timeout.tv_sec = 10; timeout.tv_nsec = 0; timer_timeout( CLOCK_MONOTONIC, _NTO_TIMEOUT_SEND | _NTO_TIMEOUT_REPLY, &event, &timeout, NULL ); MsgSendv( coid, NULL, 0, NULL, 0 ); ...
There's one exception to this rule: if you want to set the timer tolerance, you need to call timer_timeout() 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 you call timer_timeout() followed by a kernel call that can't cause the thread to block (e.g., ClockId()), the results are undefined.
If a signal handler is called between the calls to timer_timeout() and MsgSendv(), the timer_timeout() values are saved during the signal handler and then are restored when the signal handler exits.
If the timeout expires, the kernel acts upon the event specified by the sigevent structure pointed to by the notify argument. We recommend the following event types in this case:
Only SIGEV_UNBLOCK guarantees that the kernel call unblocks. A signal may be ignored, blocked, or accepted by another thread, and a pulse can unblock only a MsgReceivev(). If you specify NULL for notify, then SIGEV_UNBLOCK is assumed. In this case, a timed-out kernel call returns failure with an error of ETIMEDOUT.
The timeout:
( tv_sec × 1000000000 + tv_nsec ) / ( size of timer tick ) nanoseconds
where tv_sec and tv_nsec are fields of the timespec structure (defined in <time.h>).
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 will immediately return with ETIMEDOUT. Although a questionable practice, this can be used 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 flags to _NTO_TIMEOUT_NANOSLEEP, then:
timer_timeout( CLOCK_MONOTONIC, _NTO_TIMEOUT_NANOSLEEP, NULL, &ntime, &otime );
Blocking states
The kernel calls don't block unless you specify _NTO_TIMEOUT_NANOSLEEP in flags. In this case, the calls block as follows:
The previous flags. If an error occurs:
Safety: | |
---|---|
Cancellation point | No |
Interrupt handler | No |
Signal handler | Yes |
Thread | Yes |