15b61907216536d3af5b50b178c8ace8866f431d
[tpg/acess2.git] / KernelLand / Kernel / time.c
1 /*
2  * Acess2 Kernel
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 <timers_int.h>
12 #include <events.h>
13 #include <hal_proc.h>   // Proc_GetCurThread
14 #include <workqueue.h>
15 #include <threads_int.h>        // Used to get thread timer
16
17 // === PROTOTYPES ===
18 void    Timer_CallbackThread(void *Unused);
19 void    Timer_CallTimers(void);
20 #if 0
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);
25 #endif
26 void    Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument);
27 #if 0
28 void    Time_FreeTimer(tTimer *Timer);
29 void    Time_Delay(int Time);
30 #endif
31
32 // === GLOBALS ===
33 volatile Uint64 giTicks = 0;
34 volatile Sint64 giTimestamp = 0;
35 volatile Uint64 giPartMiliseconds = 0;
36 tTimer  *gTimers;
37 tWorkqueue      gTimers_CallbackQueue;
38 tShortSpinlock  gTimers_ListLock;
39
40 // === CODE ===
41 void Timer_CallbackThread(void *Unused)
42 {
43         Threads_SetName("Timer Callback Thread");
44         Workqueue_Init(&gTimers_CallbackQueue, "Timer Callbacks", offsetof(tTimer, Next));
45
46         for(;;)
47         {
48                 tTimer *timer = Workqueue_GetWork(&gTimers_CallbackQueue);
49         
50                 if( !timer->Callback ) {
51                         LOG("Timer %p doesn't have a callback", timer);
52                         ASSERT( timer->Callback );
53                 }
54
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;
59                 
60                 LOG("Callback fire %p", timer);
61         
62                 // Allow Time_RemoveTimer to be called safely
63                 timer->bActive = 0;
64                 
65                 // Fire callback
66                 cb(arg);
67
68                 // Mark timer as no longer needed
69         }
70 }
71
72 /**
73  * \fn void Timer_CallTimers()
74  */
75 void Timer_CallTimers()
76 {
77         // Tick the random number generator every time timers are checked
78         rand();
79         
80         SHORTLOCK(&gTimers_ListLock);
81         while( gTimers && gTimers->FiresAfter < now() )
82         {
83                 ASSERT( gTimers != gTimers->Next );     
84                 // Get timer from list
85                 tTimer  *timer = gTimers;
86                 gTimers = gTimers->Next;
87         
88                 // Perform event
89                 if( timer->Callback ) {
90                         LOG("Callback schedule %p", timer);
91                         Workqueue_AddWork(&gTimers_CallbackQueue, timer);
92                 }
93                 else {
94                         LOG("Event fire %p", timer);
95                         ASSERT( timer->Argument );
96                         Threads_PostEvent(timer->Argument, THREAD_EVENT_TIMER);
97                         timer->bActive = 0;
98                 }
99         }
100         SHORTREL(&gTimers_ListLock);
101 }
102
103 /**
104  * \brief Schedule an action (Legacy)
105  */
106 tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
107 {
108         tTimer  *ret = malloc(sizeof(tTimer));
109         if( !ret )      return NULL;
110         Time_InitTimer(ret, Callback, Argument);
111         Time_ScheduleTimer(ret, Delta);
112         return ret;
113 }
114
115 /**
116  * \brief Schedule a timer to fire
117  */
118 void Time_ScheduleTimer(tTimer *Timer, int Delta)
119 {
120         tTimer  *t;
121         tTimer  **prev_next;
122
123         // Sanity checks
124         if( !Timer )    return ;
125
126         if( Timer->bActive )    return;
127         
128         // Set time
129         Timer->FiresAfter = now() + Delta;
130         
131         // Debug
132         LOG("%p added timer %p - %i ms (ts=%lli)",
133                 __builtin_return_address(0), Timer, Delta, Timer->FiresAfter);
134
135         // Add into list (sorted)
136         SHORTLOCK(&gTimers_ListLock);
137 //      Mutex_Release( &Timer->Lock );  // Prevent deadlocks
138         for( prev_next = &gTimers, t = gTimers; t; prev_next = &t->Next, t = t->Next )
139         {
140                 ASSERT( prev_next != &t->Next );
141                 ASSERT( CheckMem(t, sizeof(tTimer)) );
142                 if( t == Timer )
143                 {
144                         LOG("Double schedule - increasing delta");
145                         SHORTREL(&gTimers_ListLock);
146                         return ;
147                 }
148                 LOG(" t = %p ts:%lli", t, t->FiresAfter);
149                 if( t->FiresAfter > Timer->FiresAfter ) break;
150         }
151         Timer->Next = t;
152         *prev_next = Timer;
153         Timer->bActive = 1;
154         LOG(" prev_next=%p Timer=%p next=%p", prev_next, Timer, t);
155         SHORTREL(&gTimers_ListLock);
156 }
157
158 /**
159  * \brief Delete a timer from the running list
160  */
161 void Time_RemoveTimer(tTimer *Timer)
162 {
163         tTimer  *t;
164         tTimer  **prev_next;
165
166         if( !Timer )    return ;
167         
168         SHORTLOCK(&gTimers_ListLock);
169         for( prev_next = &gTimers, t = gTimers; t; prev_next = &t->Next, t = t->Next )
170         {
171                 ASSERT( prev_next != &t->Next );
172                 ASSERT( CheckMem(t, sizeof(tTimer)) );
173                 if( t == Timer )
174                 {
175                         *prev_next = t->Next;
176                         break ;
177                 }
178         }
179         SHORTREL(&gTimers_ListLock);
180
181         if( t ) {
182                 Timer->bActive = 0;
183                 LOG("%p removed %p", __builtin_return_address(0), Timer);
184         }
185         else
186                 LOG("%p tried to remove %p (already complete)", __builtin_return_address(0), Timer);
187 }
188
189 /**
190  * \brief Allocate a timer object, but don't schedule it
191  */
192 tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
193 {
194         tTimer  *ret = malloc(sizeof(tTimer));
195         if( !ret )      return NULL;
196         Time_InitTimer(ret, Callback, Argument);
197         return ret;
198 }
199
200 /**
201  * \brief Initialise a timer
202  * \note Mostly an internal function
203  */
204 void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument)
205 {
206         if(Callback == NULL)
207                 Argument = Proc_GetCurThread();
208         Timer->FiresAfter = 0;
209         Timer->Callback = Callback;
210         Timer->Argument = Argument;
211 //      memset( &Timer->Lock, 0, sizeof(Timer->Lock) );
212         Timer->bActive = 0;
213         LOG("Initialised timer %p (cb=%p,arg=%p)", Timer, Callback, Argument);
214 }
215
216 /**
217  * \brief Free an allocated timer
218  */
219 void Time_FreeTimer(tTimer *Timer)
220 {
221         if( !Timer )    return ;
222         Time_RemoveTimer( Timer );      // Just in case
223
224         // Ensures that we don't free until the timer callback has started
225         while( Timer->bActive ) Threads_Yield();
226         // Release won't be needed, as nothing should be waiting on it
227 //      Mutex_Acquire( &Timer->Lock );
228         
229         // Free timer
230         free(Timer);
231         LOG("%p deallocated %p", __builtin_return_address(0), Timer);
232 }
233
234 void Time_ScheduleEvent(int Delay)
235 {
236         tTimer  *t = &Proc_GetCurThread()->ThreadTimer;
237         Time_RemoveTimer(t);
238         Time_InitTimer(t, NULL, NULL);
239         Time_ScheduleTimer(t, Delay);
240 }
241
242 /**
243  * \fn void Time_Delay(int Delay)
244  * \brief Delay for a small ammount of time
245  */
246 void Time_Delay(int Delay)
247 {
248         LOG("(%i)", Delay);
249         Threads_ClearEvent(THREAD_EVENT_TIMER);
250         Time_ScheduleEvent(Delay);
251         Threads_WaitEvents(THREAD_EVENT_TIMER);
252 }
253
254 // === EXPORTS ===
255 //EXPORT(Time_CreateTimer);
256 EXPORT(Time_ScheduleTimer);
257 EXPORT(Time_RemoveTimer);
258 EXPORT(Time_AllocateTimer);
259 //EXPORT(Time_InitTimer);
260 EXPORT(Time_FreeTimer);
261 EXPORT(Time_Delay);

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