Usermode/ld-acess - Clean up loadlib.c, improved debug
[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         LOG("gTimers = %p (%lli ms)", gTimers, (gTimers ? gTimers->FiresAfter : 0));
82         while( gTimers && gTimers->FiresAfter < now() )
83         {
84                 ASSERT( gTimers != gTimers->Next );     
85                 // Get timer from list
86                 tTimer  *timer = gTimers;
87                 gTimers = gTimers->Next;
88         
89                 // Perform event
90                 if( timer->Callback ) {
91                         LOG("Callback schedule %p", timer);
92                         Workqueue_AddWork(&gTimers_CallbackQueue, timer);
93                 }
94                 else {
95                         LOG("Event fire %p", timer);
96                         ASSERT( timer->Argument );
97                         Threads_PostEvent(timer->Argument, THREAD_EVENT_TIMER);
98                         timer->bActive = 0;
99                 }
100         }
101         SHORTREL(&gTimers_ListLock);
102 }
103
104 /**
105  * \brief Schedule an action (Legacy)
106  */
107 tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
108 {
109         tTimer  *ret = malloc(sizeof(tTimer));
110         if( !ret )      return NULL;
111         Time_InitTimer(ret, Callback, Argument);
112         Time_ScheduleTimer(ret, Delta);
113         return ret;
114 }
115
116 /**
117  * \brief Schedule a timer to fire
118  */
119 void Time_ScheduleTimer(tTimer *Timer, int Delta)
120 {
121         tTimer  *t;
122         tTimer  **prev_next;
123
124         // Sanity checks
125         if( !Timer )    return ;
126
127         if( Timer->bActive )    return;
128         
129         // Set time
130         Timer->FiresAfter = now() + Delta;
131         
132         // Debug
133         LOG("%p added timer %p - %i ms (ts=%lli)",
134                 __builtin_return_address(0), Timer, Delta, Timer->FiresAfter);
135
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 )
140         {
141                 ASSERT( prev_next != &t->Next );
142                 ASSERT( CheckMem(t, sizeof(tTimer)) );
143                 if( t == Timer )
144                 {
145                         LOG("Double schedule - increasing delta");
146                         SHORTREL(&gTimers_ListLock);
147                         return ;
148                 }
149                 LOG(" t = %p ts:%lli", t, t->FiresAfter);
150                 if( t->FiresAfter > Timer->FiresAfter ) break;
151         }
152         Timer->Next = t;
153         *prev_next = Timer;
154         Timer->bActive = 1;
155         LOG(" prev_next=%p Timer=%p next=%p", prev_next, Timer, t);
156         SHORTREL(&gTimers_ListLock);
157 }
158
159 /**
160  * \brief Delete a timer from the running list
161  */
162 void Time_RemoveTimer(tTimer *Timer)
163 {
164         tTimer  *t;
165         tTimer  **prev_next;
166
167         if( !Timer )    return ;
168         
169         SHORTLOCK(&gTimers_ListLock);
170         for( prev_next = &gTimers, t = gTimers; t; prev_next = &t->Next, t = t->Next )
171         {
172                 ASSERT( prev_next != &t->Next );
173                 ASSERT( CheckMem(t, sizeof(tTimer)) );
174                 if( t == Timer )
175                 {
176                         *prev_next = t->Next;
177                         break ;
178                 }
179         }
180         SHORTREL(&gTimers_ListLock);
181
182         if( t ) {
183                 Timer->bActive = 0;
184                 LOG("%p removed %p", __builtin_return_address(0), Timer);
185         }
186         else
187                 LOG("%p tried to remove %p (already complete)", __builtin_return_address(0), Timer);
188 }
189
190 /**
191  * \brief Allocate a timer object, but don't schedule it
192  */
193 tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
194 {
195         tTimer  *ret = malloc(sizeof(tTimer));
196         if( !ret )      return NULL;
197         Time_InitTimer(ret, Callback, Argument);
198         return ret;
199 }
200
201 /**
202  * \brief Initialise a timer
203  * \note Mostly an internal function
204  */
205 void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument)
206 {
207         if(Callback == NULL)
208                 Argument = Proc_GetCurThread();
209         Timer->FiresAfter = 0;
210         Timer->Callback = Callback;
211         Timer->Argument = Argument;
212 //      memset( &Timer->Lock, 0, sizeof(Timer->Lock) );
213         Timer->bActive = 0;
214         LOG("Initialised timer %p (cb=%p,arg=%p)", Timer, Callback, Argument);
215 }
216
217 /**
218  * \brief Free an allocated timer
219  */
220 void Time_FreeTimer(tTimer *Timer)
221 {
222         if( !Timer )    return ;
223         Time_RemoveTimer( Timer );      // Just in case
224
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 );
229         
230         // Free timer
231         free(Timer);
232         LOG("%p deallocated %p", __builtin_return_address(0), Timer);
233 }
234
235 void Time_ScheduleEvent(int Delay)
236 {
237         tTimer  *t = &Proc_GetCurThread()->ThreadTimer;
238         Time_RemoveTimer(t);
239         Time_InitTimer(t, NULL, NULL);
240         Time_ScheduleTimer(t, Delay);
241 }
242
243 /**
244  * \fn void Time_Delay(int Delay)
245  * \brief Delay for a small ammount of time
246  */
247 void Time_Delay(int Delay)
248 {
249         LOG("(%i)", Delay);
250         Threads_ClearEvent(THREAD_EVENT_TIMER);
251         Time_ScheduleEvent(Delay);
252         Threads_WaitEvents(THREAD_EVENT_TIMER);
253 }
254
255 // === EXPORTS ===
256 //EXPORT(Time_CreateTimer);
257 EXPORT(Time_ScheduleTimer);
258 EXPORT(Time_RemoveTimer);
259 EXPORT(Time_AllocateTimer);
260 //EXPORT(Time_InitTimer);
261 EXPORT(Time_FreeTimer);
262 EXPORT(Time_Delay);

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