3 * - By John Hodge (thePowersGang)
7 #define SANITY 1 // Enable ASSERTs
12 #include <hal_proc.h> // Proc_GetCurThread
13 #include <workqueue.h>
19 void (*Callback)(void*);
26 void Timer_CallbackThread(void *Unused);
27 void Timer_CallTimers(void);
29 tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument);
30 void Time_ScheduleTimer(tTimer *Timer, int Delta);
31 void Time_RemoveTimer(tTimer *Timer);
32 tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument);
34 void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument);
36 void Time_FreeTimer(tTimer *Timer);
37 void Time_Delay(int Time);
41 volatile Uint64 giTicks = 0;
42 volatile Sint64 giTimestamp = 0;
43 volatile Uint64 giPartMiliseconds = 0;
45 tWorkqueue gTimers_CallbackQueue;
46 tShortSpinlock gTimers_ListLock;
49 void Timer_CallbackThread(void *Unused)
51 Threads_SetName("Timer Callback Thread");
55 tTimer *timer = Workqueue_GetWork(&gTimers_CallbackQueue);
57 if( !timer->Callback ) {
58 LOG("Timer %p doesn't have a callback", timer);
59 ASSERT( timer->Callback );
62 // Save callback and argument (because once the mutex is released
63 // the timer may no longer be valid)
64 tTimerCallback *cb = timer->Callback;
65 void *arg = timer->Argument;
67 LOG("Callback fire %p", timer);
69 // Allow Time_RemoveTimer to be called safely
75 // Mark timer as no longer needed
80 * \fn void Timer_CallTimers()
82 void Timer_CallTimers()
84 SHORTLOCK(&gTimers_ListLock);
85 while( gTimers && gTimers->FiresAfter < now() )
87 // Get timer from list
88 tTimer *timer = gTimers;
90 ASSERT( gTimers != gTimers->Next );
91 gTimers = gTimers->Next;
94 if( timer->Callback ) {
95 LOG("Callback schedule %p", timer);
96 // PROBLEM! Possibly causes rescheudle during interrupt
97 // Mutex_Acquire( &timer->Lock ); // Released once the callback fires
98 Workqueue_AddWork(&gTimers_CallbackQueue, timer);
101 LOG("Event fire %p", timer);
102 ASSERT( timer->Argument );
103 Threads_PostEvent(timer->Argument, THREAD_EVENT_TIMER);
107 SHORTREL(&gTimers_ListLock);
111 * \brief Schedule an action (Legacy)
113 tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
115 tTimer *ret = malloc(sizeof(tTimer));
116 if( !ret ) return NULL;
117 Time_InitTimer(ret, Callback, Argument);
118 Time_ScheduleTimer(ret, Delta);
123 * \brief Schedule a timer to fire
125 void Time_ScheduleTimer(tTimer *Timer, int Delta)
130 if( !Timer ) return ;
132 if( Timer->bActive ) return;
135 Timer->FiresAfter = now() + Delta;
138 LOG("%p added timer %p - %i ms (ts=%lli)",
139 __builtin_return_address(0), Timer, Delta, Timer->FiresAfter);
141 // Add into list (sorted)
142 SHORTLOCK(&gTimers_ListLock);
143 // Mutex_Release( &Timer->Lock ); // Prevent deadlocks
144 for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
146 ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
149 LOG("Double schedule - increasing delta");
150 SHORTREL(&gTimers_ListLock);
153 LOG(" t = %p ts:%lli", t, t->FiresAfter);
154 if( t->FiresAfter > Timer->FiresAfter ) break;
159 LOG(" %p %p %p", p, Timer, t);
160 SHORTREL(&gTimers_ListLock);
164 * \brief Delete a timer from the running list
166 void Time_RemoveTimer(tTimer *Timer)
170 if( !Timer ) return ;
172 SHORTLOCK(&gTimers_ListLock);
173 for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
175 ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
182 SHORTREL(&gTimers_ListLock);
186 LOG("%p removed %p", __builtin_return_address(0), Timer);
189 LOG("%p tried to remove %p (already complete)", __builtin_return_address(0), Timer);
193 * \brief Allocate a timer object, but don't schedule it
195 tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
197 tTimer *ret = malloc(sizeof(tTimer));
198 if( !ret ) return NULL;
199 Time_InitTimer(ret, Callback, Argument);
204 * \brief Initialise a timer
205 * \note Mostly an internal function
207 void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument)
210 Argument = Proc_GetCurThread();
211 Timer->FiresAfter = 0;
212 Timer->Callback = Callback;
213 Timer->Argument = Argument;
214 // memset( &Timer->Lock, 0, sizeof(Timer->Lock) );
219 * \brief Free an allocated timer
221 void Time_FreeTimer(tTimer *Timer)
223 if( !Timer ) return ;
224 Time_RemoveTimer( Timer ); // Just in case
226 // Ensures that we don't free until the timer callback has started
227 while( Timer->bActive ) Threads_Yield();
228 // Release won't be needed, as nothing should be waiting on it
229 // Mutex_Acquire( &Timer->Lock );
233 LOG("%p deallocated %p", __builtin_return_address(0), Timer);
237 * \fn void Time_Delay(int Delay)
238 * \brief Delay for a small ammount of time
240 void Time_Delay(int Delay)
243 t = Time_AllocateTimer(NULL, NULL);
244 Time_ScheduleTimer(t, Delay);
245 Threads_WaitEvents(THREAD_EVENT_TIMER);
250 //EXPORT(Time_CreateTimer);
251 EXPORT(Time_ScheduleTimer);
252 EXPORT(Time_RemoveTimer);
253 EXPORT(Time_AllocateTimer);
254 //EXPORT(Time_InitTimer);
255 EXPORT(Time_FreeTimer);