.\" Copyright (c) 2004-2022 Julien Nadeau Carriere .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED .\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, .\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES .\" (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR .\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, .\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING .\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd December 21, 2022 .Dt AG_TIMER 3 .Os Agar 1.7 .Sh NAME .Nm AG_Timer .Nd agar timer facility .Sh SYNOPSIS .Bd -literal #include .Ed .Sh DESCRIPTION .\" MANLINK(AG_Timeout) .\" MANLINK(AG_Timers) The .Nm structure describes a unique timer, which may or may not be associated with some parent .Xr 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: .Pp .nr nS 1 .Ft "Uint32" .Fn AG_TimerFn "AG_Timer *timer" "AG_Event *event" .Pp .nr nS 0 The .Fa timer argument, and the list of arguments under .Fa event are those previously specified in .Fn AG_AddTimer . The current timer interval (in ticks) can be retrieved from the .Va ival member of the .Nm 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. .Pp 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 .Xr 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 .Fn AG_ProcessTimeouts routine to process expired timers. Different objects may also manage timers differently (see .Sx SPECIALIZED TIMERS below). .Sh INTERFACE .nr nS 1 .Ft "void" .Fn AG_InitTimer "AG_Timer *timer" "const char *name" "Uint flags" .Pp .Ft "int" .Fn AG_AddTimer "void *obj" "AG_Timer *timer" "Uint32 t" "Uint32 (*fn)(AG_Timer *, AG_Event *)" "const char *fmt" "..." .Pp .Ft "AG_Timer *" .Fn AG_AddTimerAuto "void *obj" "Uint32 t" "Uint32 (*fn)(AG_Timer *, AG_Event *)" "const char *fmt" "..." .Pp .Ft "void" .Fn AG_DelTimer "void *obj" "AG_Timer *timer" .Pp .Ft "void" .Fn AG_DelTimers "void *obj" .Pp .Ft "int" .Fn AG_ResetTimer "void *obj" "AG_Timer *timer" "Uint32 t" .Pp .Ft "void" .Fn AG_LockTimers "void *obj" .Pp .Ft "void" .Fn AG_UnlockTimers "void *obj" .Pp .Ft "int" .Fn AG_TimerIsRunning "void *obj" "AG_Timer *timer" .Pp .Ft "Uint32" .Fn AG_ExecTimer "AG_Timer *timer" .Pp .Ft "void" .Fn AG_ProcessTimeouts "Uint32 ticks" .Pp .nr nS 0 The .Fn AG_InitTimer routine initializes a .Nm structure. .Fa name is an optional string identifier, useful for debugging purposes. Acceptable .Fa flags options include: .Bl -tag -width "AG_TIMER_SURVIVE_DETACH " .It Dv AG_TIMER_SURVIVE_DETACH Don't automatically cancel the timer if its parent object is being detached (see .Xr AG_ObjectDetach 3 ) . .It Dv AG_TIMER_AUTO_FREE Automatically free() the timer structure upon expiration or cancellation (set implicitely by .Fn AG_AddTimerAuto ) . .El .Pp The .Fn AG_AddTimer function starts the timer. The optional .Fa obj argument specifies a parent .Xr AG_Object 3 which will manage the timer. The callback routine is specified as the .Fn fn argument. Arguments to pass to the callback may be specified under .Fa fmt (using the .Xr AG_Event 3 style of argument passing). The .Fn AG_AddTimer function returns 0 on success or -1 if the timer could not be created. .Pp Timers created with .Fn AG_AddTimer are set to expire in .Fa 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. .Pp The .Fn AG_AddTimerAuto variant of .Fn AG_AddTimer allocates an anonymous .Ft 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 .Fn AG_LockTimers is in effect). On failure, .Fn AG_AddTimerAuto returns NULL. .Pp The .Fn AG_DelTimer function cancels the execution of a timer and frees all resources allocated by it. If the given timer is not active, .Fn AG_DelTimer does nothing. The optional .Fa obj argument specifies the timer's parent object. The .Fa timer argument does not need to point to an initialized structure. If the timer is not running, .Fn AG_DelTimer is a safe no-op. .Pp The .Fn AG_ResetTimer function changes the interval of a running timer, such that it will expire in .Fa t ticks from now. It is illegal to invoke .Fn AG_ResetTimer on a timer that is not currently running, and the call must be protected by .Fn AG_LockTimers . .Pp In the timer callback routine, it is safe to make .Fn AG_AddTimer or .Fn AG_DelTimer calls. It is not safe to try and detach or destroy the timer's parent object from the callback routine. .Pp The .Fn AG_TimerIsRunning function returns 1 if the timer is active. For thread safety, the call should be protected by .Fn AG_LockTimers : .Bd -literal -offset indent .\" SYNTAX(c) AG_LockTimers(obj); if (AG_TimerIsRunning(obj, &timer)) { ... } AG_UnlockTimers(obj); .Ed .Pp .Fn AG_ExecTimer runs the timer's callback routine artificially and returns its return value. The caller is normally expected to use .Fn AG_DelTimer on a return value of 0 and .Fn AG_ResetTimer if the returned interval differs from current .Va to->ival . .Pp The .Fn 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., .Fn AG_ProcessTimeouts may be called repeatedly from a delay loop). The .Fa ticks argument is the monotonic time in ticks (usually obtained from .Xr AG_GetTicks 3 ) . For .Fn AG_ProcessTimeouts to work as expected, the .Dv AG_SOFT_TIMERS flag must be passed to .Xr AG_InitCore 3 . .Sh SPECIALIZED TIMERS The .Nm interface is not tied to any specific time source. A timer's parent object may influence the way timers are processed. .Pp 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 .Xr AG_Time 3 ) . .Sh EXAMPLES The following code creates 3 one-shot and 1 regular timer: .Bd -literal -offset indent .\" SYNTAX(c) 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); .Ed .Sh SEE ALSO .Xr AG_Event 3 , .Xr AG_GetTicks 3 , .Xr AG_Intro 3 , .Xr AG_Object 3 , .Xr AG_SchedEvent 3 , .Xr AG_Time 3 .Rs .%T "Hashed and Hierarchical Timing Wheels: Efficient Data Structures for Implementing a Timer Facility" .%A "George Varghese" .%A "Tony Lauck" .%D "February 14, 1996" .Re .Sh HISTORY The .Nm facility first appeared in Agar 1.0 as .Ft AG_Timeout . It is modeled after the OpenBSD .Xr timeout 9 API by Artur Grabowski and Thomas Nordin. Support for multiple arguments in callback routines was added in Agar 1.5. Support for .Xr kqueue 2 appeared in Agar 1.5.