DragonFly On-Line Manual Pages
CALLOUT(9) DragonFly Kernel Developer's Manual CALLOUT(9)
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);
void
callout_init_mp(struct callout *c);
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
The callout 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 mplock 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 this function returns 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 this
function returns 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. These 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 callout_pending(), callout_active() and callout_deactivate()
functions provide access to the current state of the callout. The
callout_pending() function 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() function checks whether a callout
is marked as active, and the callout_deactivate() function 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 two main techniques for addressing these synchronization
concerns. The first approach is preferred as it is the simplest:
1. 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 calling callout_stop()
or callout_reset() functions to provide this safety.
2. The callout_pending(), callout_active() and
callout_deactivate() functions 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
callout_deactivate(). 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() returns 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 callout function
should also return without action. This indicates that the
callout has been stopped. Finally, the callout function
should call callout_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() and
callout_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() function returns the state of a callout's active
flag.
The callout_pending() function returns the state of a callout's pending
flag.
The callout_cancel(), 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 queuing
one operation concurrent with an in-progress callback without blocking.
DragonFly 6.3-DEVELOPMENT April 15, 2022 DragonFly 6.3-DEVELOPMENT