Kernel/timers - Cleaning up timer code... might have made more mess
[tpg/acess2.git] / KernelLand / Kernel / time.c
1 /*
2  * Acess 2
3  * - By John Hodge (thePowersGang) 
4  *
5  * Timer Code
6  */
7 #define SANITY  1       // Enable ASSERTs
8 #define DEBUG   0
9 #include <acess.h>
10 #include <timers.h>
11 #include <events.h>
12 #include <hal_proc.h>   // Proc_GetCurThread
13 #include <workqueue.h>
14
15 // === TYPEDEFS ===
16 struct sTimer {
17         tTimer  *Next;
18         Sint64  FiresAfter;
19         void    (*Callback)(void*);
20         void    *Argument;
21 //      tMutex  Lock;
22         BOOL    bActive;
23 };
24
25 // === PROTOTYPES ===
26 void    Timer_CallbackThread(void *Unused);
27 void    Timer_CallTimers(void);
28 #if 0
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);
33 #endif
34 void    Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument);
35 #if 0
36 void    Time_FreeTimer(tTimer *Timer);
37 void    Time_Delay(int Time);
38 #endif
39
40 // === GLOBALS ===
41 volatile Uint64 giTicks = 0;
42 volatile Sint64 giTimestamp = 0;
43 volatile Uint64 giPartMiliseconds = 0;
44 tTimer  *gTimers;
45 tWorkqueue      gTimers_CallbackQueue;
46 tShortSpinlock  gTimers_ListLock;
47
48 // === CODE ===
49 void Timer_CallbackThread(void *Unused)
50 {
51         Threads_SetName("Timer Callback Thread");
52
53         for(;;)
54         {
55                 tTimer *timer = Workqueue_GetWork(&gTimers_CallbackQueue);
56         
57                 if( !timer->Callback ) {
58                         LOG("Timer %p doesn't have a callback", timer);
59                         ASSERT( timer->Callback );
60                 }
61
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;
66                 
67                 LOG("Callback fire %p", timer);
68         
69                 // Allow Time_RemoveTimer to be called safely
70                 timer->bActive = 0;
71                 
72                 // Fire callback
73                 cb(arg);
74
75                 // Mark timer as no longer needed
76         }
77 }
78
79 /**
80  * \fn void Timer_CallTimers()
81  */
82 void Timer_CallTimers()
83 {
84         SHORTLOCK(&gTimers_ListLock);
85         while( gTimers && gTimers->FiresAfter < now() )
86         {
87                 // Get timer from list
88                 tTimer  *timer = gTimers;
89         
90                 ASSERT( gTimers != gTimers->Next );     
91                 gTimers = gTimers->Next;
92         
93                 // Perform event
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);
99                 }
100                 else {
101                         LOG("Event fire %p", timer);
102                         ASSERT( timer->Argument );
103                         Threads_PostEvent(timer->Argument, THREAD_EVENT_TIMER);
104                         timer->bActive = 0;
105                 }
106         }
107         SHORTREL(&gTimers_ListLock);
108 }
109
110 /**
111  * \brief Schedule an action (Legacy)
112  */
113 tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
114 {
115         tTimer  *ret = malloc(sizeof(tTimer));
116         if( !ret )      return NULL;
117         Time_InitTimer(ret, Callback, Argument);
118         Time_ScheduleTimer(ret, Delta);
119         return ret;
120 }
121
122 /**
123  * \brief Schedule a timer to fire
124  */
125 void Time_ScheduleTimer(tTimer *Timer, int Delta)
126 {
127         tTimer  *t, *p;
128
129         // Sanity checks
130         if( !Timer )    return ;
131
132         if( Timer->bActive )    return;
133         
134         // Set time
135         Timer->FiresAfter = now() + Delta;
136         
137         // Debug
138         LOG("%p added timer %p - %i ms (ts=%lli)",
139                 __builtin_return_address(0), Timer, Delta, Timer->FiresAfter);
140
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 )
145         {
146                 ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
147                 if( t == Timer )
148                 {
149                         LOG("Double schedule - increasing delta");
150                         SHORTREL(&gTimers_ListLock);
151                         return ;
152                 }
153                 LOG(" t = %p ts:%lli", t, t->FiresAfter);
154                 if( t->FiresAfter > Timer->FiresAfter ) break;
155         }
156         Timer->Next = t;
157         p->Next = Timer;
158         Timer->bActive = 1;
159         LOG(" %p %p %p", p, Timer, t);
160         SHORTREL(&gTimers_ListLock);
161 }
162
163 /**
164  * \brief Delete a timer from the running list
165  */
166 void Time_RemoveTimer(tTimer *Timer)
167 {
168         tTimer  *t, *p;
169
170         if( !Timer )    return ;
171         
172         SHORTLOCK(&gTimers_ListLock);
173         for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
174         {
175                 ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
176                 if( t == Timer )
177                 {
178                         p->Next = t->Next;
179                         break ;
180                 }
181         }
182         SHORTREL(&gTimers_ListLock);
183
184         if( t ) {
185                 Timer->bActive = 0;
186                 LOG("%p removed %p", __builtin_return_address(0), Timer);
187         }
188         else
189                 LOG("%p tried to remove %p (already complete)", __builtin_return_address(0), Timer);
190 }
191
192 /**
193  * \brief Allocate a timer object, but don't schedule it
194  */
195 tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
196 {
197         tTimer  *ret = malloc(sizeof(tTimer));
198         if( !ret )      return NULL;
199         Time_InitTimer(ret, Callback, Argument);
200         return ret;
201 }
202
203 /**
204  * \brief Initialise a timer
205  * \note Mostly an internal function
206  */
207 void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument)
208 {
209         if(Callback == NULL)
210                 Argument = Proc_GetCurThread();
211         Timer->FiresAfter = 0;
212         Timer->Callback = Callback;
213         Timer->Argument = Argument;
214 //      memset( &Timer->Lock, 0, sizeof(Timer->Lock) );
215         Timer->bActive = 0;
216 }
217
218 /**
219  * \brief Free an allocated timer
220  */
221 void Time_FreeTimer(tTimer *Timer)
222 {
223         if( !Timer )    return ;
224         Time_RemoveTimer( Timer );      // Just in case
225
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 );
230         
231         // Free timer
232         free(Timer);
233         LOG("%p deallocated %p", __builtin_return_address(0), Timer);
234 }
235
236 /**
237  * \fn void Time_Delay(int Delay)
238  * \brief Delay for a small ammount of time
239  */
240 void Time_Delay(int Delay)
241 {
242         tTimer  *t;
243         t = Time_AllocateTimer(NULL, NULL);
244         Time_ScheduleTimer(t, Delay);
245         Threads_WaitEvents(THREAD_EVENT_TIMER);
246         Time_FreeTimer(t);
247 }
248
249 // === EXPORTS ===
250 //EXPORT(Time_CreateTimer);
251 EXPORT(Time_ScheduleTimer);
252 EXPORT(Time_RemoveTimer);
253 EXPORT(Time_AllocateTimer);
254 //EXPORT(Time_InitTimer);
255 EXPORT(Time_FreeTimer);
256 EXPORT(Time_Delay);

UCC git Repository :: git.ucc.asn.au