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");
52 Workqueue_Init(&gTimers_CallbackQueue, "Timer Callbacks", offsetof(tTimer, Next));
56 tTimer *timer = Workqueue_GetWork(&gTimers_CallbackQueue);
58 if( !timer->Callback ) {
59 LOG("Timer %p doesn't have a callback", timer);
60 ASSERT( timer->Callback );
63 // Save callback and argument (because once the mutex is released
64 // the timer may no longer be valid)
65 tTimerCallback *cb = timer->Callback;
66 void *arg = timer->Argument;
68 LOG("Callback fire %p", timer);
70 // Allow Time_RemoveTimer to be called safely
76 // Mark timer as no longer needed
81 * \fn void Timer_CallTimers()
83 void Timer_CallTimers()
85 SHORTLOCK(&gTimers_ListLock);
86 while( gTimers && gTimers->FiresAfter < now() )
88 // Get timer from list
89 tTimer *timer = gTimers;
91 ASSERT( gTimers != gTimers->Next );
92 gTimers = gTimers->Next;
95 if( timer->Callback ) {
96 LOG("Callback schedule %p", timer);
97 // PROBLEM! Possibly causes rescheudle during interrupt
98 // Mutex_Acquire( &timer->Lock ); // Released once the callback fires
99 Workqueue_AddWork(&gTimers_CallbackQueue, timer);
102 LOG("Event fire %p", timer);
103 ASSERT( timer->Argument );
104 Threads_PostEvent(timer->Argument, THREAD_EVENT_TIMER);
108 SHORTREL(&gTimers_ListLock);
112 * \brief Schedule an action (Legacy)
114 tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
116 tTimer *ret = malloc(sizeof(tTimer));
117 if( !ret ) return NULL;
118 Time_InitTimer(ret, Callback, Argument);
119 Time_ScheduleTimer(ret, Delta);
124 * \brief Schedule a timer to fire
126 void Time_ScheduleTimer(tTimer *Timer, int Delta)
131 if( !Timer ) return ;
133 if( Timer->bActive ) return;
136 Timer->FiresAfter = now() + Delta;
139 LOG("%p added timer %p - %i ms (ts=%lli)",
140 __builtin_return_address(0), Timer, Delta, Timer->FiresAfter);
142 // Add into list (sorted)
143 SHORTLOCK(&gTimers_ListLock);
144 // Mutex_Release( &Timer->Lock ); // Prevent deadlocks
145 for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
147 ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
150 LOG("Double schedule - increasing delta");
151 SHORTREL(&gTimers_ListLock);
154 LOG(" t = %p ts:%lli", t, t->FiresAfter);
155 if( t->FiresAfter > Timer->FiresAfter ) break;
160 LOG(" %p %p %p", p, Timer, t);
161 SHORTREL(&gTimers_ListLock);
165 * \brief Delete a timer from the running list
167 void Time_RemoveTimer(tTimer *Timer)
171 if( !Timer ) return ;
173 SHORTLOCK(&gTimers_ListLock);
174 for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
176 ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
183 SHORTREL(&gTimers_ListLock);
187 LOG("%p removed %p", __builtin_return_address(0), Timer);
190 LOG("%p tried to remove %p (already complete)", __builtin_return_address(0), Timer);
194 * \brief Allocate a timer object, but don't schedule it
196 tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
198 tTimer *ret = malloc(sizeof(tTimer));
199 if( !ret ) return NULL;
200 Time_InitTimer(ret, Callback, Argument);
205 * \brief Initialise a timer
206 * \note Mostly an internal function
208 void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument)
211 Argument = Proc_GetCurThread();
212 Timer->FiresAfter = 0;
213 Timer->Callback = Callback;
214 Timer->Argument = Argument;
215 // memset( &Timer->Lock, 0, sizeof(Timer->Lock) );
220 * \brief Free an allocated timer
222 void Time_FreeTimer(tTimer *Timer)
224 if( !Timer ) return ;
225 Time_RemoveTimer( Timer ); // Just in case
227 // Ensures that we don't free until the timer callback has started
228 while( Timer->bActive ) Threads_Yield();
229 // Release won't be needed, as nothing should be waiting on it
230 // Mutex_Acquire( &Timer->Lock );
234 LOG("%p deallocated %p", __builtin_return_address(0), Timer);
238 * \fn void Time_Delay(int Delay)
239 * \brief Delay for a small ammount of time
241 void Time_Delay(int Delay)
244 t = Time_AllocateTimer(NULL, NULL);
245 Time_ScheduleTimer(t, Delay);
246 Threads_WaitEvents(THREAD_EVENT_TIMER);
251 //EXPORT(Time_CreateTimer);
252 EXPORT(Time_ScheduleTimer);
253 EXPORT(Time_RemoveTimer);
254 EXPORT(Time_AllocateTimer);
255 //EXPORT(Time_InitTimer);
256 EXPORT(Time_FreeTimer);