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 after a specified length of time
typedef void timeout_t (void *);struct callout_handle timeout (timeout_t *func void *arg int ticks);
struct callout_handle handle = CALLOUT_HANDLE_INITIALIZER(&handle)void untimeout (timeout_t *func void *arg struct callout_handle handle);
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 ();
performs the same function as
callout_handle_init ();
and is provided for use on statically declared or global callout handles.
The function
untimeout ();
cancels the timeout associated with
Fa handle
using the
Fa func
and
Fa arg
arguments to validate the handle.
If the handle does not correspond to a timeout with
the function
Fa func
taking the argument
Fa arg
no action is taken.
Fa 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 (although 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-entrancy.
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
Fa mpsafe
argument is zero,
the callout structure is not considered to be
``multi-processor safe''
that is,
the Giant lock will be acquired before calling the callout function,
and released when the callout function returns.
The
callout_init_mtx ();
function may be used as an alternative to
callout_init (.);
The parameter
Fa 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 following
Fa flags
may be specified:
The function
callout_stop ();
cancels a callout if it is currently 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
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 during the execution of
callout_drain (.);
However, the callout subsystem does guarantee that the callout 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 establishes a new callout in the
same manner as
timeout (.);
If there was already a pending callout and it was rescheduled, then
callout_reset ();
will return a non-zero 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 inherent in asynchronous timer facilities; see
Sx Avoiding Race Conditions
below for further details.
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.
The callout subsystem provides a number of mechanisms to address these synchronization concerns:
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 */ } }
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:
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 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 finished, a call to
callout_drain ();
should be used.
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |