AG_Timer
— agar
timer facility
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:
Uint32
AG_TimerFn
(AG_Timer
*timer, AG_Event
*event);
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).
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).
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)).
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);
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.