man.bsd.lv manual page server

Manual Page Search Parameters

TIMEOUT(9) Kernel Developer's Manual TIMEOUT(9)

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_terminateexecute a function after a specified length of time

#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);

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 (), (), 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 () 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 () 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 ().

The function () 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 () 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 () 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 () 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 () 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 () 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_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 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:

  1. Callouts can be associated with a specific lock when they are initialized by () 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() 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 () 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 () 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 () 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 () call.

    The above technique additionally ensures that the active flag always reflects whether the callout is effectively enabled or disabled. If () 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 () or callout_terminate() should be used.

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.

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.

March 26, 2019 DragonFly-5.6.1