timer_timeout(), timer_timeout_r()

Set a timeout on a blocking state

Synopsis:

#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 );

Arguments:

id
The type of timer used to implement the timeout. The possible clock types of id are:
flags
Flags that specify which states the timeout applies to. For the list and description of applicable states, see below. You can also OR in TIMER_ABSTIME to set an absolute expiration time; otherwise the time is relative to the time at which you called timer_timeout().
notify
NULL, or a pointer to a sigevent structure that defines the event that the kernel acts on if the timeout expires; see below.
ntime
NULL, or a pointer to a pointer to a timespec structure that specifies the timeout. If this argument is NULL, the timeout occurs immediately.
otime
NULL, or a pointer to a location where the function can store the time remaining in the sleep if the flags include _NTO_TIMEOUT_NANOSLEEP.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

The timer_timeout() and timer_timeout_r() functions set a timeout on any kernel blocking state. These functions are identical except in the way they indicate errors. See the Returns section for details.


Note: The timer_timeout() function is a cover for TimerTimeout() that uses a timespec structure instead of a uint64_t to specify the timeout.

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(), MsgReceivePulse(), MsgReceivePulsev(), MsgReceivev() 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() STATE_SIGWAITINFO _NTO_TIMEOUT_SIGWAITINFO
SyncCondvarWait() STATE_CONDVAR _NTO_TIMEOUT_CONDVAR
SyncMutexLock() STATE_MUTEX _NTO_TIMEOUT_MUTEX
SyncSemWait() STATE_SEM _NTO_TIMEOUT_SEM
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:


Note: A relative timeout starts when you call timer_timeout(), not when the blocking state is entered. It's possible to get preempted after calling timer_timeout() but before the blocking kernel call.

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 );
...

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.


Note: MsgSend*() doesn't unblock on SIGEV_UNBLOCK if the server has already received the message via MsgReceive*() and has specified _NTO_CHF_UNBLOCK in the flags argument to its ChannelCreate() call. In this case, it's up to the server to do a MsgReply*() or MsgError().

The timeout:


Note: Because of the nature of time measurement, the timer might actually expire later than the specified time. For more information, see the Tick, Tock: Understanding the Neutrino Microkernel's Concept of Time chapter of the QNX Neutrino 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 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:

Blocking states

The kernel calls don't block unless _NTO_TIMEOUT_NANOSLEEP is specified in flags. In this case, the calls block as follows:

STATE_NANOSLEEP
The calling thread blocks for the requested time period.

Returns:

The previous flags. If an error occurs:

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
The id argument doesn't specify a valid clock ID.

Classification:

QNX Neutrino

Safety:
Cancellation point No
Interrupt handler No
Signal handler Yes
Thread Yes

See also:

sigevent, TimerCreate(), TimerInfo(), TimerTimeout()

Clocks, Timers, and Getting a Kick Every So Often chapter of Getting Started with QNX Neutrino

Tick, Tock: Understanding the Neutrino Microkernel's Concept of Time chapter of the QNX Neutrino Programmer's Guide