NAME
callout_active
,
callout_deactivate
,
callout_drain
, callout_init
,
callout_init_mp
,
callout_init_lk
,
callout_pending
,
callout_reset
,
callout_reset_bycpu
,
callout_stop
,
callout_stop_async
,
callout_cancel
,
callout_terminate
—
execute a function after a specified
length of time
SYNOPSIS
#include
<sys/types.h>
#include <sys/systm.h>
#include <sys/callout.h>
typedef void timeout_t (void *);
int
callout_active
(struct
callout *c);
void
callout_deactivate
(struct
callout *c);
int
callout_drain
(struct
callout *c);
void
callout_init
(struct
callout *c, int
mpsafe);
void
callout_init_lk
(struct
callout *c, struct lock
*lk);
int
callout_pending
(struct
callout *c);
void
callout_reset
(struct
callout *c, int
ticks, timeout_t
*func, void
*arg);
void
callout_reset_bycpu
(struct callout
*c, int ticks, timeout_t
*func, void *arg, int
cpuid);
int
callout_stop
(struct
callout *c);
int
callout_stop_async
(struct
callout *c);
int
callout_cancel
(struct
callout *c);
void
callout_terminate
(struct
callout *c);
DESCRIPTION
Thecallout
API is used to schedule a call to an
arbitrary function at a specific time in the future. Consumers of this API are
required to allocate a callout structure (struct callout) for each pending
function invocation. This structure stores state about the pending function
invocation including the function to be called and the time at which the
function should be invoked. Pending function calls can be cancelled or
rescheduled to a different time. In addition, a callout structure may be
reused to schedule a new function call after a scheduled call is completed.
Callouts only provide a single-shot mode. If a consumer requires a periodic timer, it must explicitly reschedule each function call. This is normally done by rescheduling the subsequent call within the called function.
In FreeBSD callout functions must not sleep. They may not acquire sleepable locks, wait on condition variables, perform blocking allocation requests, or invoke any other action that might sleep. In DragonFly all callout functions are executed from a common kernel thread on the target cpu and may block as long as deadlocks are avoided. But generally speaking, callout functions should run in as short a time as possible as they can add lag to other unrelated callouts.
Each callout structure must be initialized by
callout_init
(),
callout_init_mp
(),
or callout_init_lk
() before it is passed to any of
the other callout functions. The callout_init
() and
callout_init_mp
() functions initialize a callout
structure in c that is not associated with a specific
lock. The former will hold the mp_lock across callback. However, it is
deprecated and should not be used in new code.
callout_init_mp
() should be used for any new
code.
The
callout_init_lk
()
function initialize a callout structure in c that is
associated with a specific lock. In FreeBSD the
associated lock should be held while stopping or rescheduling the callout.
In DragonFly the same is true, but is not a
requirement.
The callout subsystem acquires the associated lock before calling the callout function and releases it after the function returns. If the callout was cancelled while the callout subsystem waited for the associated lock, the callout function is not called, and the associated lock is released. This ensures that stopping or rescheduling the callout will abort any previously scheduled invocation.
The function
callout_stop
()
cancels a callout c if it is currently pending. If the
callout is pending and successfully stopped, then
callout_stop
() returns a value of one. In
FreeBSD if the callout is not set, or has already
been serviced, then negative one is returned. In
DragonFly if the callout is not set, or has already
been serviced, then zero is returned. If the callout is currently being
serviced and cannot be stopped, then zero will be returned. If the callout
is currently being serviced and cannot be stopped, and at the same time a
next invocation of the same callout is also scheduled, then
callout_stop
() unschedules the next run and returns
zero. In FreeBSD if the callout has an associated
lock, then that lock must be held when this function is called. In
DragonFly if the callout has an associated lock,
then that lock should be held when this function is called to avoid races,
but does not have to be.
In DragonFly the
stop operation is guaranteed to be synchronous if the callout was
initialized with
callout_init_lk
().
The function
callout_stop_async
()
is identical to callout_stop
() but does not block
and allows the STOP operation to be asynchronous, meaning that the callout
structure may still be relevant after the function returns. This situation
can occur if the callback was in-progress at the time the stop was
issued.
The function
callout_cancel
()
synchronously cancels a callout and returns a value similar to that of
callout_stop
().
callout_cancel
() overrides all other operations
while it is in-progress.
The function
callout_terminate
()
synchronously cancels a callout and informs the system that the callout
structure will no longer be referenced. This function will clear the
initialization flag and any further use of the callout structure will panic
the system until it is next initialized. The callout structure can be safely
freed after this function returns, assuming other program references to it
have been removed.
The function
callout_drain
()
is identical to callout_stop
() except that it will
wait for the callout c to complete if it is already 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 before callout_drain
()
returns. However, the callout subsystem does guarantee that the callout will
be fully stopped before callout_drain
() returns.
The
callout_reset
()
function schedules a future function invocation for callout
c. If c already has a pending
callout, it is cancelled before the new invocation is scheduled. In
FreeBSD these functions return a value of one if a
pending callout was cancelled and zero if there was no pending callout. If
the callout has an associated lock, then that lock must be held when any of
these functions are called. In DragonFly these
functions return void. If the callout has an associated lock, then that lock
should generally be held when any of these functions are called, but the API
will work either way. If a callout is already in-progress, this function's
parameters will be applied when the in-progress callback returns, if not
overridden from within the callback.
The time at which the callout function will be invoked is determined by the ticks argument. The callout is scheduled to execute after ticks/hz seconds. Non-positive values of ticks are silently converted to the value ‘1’.
The
callout_reset_bycpu
()
function schedules the callout to occur on the target cpu. The normal
callout_reset
() function schedules the callout to
occur on the current cpu. The callout_reset
()
functions accept a func argument which identifies the
function to be called when the time expires. It must be a pointer to a
function that takes a single void * argument. Upon
invocation, func will receive
arg as its only argument.
The callout subsystem provides a softclock thread for each CPU in the system. Callouts are assigned to a single CPU and are executed by the softclock thread for that CPU. The callouts are assigned to the current cpu or to a specific cpu depending on the call.
The macros
callout_pending
(),
callout_active
() and
callout_deactivate
() provide access to the current
state of the callout. The callout_pending
() macro
checks whether a callout 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.
There are three main techniques for addressing these synchronization concerns. The first approach is preferred as it is the simplest:
- Callouts can be associated with a specific lock
when they are initialized by
callout_init_lk
() When a callout is associated with a lock, the callout subsystem acquires the lock before the callout function is invoked. This allows the callout subsystem to transparently handle races between callout cancellation, scheduling, and execution. Note that the associated lock must be acquired before callingcallout_stop
() orcallout_reset
() functions to provide this safety. - The
callout_pending
(),callout_active
() andcallout_deactivate
() macros can be used together to work around the race conditions, but the interpretation of these calls can be confusing and it is recommended that a different, caller-specific method be used to determine whether a race condition is present.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 without changing the active flag, and does not clear the active flag even after the callout function returns. The mechanism described here requires the callout function itself to clear the active flag using the
callout_deactivate
() macro. Thecallout_stop
() andcallout_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
() returnsTRUE
. This indicates that the callout was rescheduled usingcallout_reset
() just before the callout function was invoked. Ifcallout_active
() returnsFALSE
then the callout function should also return without action. This indicates that the callout has been stopped. Finally, the callout function should callcallout_deactivate
() to clear the active flag. For example:lockmgr(&sc->sc_lock, LK_EXCLUSIVE); if (callout_pending(&sc->sc_callout)) { /* callout was reset */ lockmgr(&sc->sc_lock, LK_RELEASE); return; } if (!callout_active(&sc->sc_callout)) { /* callout was stopped */ lockmgr(&sc->sc_lock, LK_RELEASE); return; } callout_deactivate(&sc->sc_callout); /* rest of callout function */
Together with appropriate synchronization, such as the lock used above, this approach permits the
callout_stop
() andcallout_reset
() functions to be used at any time without races. For example:lockmgr(&sc->sc_mtx, LK_EXCLUSIVE); callout_stop(&sc->sc_callout); /* The callout is effectively stopped now. */
If the callout is still pending then these functions 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 effectively enabled or disabled. If
callout_active
() returns false, then the callout is effectively disabled, since even if the callout 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
inactive, a call to
callout_cancel
()
or callout_terminate
() should be used.
RETURN VALUES
The callout_active
() macro returns the
state of a callout's active flag.
The callout_pending
() macro returns the
state of a callout's pending flag.
The callout_stop
() and
callout_drain
() functions return a value of one if
the callout was removed by the function, or zero if the callout could not be
stopped or was not running in the first place.
HISTORY
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 untimeout operations.
In DragonFly the entire API was reformulated by Matthew Dillon for optimal SMP operation, uses much larger rings, and is capable of queueing one operation concurrent with an in-progress callback without blocking.