timeout(9)
NAME
- timeout, untimeout, callout_handle_init, callout_init,
- callout_init_mtx,
callout_stop, callout_drain, callout_reset, callout_pending,
callout_active, callout_deactivate - execute a function af
- ter a specified
length of time
SYNOPSIS
#include <sys/types.h>
#include <sys/systm.h>
typedef void timeout_t (void *);
struct callout_handle
timeout(timeout_t *func, void *arg, int ticks);
void
callout_handle_init(struct callout_handle *handle);
struct callout_handle handle = CALLOUT_HANDLE_INITIALIZER(&handle)
void
untimeout(timeout_t *func, void *arg, struct callout_handle
handle);
void
callout_init(struct callout *c, int mpsafe);
void
callout_init_mtx(struct callout *c, struct mtx *mtx, int
flags);
int
callout_stop(struct callout *c);
int
callout_drain(struct callout *c);
int
callout_reset(struct callout *c, int ticks, timeout_t *func,
void *arg);
int
callout_pending(struct callout *c);
int
callout_active(struct callout *c);
callout_deactivate(struct callout *c);
DESCRIPTION
- The function timeout() schedules a call to the function giv
- en by the
argument func to take place after ticks/hz seconds. Non
- positive values
of ticks are silently converted to the value `1'. func
- should be a
pointer to a function that takes a void * argument. Upon
- invocation,
func will receive arg as its only argument. The return val
- ue from
timeout() is a struct callout_handle which can be used in
- conjunction
with the untimeout() function to request that a scheduled
- timeout be canceled. The timeout() call is the old style and new code
- should use the
callout_*() functions.
- The function callout_handle_init() can be used to initialize
- a handle to
a state which will cause any calls to untimeout() with that
- handle to
return with no side effects.
- Assigning a callout handle the value of
CALLOUT_HANDLE_INITIALIZER
- forms the same function as callout_handle_init() and is pro
- vided for use
on statically declared or global callout handles.
- The function untimeout() cancels the timeout associated with
- handle using
the func and arg arguments to validate the handle. If the
- handle does
not correspond to a timeout with the function func taking
- the argument
arg no action is taken. handle must be initialized by a
- previous call to
timeout(), callout_handle_init(), or assigned the value of
CALLOUT_HANDLE_INITIALIZER(&handle) before being passed to
- untimeout().
The behavior of calling untimeout() with an uninitialized
- handle is undefined. The untimeout() call is the old style and new code
- should use the
callout_*() functions.
- As handles are recycled by the system, it is possible (al
- though unlikely)
that a handle from one invocation of timeout() may match the
- handle of
another invocation of timeout() if both calls used the same
- function
pointer and argument, and the first timeout is expired or
- canceled before
the second call. The timeout facility offers O(1) running
- time for
timeout() and untimeout(). Timeouts are executed from
- softclock() with
the Giant lock held. Thus they are protected from re-en
- trancy.
- The functions callout_init(), callout_init_mtx(),
- callout_stop(),
callout_drain() and callout_reset() are low-level routines
- for clients
who wish to allocate their own callout structures.
- The function callout_init() initializes a callout so it can
- be passed to
callout_stop(), callout_drain() or callout_reset() without
- any side
effects. If the mpsafe argument is zero, the callout struc
- ture is not
considered to be ``multi-processor safe''; that is, the Gi
- ant lock will
be acquired before calling the callout function, and re
- leased when the
callout function returns.
- The callout_init_mtx() function may be used as an alterna
- tive to
callout_init(). The parameter mtx specifies a mutex that is
- to be
acquired by the callout subsystem before calling the callout
- function,
and released when the callout function returns. The follow
- ing flags may
be specified:
- CALLOUT_RETURNUNLOCKED The callout function will re
- lease mtx itself,
- so the callout subsystem should
- not attempt
to unlock it after the callout
- function
returns.
- The function callout_stop() cancels a callout if it is cur
- rently pending.
If the callout is pending, then callout_stop() will return a
- non-zero
value. If the callout is not set, has already been serviced
- or is currently being serviced, then zero will be returned. If the
- callout has an
associated mutex, then that mutex must be held when this
- function is
called.
- The function callout_drain() is identical to callout_stop()
- except that
it will wait for the callout to be completed if it is al
- ready in
progress. This function MUST NOT be called while holding
- any locks on
which the callout might block, or deadlock will result.
- Note that if the
callout subsystem has already begun processing this callout,
- then the
callout function may be invoked during the execution of
- callout_drain().
However, the callout subsystem does guarantee that the call
- out will be
fully stopped before callout_drain() returns.
- The function callout_reset() first performs the equivalent
- of
callout_stop() to disestablish the callout, and then estab
- lishes a new
callout in the same manner as timeout(). If there was al
- ready a pending
callout and it was rescheduled, then callout_reset() will
- return a nonzero value. If the callout has an associated mutex, then
- that mutex must
be held when this function is called.
- The macros callout_pending(), callout_active() and
- callout_deactivate()
provide access to the current state of the callout. Careful
- use of these
macros can avoid many of the race conditions that are inher
- ent in asynchronous timer facilities; see Avoiding Race Conditions be
- low for further
details. The callout_pending() macro checks whether a call
- out is
pending; a callout is considered pending when a timeout has
- been set but
the time has not yet arrived. Note that once the timeout
- time arrives
and the callout subsystem starts to process this callout,
callout_pending() will return FALSE even though the callout
- function may
not have finished (or even begun) executing. The
- callout_active() macro
checks whether a callout is marked as active, and the
callout_deactivate() macro clears the callout's active flag.
- The callout
subsystem marks a callout as active when a timeout is set
- and it clears
the active flag in callout_stop() and callout_drain(), but
- it does not
clear it when a callout expires normally via the execution
- of the callout
function.
- Avoiding Race Conditions
- The callout subsystem invokes callout functions from its own
- timer context. Without some kind of synchronization it is possible
- that a callout
function will be invoked concurrently with an attempt to
- stop or reset
the callout by another thread. In particular, since callout
- functions
typically acquire a mutex as their first action, the callout
- function may
have already been invoked, but be blocked waiting for that
- mutex at the
time that another thread tries to reset or stop the callout.
- The callout subsystem provides a number of mechanisms to ad
- dress these
synchronization concerns:
1. If the callout has an associated mutex that was
- specifiedusing the callout_init_mtx() function (or implic
- itly specified
as the Giant mutex using callout_init() with
- mpsafe set to
FALSE), then this mutex is used to avoid the race
- conditions.
The associated mutex must be acquired by the
- caller before
calling callout_stop() or callout_reset() and it
- is guaranteed
that the callout will be correctly stopped or re
- set as
expected. Note that it is still necessary to use
callout_drain() before destroying the callout or
- its associated mutex.
- 2. The return value from callout_stop() and
- callout_reset() indi
cates whether or not the callout was removed. If
- it is known
that the callout was set and the callout function
- has not yet
executed, then a return value of FALSE indicates
- that the
callout function is about to be called. For ex
- ample:
if (sc->sc_flags & SCFLG_CALLOUT_RUNNING) {
if (callout_stop(&sc->sc_callout))
- {
sc->sc_flags &=
- ~SCFLG_CALLOUT_RUNNING;
/* successfully stopped */
- } else {
/*
* callout has expired and
- callout
* function is about to be
- executed
*/
- }
- }
- 3. The callout_pending(), callout_active() and
callout_deactivate() macros can be used together
- to work
around the race conditions. When a callout's
- timeout is set,
the callout subsystem marks the callout as both
- active and
pending. When the timeout time arrives, the
- callout subsystem
begins processing the callout by first clearing
- the pending
flag. It then invokes the callout function with
- out changing
the active flag, and does not clear the active
- flag even after
the callout function returns. The mechanism de
- scribed here
requires the callout function itself to clear the
- active flag
using the callout_deactivate() macro. The
- callout_stop() and
callout_drain() functions always clear both the
- active and
pending flags before returning.
- The callout function should first check the
- pending flag and
return without action if callout_pending() re
- turns TRUE. This
indicates that the callout was rescheduled using
callout_reset() just before the callout function
- was invoked.
If callout_active() returns FALSE then the call
- out function
should also return without action. This indi
- cates that the
callout has been stopped. Finally, the callout
- function
should call callout_deactivate() to clear the
- active flag.
For example:
mtx_lock(&sc->sc_mtx);
if (callout_pending(&sc->sc_callout)) {
/* callout was reset */
mtx_unlock(&sc->sc_mtx);
return;
- }
if (!callout_active(&sc->sc_callout)) {
/* callout was stopped */
mtx_unlock(&sc->sc_mtx);
return;
- }
callout_deactivate(&sc->sc_callout);
/* rest of callout function */
- Together with appropriate synchronization, such
- as the mutex
used above, this approach permits the
- callout_stop() and
callout_reset() functions to be used at any time
- without
races. For example:
mtx_lock(&sc->sc_mtx);
callout_stop(&sc->sc_callout);
/* The callout is effectively stopped now.
- */
- If the callout is still pending then these func
- tions operate
normally, but if processing of the callout has
- already begun
then the tests in the callout function cause it
- to return
without further action. Synchronization between
- the callout
function and other code ensures that stopping or
- resetting the
callout will never be attempted while the callout
- function is
past the callout_deactivate() call.
- The above technique additionally ensures that the
- active flag
always reflects whether the callout is effective
- ly enabled or
disabled. If callout_active() returns false,
- then the callout
is effectively disabled, since even if the call
- out subsystem
is actually just about to invoke the callout
- function, the
callout function will return without action.
- There is one final race condition that must be considered
- when a callout
is being stopped for the last time. In this case it may not
- be safe to
let the callout function itself detect that the callout was
- stopped,
since it may need to access data objects that have already
- been destroyed
or recycled. To ensure that the callout is completely fin
- ished, a call
to callout_drain() should be used.
RETURN VALUES
- The timeout() function returns a struct callout_handle that
- can be passed
to untimeout(). The callout_stop() and callout_drain()
- functions return
non-zero if the callout was still pending when it was called
- or zero otherwise.
HISTORY
- The current timeout and untimeout routines are based on the
- work of Adam
M. Costello and George Varghese, published in a technical
- report entitled
Redesigning the BSD Callout and Timer Facilities and modi
- fied slightly
for inclusion in FreeBSD by Justin T. Gibbs. The original
- work on the
data structures used in this implementation was published by
- G. Varghese
and A. Lauck in the paper Hashed and Hierarchical Timing
- Wheels: Data
Structures for the Efficient Implementation of a Timer
- Facility in the
Proceedings of the 11th ACM Annual Symposium on Operating
- Systems
Principles. The current implementation replaces the long
- standing BSD
linked list callout mechanism which offered O(n) insertion
- and removal
running time but did not generate or require handles for un
- timeout operations.
- BSD September 8, 2005