3 * - By John Hodge (thePowersGang)
7 #define SANITY 1 // Enable ASSERTs
11 #include <timers_int.h>
13 #include <hal_proc.h> // Proc_GetCurThread
14 #include <workqueue.h>
15 #include <threads_int.h> // Used to get thread timer
18 void Timer_CallbackThread(void *Unused);
19 void Timer_CallTimers(void);
21 tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument);
22 void Time_ScheduleTimer(tTimer *Timer, int Delta);
23 void Time_RemoveTimer(tTimer *Timer);
24 tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument);
26 void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument);
28 void Time_FreeTimer(tTimer *Timer);
29 void Time_Delay(int Time);
33 volatile Uint64 giTicks = 0;
34 volatile Sint64 giTimestamp = 0;
35 volatile Uint64 giPartMiliseconds = 0;
37 tWorkqueue gTimers_CallbackQueue;
38 tShortSpinlock gTimers_ListLock;
41 void Timer_CallbackThread(void *Unused)
43 Threads_SetName("Timer Callback Thread");
44 Workqueue_Init(&gTimers_CallbackQueue, "Timer Callbacks", offsetof(tTimer, Next));
48 tTimer *timer = Workqueue_GetWork(&gTimers_CallbackQueue);
50 if( !timer->Callback ) {
51 LOG("Timer %p doesn't have a callback", timer);
52 ASSERT( timer->Callback );
55 // Save callback and argument (because once the mutex is released
56 // the timer may no longer be valid)
57 tTimerCallback *cb = timer->Callback;
58 void *arg = timer->Argument;
60 LOG("Callback fire %p", timer);
62 // Allow Time_RemoveTimer to be called safely
68 // Mark timer as no longer needed
73 * \fn void Timer_CallTimers()
75 void Timer_CallTimers()
77 // Tick the random number generator every time timers are checked
80 SHORTLOCK(&gTimers_ListLock);
81 LOG("gTimers = %p (%lli ms)", gTimers, (gTimers ? gTimers->FiresAfter : 0));
82 while( gTimers && gTimers->FiresAfter < now() )
84 ASSERT( gTimers != gTimers->Next );
85 // Get timer from list
86 tTimer *timer = gTimers;
87 gTimers = gTimers->Next;
90 if( timer->Callback ) {
91 LOG("Callback schedule %p", timer);
92 Workqueue_AddWork(&gTimers_CallbackQueue, timer);
95 LOG("Event fire %p", timer);
96 ASSERT( timer->Argument );
97 Threads_PostEvent(timer->Argument, THREAD_EVENT_TIMER);
101 SHORTREL(&gTimers_ListLock);
105 * \brief Schedule an action (Legacy)
107 tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
109 tTimer *ret = malloc(sizeof(tTimer));
110 if( !ret ) return NULL;
111 Time_InitTimer(ret, Callback, Argument);
112 Time_ScheduleTimer(ret, Delta);
117 * \brief Schedule a timer to fire
119 void Time_ScheduleTimer(tTimer *Timer, int Delta)
125 if( !Timer ) return ;
127 if( Timer->bActive ) return;
130 Timer->FiresAfter = now() + Delta;
133 LOG("%p added timer %p - %i ms (ts=%lli)",
134 __builtin_return_address(0), Timer, Delta, Timer->FiresAfter);
136 // Add into list (sorted)
137 SHORTLOCK(&gTimers_ListLock);
138 // Mutex_Release( &Timer->Lock ); // Prevent deadlocks
139 for( prev_next = &gTimers, t = gTimers; t; prev_next = &t->Next, t = t->Next )
141 ASSERT( prev_next != &t->Next );
142 ASSERT( CheckMem(t, sizeof(tTimer)) );
145 LOG("Double schedule - increasing delta");
146 SHORTREL(&gTimers_ListLock);
149 LOG(" t = %p ts:%lli", t, t->FiresAfter);
150 if( t->FiresAfter > Timer->FiresAfter ) break;
155 LOG(" prev_next=%p Timer=%p next=%p", prev_next, Timer, t);
156 SHORTREL(&gTimers_ListLock);
160 * \brief Delete a timer from the running list
162 void Time_RemoveTimer(tTimer *Timer)
167 if( !Timer ) return ;
169 SHORTLOCK(&gTimers_ListLock);
170 for( prev_next = &gTimers, t = gTimers; t; prev_next = &t->Next, t = t->Next )
172 ASSERT( prev_next != &t->Next );
173 ASSERT( CheckMem(t, sizeof(tTimer)) );
176 *prev_next = t->Next;
180 SHORTREL(&gTimers_ListLock);
184 LOG("%p removed %p", __builtin_return_address(0), Timer);
187 LOG("%p tried to remove %p (already complete)", __builtin_return_address(0), Timer);
191 * \brief Allocate a timer object, but don't schedule it
193 tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
195 tTimer *ret = malloc(sizeof(tTimer));
196 if( !ret ) return NULL;
197 Time_InitTimer(ret, Callback, Argument);
202 * \brief Initialise a timer
203 * \note Mostly an internal function
205 void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument)
208 Argument = Proc_GetCurThread();
209 Timer->FiresAfter = 0;
210 Timer->Callback = Callback;
211 Timer->Argument = Argument;
212 // memset( &Timer->Lock, 0, sizeof(Timer->Lock) );
214 LOG("Initialised timer %p (cb=%p,arg=%p)", Timer, Callback, Argument);
218 * \brief Free an allocated timer
220 void Time_FreeTimer(tTimer *Timer)
222 if( !Timer ) return ;
223 Time_RemoveTimer( Timer ); // Just in case
225 // Ensures that we don't free until the timer callback has started
226 while( Timer->bActive ) Threads_Yield();
227 // Release won't be needed, as nothing should be waiting on it
228 // Mutex_Acquire( &Timer->Lock );
232 LOG("%p deallocated %p", __builtin_return_address(0), Timer);
235 void Time_ScheduleEvent(int Delay)
237 tTimer *t = &Proc_GetCurThread()->ThreadTimer;
239 Time_InitTimer(t, NULL, NULL);
240 Time_ScheduleTimer(t, Delay);
244 * \fn void Time_Delay(int Delay)
245 * \brief Delay for a small ammount of time
247 void Time_Delay(int Delay)
250 Threads_ClearEvent(THREAD_EVENT_TIMER);
251 Time_ScheduleEvent(Delay);
252 Threads_WaitEvents(THREAD_EVENT_TIMER);
256 //EXPORT(Time_CreateTimer);
257 EXPORT(Time_ScheduleTimer);
258 EXPORT(Time_RemoveTimer);
259 EXPORT(Time_AllocateTimer);
260 //EXPORT(Time_InitTimer);
261 EXPORT(Time_FreeTimer);