TimerTimeout(), TimerTimeout_r()
Set a timeout on a blocking state
Synopsis:
#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 );
Arguments:
- id
- The type of timer 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
- Flags that specify which states the timeout applies to. For the list and description of applicable states, see the section Timeout states.
- notify
- NULL, or a pointer to a sigevent structure that contains the event to act on when the timeout expires. See Event types for the list of recommended event types.
- ntime
- The timeout (in nanoseconds).
- 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 TimerTimeout() and TimerTimeout_r() kernel calls 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.
These blocking states are entered as a result of the following kernel calls:
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 |
Timeout states
You can specify which states the timeout should apply to via a bitmask passed in the flags argument. The bits are defined by the following constants:
- _NTO_TIMEOUT_CONDVAR
- Timeout on STATE_CONDVAR.
- _NTO_TIMEOUT_JOIN
- Timeout on STATE_JOIN.
- _NTO_TIMEOUT_INTR
- Timeout on STATE_INTR.
- _NTO_TIMEOUT_MUTEX
- Timeout on STATE_MUTEX.
- _NTO_TIMEOUT_RECEIVE
- Timeout on STATE_RECEIVE.
- _NTO_TIMEOUT_REPLY
- Timeout on STATE_REPLY.
- _NTO_TIMEOUT_SEM
- Timeout on STATE_SEM.
- _NTO_TIMEOUT_SEND
- Timeout on STATE_SEND.
- _NTO_TIMEOUT_SIGSUSPEND
- Timeout on STATE_SIGSUSPEND.
- _NTO_TIMEOUT_SIGWAITINFO
- Timeout on STATE_SIGWAITINFO.
For example, to set a timeout on MsgSendv(), specify:
_NTO_TIMEOUT_SEND | _NTO_TIMEOUT_REPLY
Once a timeout is specified using TimerTimeout(), 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.
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 timeout. For example:
... event.sigev_notify = SIGEV_UNBLOCK; timeout = 10×1000000000; TimerTimeout( 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 TimerTimeout() and MsgSendv() , the TimerTimeout() values are saved during the signal handler and then are restored when the signal handler exits.
Event types
If the timeout expires, the kernel acts upon the event specified in 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 you pass NULL for notify, SIGEV_UNBLOCK is assumed. In this case, a timed out kernel call returns failure with an error of ETIMEDOUT.
The timeout
The type of timer used to implement the timeout is specified with the id argument.
The timeout:
- Is specified by the ntime argument (the number of nanoseconds).
- Is relative to the current time (when TimerTimeout() 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:
( ntime ) / ( size of timer tick ) nanoseconds
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 waiting for messages.
If you set flags to _NTO_TIMEOUT_NANOSLEEP, then these calls block in the STATE_NANOSLEEP state until the timeout (or a signal that unblocks the thread) occurs. You can use this to implement an efficient kernel sleep as follows:
TimerTimeout( CLOCK_REALTIME, _NTO_TIMEOUT_NANOSLEEP, NULL, ntime, otime );
If otime isn't NULL and the sleep is unblocked by a signal, it contains the time remaining in the sleep.
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.
Blocking states
These 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:
The only difference between these functions is the way they indicate errors:
- TimerTimeout()
- The previous flags. If an error occurs, -1 is returned and errno is set.
- TimerTimeout_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
- Invalid timer value id.
Classification:
Safety: | |
---|---|
Cancellation point | No |
Interrupt handler | No |
Signal handler | Yes |
Thread | Yes |
Caveats:
The timeout value starts timing out when TimerTimeout() is called, not when the blocking state is entered. It might be possible to get preempted after calling TimerTimeout() but before the blocking kernel call.