DragonFly On-Line Manual Pages

Search: Section:  


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

Search: Section: