timer_timeout(), timer_timeout_r()

Updated: May 06, 2022

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 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 Neutrino 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.

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.

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.
  • TIMER_PRECISE — override any default tolerance that was set for the process (see procmgr_timer_tolerance()).
notify
A pointer to a sigevent structure that defines the event that the kernel acts on if 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 timespec structure where the function can get some data, depending on the flags:
  • TIMER_TOLERANCE — a pointer to the timer tolerance. 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. 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 timer_timeout() and timer_timeout_r() functions 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.

Note: The timer_timeout() function is a cover for TimerTimeout() that uses a timespec structure (specifying seconds and nanoseconds) instead of a uint64_t (specifying nanoseconds) for the ntime and otime arguments.

Setting the tolerance for a timeout

To set the timer tolerance for a timeout, set flags to TIMER_TOLERANCE. In this case, timer_timeout() 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.

Setting a timeout on a kernel blocking state

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:

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

There's one exception to this rule: as described above, 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 call to timer_timeout() and the kernel call (MsgSendv() in this example), 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 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. If you pass NULL for notify, SIGEV_UNBLOCK is assumed.

Note: MsgSend*() doesn't 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, the server doesn't want the client to unblock on its own; instead, the kernel sends an unblock pulse to the server, and it's up to the server to do a MsgReply*() or MsgError(). For more information, see _NTO_CHF_UNBLOCK in the “Message Passing” chapter of Getting Started with QNX Neutrino.

The id argument specifies the clock to use to implement the timeout. 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 Understanding the 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.

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.

You can use this to implement an efficient kernel sleep as follows:

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

If otime isn't NULL, the kernel sets *otime to the time remaining in the sleep.

Blocking states

The kernel 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:

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
Invalid arguments:
  • The id argument doesn't specify a valid clock ID.
  • If you specified TIMER_TOLERANCE in the flags, you also set other bits in the flags, or notify isn't NULL.
EPERM
The calling process doesn't have the required permission; see procmgr_ability().

Classification:

QNX Neutrino

Safety:  
Cancellation point Read the Caveats
Interrupt handler No
Signal handler Yes
Thread Yes

Caveats:

The timer_timeout() or timer_timeout_r() function can serve as a cancellation point only if _NTO_TIMEOUT_NANOSLEEP is given in flags.