SYNOPSIS
#include <agar/core.h>
DESCRIPTION
The
AG_Timer structure describes a unique timer, which may or may not be associated with
some parent
AG_Object(3). If a timer has a parent object, Agar will guarantee cancellation of any
callbacks if the parent object is destroyed or detached.
When a timer expires, its callback routine is executed.
Timer callback routines should be defined as:
The timer argument, and the list of arguments under event are those previously specified in AG_AddTimer(). The current timer interval (in ticks) can be retrieved from the ival member of the AG_Timer structure. The callback should return a new timer interval (if the timer is to be restarted), or 0 if the timer is to be cancelled.
The timer's parent object is guaranteed to remain locked during the execution of the callback. The context of execution of the callback is platform-dependent. On platforms where kqueue(2) is available, the routine is executed in the event loop. On platforms where only POSIX timers are available, the routine is executed in a separate thread. On platforms which don't provide any timer interface at all, the event loop repeatedly calls AG_ProcessTimeouts() routine to process expired timers. Different objects may also manage timers differently (see SPECIALIZED TIMERS below).
The timer argument, and the list of arguments under event are those previously specified in AG_AddTimer(). The current timer interval (in ticks) can be retrieved from the ival member of the AG_Timer structure. The callback should return a new timer interval (if the timer is to be restarted), or 0 if the timer is to be cancelled.
The timer's parent object is guaranteed to remain locked during the execution of the callback. The context of execution of the callback is platform-dependent. On platforms where kqueue(2) is available, the routine is executed in the event loop. On platforms where only POSIX timers are available, the routine is executed in a separate thread. On platforms which don't provide any timer interface at all, the event loop repeatedly calls AG_ProcessTimeouts() routine to process expired timers. Different objects may also manage timers differently (see SPECIALIZED TIMERS below).
INTERFACE
void AG_InitTimer (AG_Timer *timer, const char *name, Uint flags)
int AG_AddTimer (void *obj, AG_Timer *timer, Uint32 t, Uint32 (*fn)(AG_Timer *, AG_Event *), const char *fmt, ...)
AG_Timer * AG_AddTimerAuto (void *obj, Uint32 t, Uint32 (*fn)(AG_Timer *, AG_Event *), const char *fmt, ...)
void AG_DelTimer (void *obj, AG_Timer *timer)
void AG_DelTimers (void *obj)
int AG_ResetTimer (void *obj, AG_Timer *timer, Uint32 t)
void AG_LockTimers (void *obj)
void AG_UnlockTimers (void *obj)
int AG_TimerIsRunning (void *obj, AG_Timer *timer)
Uint32 AG_ExecTimer (AG_Timer *timer)
void AG_ProcessTimeouts (Uint32 ticks)
The AG_InitTimer() routine initializes a AG_Timer structure. name is an optional string identifier, useful for debugging purposes. Acceptable flags options include:
AG_TIMER_SURVIVE_DETACH | Don't automatically cancel the timer if its parent object is being detached (see AG_ObjectDetach(3)). |
AG_TIMER_AUTO_FREE | Automatically free() the timer structure upon expiration or cancellation (set implicitely by AG_AddTimerAuto()). |
The AG_AddTimer() function starts the timer. The optional obj argument specifies a parent AG_Object(3) which will manage the timer. The callback routine is specified as the fn() argument. Arguments to pass to the callback may be specified under fmt (using the AG_Event(3) style of argument passing). The AG_AddTimer() function returns 0 on success or -1 if the timer could not be created.
Timers created with AG_AddTimer() are set to expire in t ticks from now. On expiration, the timer's callback is invoked. If it returns a non-zero number of ticks, the timer is restarted, otherwise it is cancelled.
The AG_AddTimerAuto() variant of AG_AddTimer() allocates an anonymous AG_Timer structure which will be freed upon cancellation. On success, a pointer to the new timer structure is returned (it is not safe to dereference this pointer unless AG_LockTimers() is in effect). On failure, AG_AddTimerAuto() returns NULL.
The AG_DelTimer() function cancels the execution of a timer and frees all resources allocated by it. If the given timer is not active, AG_DelTimer() does nothing. The optional obj argument specifies the timer's parent object. The timer argument does not need to point to an initialized structure. If the timer is not running, AG_DelTimer() is a safe no-op.
The AG_ResetTimer() function changes the interval of a running timer, such that it will expire in t ticks from now. It is illegal to invoke AG_ResetTimer() on a timer that is not currently running, and the call must be protected by AG_LockTimers().
In the timer callback routine, it is safe to make AG_AddTimer() or AG_DelTimer() calls. It is not safe to try and detach or destroy the timer's parent object from the callback routine.
The AG_TimerIsRunning() function returns 1 if the timer is active. For thread safety, the call should be protected by AG_LockTimers():
AG_LockTimers(obj); if (AG_TimerIsRunning(obj, &timer)) { ... } AG_UnlockTimers(obj);
AG_ExecTimer() runs the timer's callback routine artificially and returns its return value. The caller is normally expected to use AG_DelTimer() on a return value of 0 and AG_ResetTimer() if the returned interval differs from current to->ival.
The AG_ProcessTimeouts() function advances the timing wheel and executes the callbacks of expired timers. Normally, this function is not used directly, but it can be useful on platforms without timer interfaces (i.e., AG_ProcessTimeouts() may be called repeatedly from a delay loop). The ticks argument is the monotonic time in ticks (usually obtained from AG_GetTicks(3)). For AG_ProcessTimeouts() to work as expected, the AG_SOFT_TIMERS flag must be passed to AG_InitCore(3).
SPECIALIZED TIMERS
The
AG_Timer interface is not tied to any specific time source.
A timer's parent object may influence the way timers are processed.
By default, the execution of timers is based on the progress of a monotonic system clock and one "tick" is roughly equivalent to one millisecond. However, it is possible for different parent objects to process time differently. For example, an object in a simulation application might manage its timers using use some software-defined time, or an offline renderer might require that logic time be stopped during rendering (see AG_Time(3)).
By default, the execution of timers is based on the progress of a monotonic system clock and one "tick" is roughly equivalent to one millisecond. However, it is possible for different parent objects to process time differently. For example, an object in a simulation application might manage its timers using use some software-defined time, or an offline renderer might require that logic time be stopped during rendering (see AG_Time(3)).
EXAMPLES
The following code creates 3 one-shot and 1 regular timer:
static Uint32 Timeout1(AG_Timer *to, AG_Event *event) { AG_Verbose("This message should appear first\n"); return (0); } static Uint32 Timeout2(AG_Timer *to, AG_Event *event) { AG_Verbose("This message should appear second\n"); return (0); } static Uint32 Timeout3(AG_Timer *to, AG_Event *event) { AG_Verbose("This message should appear last\n"); return (0); } static Uint32 TimeoutReg(AG_Timer *to, AG_Event *event) { AG_Verbose("Tick #%u\n", AG_GetTicks()); return (to->ival); } AG_Object obj; AG_ObjectInit(&obj, NULL, &agObjectClass); AG_AddTimerAuto(obj, 1000, Timeout1,NULL); AG_AddTimerAuto(obj, 2000, Timeout2,NULL); AG_AddTimerAuto(obj, 2100, Timeout3,NULL); AG_AddTimerAuto(obj, 1000, TimeoutReg,NULL);
SEE ALSO
AG_Event(3), AG_GetTicks(3), AG_Intro(3), AG_Object(3), AG_SchedEvent(3), AG_Time(3)
- "Hashed and Hierarchical Timing Wheels: Efficient Data Structures for Implementing a Timer Facility"
George Varghese, Tony Lauck, February 14, 1996
HISTORY
The
AG_Timer facility first appeared in
Agar 1.0 as
AG_Timeout. It is modeled after the OpenBSD
timeout(9) API by Artur Grabowski and Thomas Nordin.
Support for multiple arguments in callback routines was added in
Agar 1.5.
Support for
kqueue(2) appeared in
Agar 1.5.