timer_timeout(), timer_timeout_r()

Set a timeout on a blocking state

Synopsis:

#include <time.h>

extern int timer_timeout( 
              clockid_t id,
              int flags,
              const struct sigevent* notify,
              const struct timespec* ntime,
              struct timespec* otime );

extern int timer_timeout_r(
              clockid_t id,
              int flags,
              const struct sigevent* notify,
              const struct timespec* ntime,
              struct timespec* otime );

Arguments:

id
The clock to use to implement the timeout; one of:
  • 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 — 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
A bitmask that specifies which states you want the timeout to apply to; see below.

You can also OR in at most one of the following bits:

  • TIMER_ABSTIME — set an absolute expiration time.
  • TIMER_TOLERANCE — specify the amount of the tolerance to allow the kernel in low-power situations.

If you don't set either of these bits, the function sets a relative expiration time.

If you're setting an expiration time (i.e., you didn't specify TIMER_TOLERANCE), you can OR in TIMER_PRECISE to exclude the timer from timer harmonization.

notify
A pointer to a sigevent structure that defines the event that the kernel acts on if the timeout expires; see below.
ntime
A pointer to a timespec structure that specifies the timeout.
otime
NULL, or a pointer to a location where the function can store the time remaining in the sleep.

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 are identical except in the way they indicate errors. See the Returns section for details.

The timer_timeout() function sets the timeout ntime on any kernel blocking state. The actual timeout that occurred is returned in otime. The resolution of otime for both timer_timeout() and TimerTimeout() functions is in nanoseconds. The difference, however, for otime in these two functions is in the format. For timer_timeout(), the otime is a pointer to timespec structure with two integers, whereas for TimerTimeout(), the pointer is of uint64_t type.

The time in TimerTimeout()'s ntime argument is also in nanoseconds. When ntime is passed to TimerTimeout(), the time (in timespec) is converted from seconds and nanoseconds into nanoseconds. When otime is returned to timer_timeout(), the time is converted from nanoseconds into seconds and nanoseconds.

The kernel blocking states are entered as a result of the following kernel calls:

Kernel function call Blocking state
InterruptWait() STATE_INTR
MsgReceivev() STATE_RECEIVE
MsgSendv() STATE_SEND or STATE_REPLY
SignalSuspend() STATE_SIGSUSPEND
SignalWaitinfo() STATE_SIGWAITINFO
SyncCondvarWait() STATE_CONDVAR
SyncMutexLock() STATE_MUTEX
SyncSemWait() STATE_SEM
ThreadJoin() STATE_JOIN

The user specifies which states the timeout should apply to via a bitmask passed in the flags argument. The bits are defined by the following constants:

Constant Meaning
_NTO_TIMEOUT_CONDVAR Time out on STATE_CONDVAR.
_NTO_TIMEOUT_JOIN Time out on STATE_JOIN.
_NTO_TIMEOUT_INTR Time out on STATE_INTR.
_NTO_TIMEOUT_MUTEX Time out on STATE_MUTEX.
_NTO_TIMEOUT_RECEIVE Time out on STATE_RECEIVE.
_NTO_TIMEOUT_REPLY Time out on STATE_REPLY.
_NTO_TIMEOUT_SEM Time out on STATE_SEM.
_NTO_TIMEOUT_SEND Time out on STATE_SEND.
_NTO_TIMEOUT_SIGSUSPEND Time out on STATE_SIGSUSPEND.
_NTO_TIMEOUT_SIGWAITINFO Time out on STATE_SIGWAITINFO.

For example, to set a timeout on MsgSendv(), specify:

_NTO_TIMEOUT_SEND | _NTO_TIMEOUT_REPLY

Once a timeout is specified using timer_timeout(), it's armed and released under the following conditions:

Armed
The kernel attempts to enter a blocking state specified in flags.
Released
One of the above kernel calls completed without blocking, or the kernel call blocks but unblocks before the timeout expires, or the timeout expires.

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_REALTIME,
               _NTO_TIMEOUT_SEND | _NTO_TIMEOUT_REPLY, 
               &event, &timeout, NULL );
MsgSendv( coid, NULL, 0, NULL, 0 );
...

If the 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:

  • SIGEV_SIGNAL
  • SIGEV_SIGNAL_CODE
  • SIGEV_SIGNAL_THREAD
  • SIGEV_PULSE
  • SIGEV_UNBLOCK
  • SIGEV_INTR

Only SIGEV_UNBLOCK guarantees that the kernel call unblocks. A signal may be ignored, blocked, or accepted by another thread and a pulse can only unblock a MsgReceivev(). If a NULL is passed for event then SIGEV_UNBLOCK is assumed. In this case, a timed out kernel call will return failure with an error of ETIMEDOUT.

MsgSendv() won't unblock on SIGEV_UNBLOCK if the server has already received the message via MsgReceivev() 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 MsgReplyv() or MsgError() .

The timeout:

  • is specified by the ntime argument
  • is relative to the current time (when timer_timeout() 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()) so the actual wakeup time is a minimum of:

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

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 Microkernel's Concept of Time chapter of the BlackBerry 10 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 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 waiting for messages.

If flags is set to _NTO_TIMEOUT_NANOSLEEP, then these calls block in the STATE_NANOSLEEP state until the timeout (or a signal which unblocks the thread) occurs. This can be used to implement an efficient kernel sleep as follows:

timer_timeout( CLOCK_REALTIME, _NTO_TIMEOUT_NANOSLEEP, 
               NULL, &ntime, &otime );

If otime isn't NULL and the sleep is unblocked by a signal then it contains the time remaining in the sleep.

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:

timer_timeout()
The previous flags. If an error occurs, the function returns -1 and sets errno .
timer_timeout_r()
The previous flags. This function does NOT set errno. If an error occurs, the negative of a value from the Errors section is returned.

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 clock type id isn't one of CLOCK_MONOTONIC, CLOCK_SOFTTIME, or CLOCK_REALTIME.

Classification:

QNX Neutrino

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

Caveats:

The timeout value starts timing out when timer_timeout() is called, not when the blocking state is entered. It might be possible to get preempted after calling timer_timeout() but before the blocking kernel call.

Last modified: 2013-12-23

comments powered by Disqus