Merge branch 'master' of git://localhost/acess2
[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         SHORTLOCK(&gTimers_ListLock);
78         while( gTimers && gTimers->FiresAfter < now() )
79         {
80                 // Get timer from list
81                 tTimer  *timer = gTimers;
82         
83                 ASSERT( gTimers != gTimers->Next );     
84                 gTimers = gTimers->Next;
85         
86                 // Perform event
87                 if( timer->Callback ) {
88                         LOG("Callback schedule %p", timer);
89                         // PROBLEM! Possibly causes rescheudle during interrupt
90 //                      Mutex_Acquire( &timer->Lock );  // Released once the callback fires
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, *p;
121
122         // Sanity checks
123         if( !Timer )    return ;
124
125         if( Timer->bActive )    return;
126         
127         // Set time
128         Timer->FiresAfter = now() + Delta;
129         
130         // Debug
131         LOG("%p added timer %p - %i ms (ts=%lli)",
132                 __builtin_return_address(0), Timer, Delta, Timer->FiresAfter);
133
134         // Add into list (sorted)
135         SHORTLOCK(&gTimers_ListLock);
136 //      Mutex_Release( &Timer->Lock );  // Prevent deadlocks
137         for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
138         {
139                 ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
140                 if( t == Timer )
141                 {
142                         LOG("Double schedule - increasing delta");
143                         SHORTREL(&gTimers_ListLock);
144                         return ;
145                 }
146                 LOG(" t = %p ts:%lli", t, t->FiresAfter);
147                 if( t->FiresAfter > Timer->FiresAfter ) break;
148         }
149         Timer->Next = t;
150         p->Next = Timer;
151         Timer->bActive = 1;
152         LOG(" %p %p %p", p, Timer, t);
153         SHORTREL(&gTimers_ListLock);
154 }
155
156 /**
157  * \brief Delete a timer from the running list
158  */
159 void Time_RemoveTimer(tTimer *Timer)
160 {
161         tTimer  *t, *p;
162
163         if( !Timer )    return ;
164         
165         SHORTLOCK(&gTimers_ListLock);
166         for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
167         {
168                 ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
169                 if( t == Timer )
170                 {
171                         p->Next = t->Next;
172                         break ;
173                 }
174         }
175         SHORTREL(&gTimers_ListLock);
176
177         if( t ) {
178                 Timer->bActive = 0;
179                 LOG("%p removed %p", __builtin_return_address(0), Timer);
180         }
181         else
182                 LOG("%p tried to remove %p (already complete)", __builtin_return_address(0), Timer);
183 }
184
185 /**
186  * \brief Allocate a timer object, but don't schedule it
187  */
188 tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
189 {
190         tTimer  *ret = malloc(sizeof(tTimer));
191         if( !ret )      return NULL;
192         Time_InitTimer(ret, Callback, Argument);
193         return ret;
194 }
195
196 /**
197  * \brief Initialise a timer
198  * \note Mostly an internal function
199  */
200 void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument)
201 {
202         if(Callback == NULL)
203                 Argument = Proc_GetCurThread();
204         Timer->FiresAfter = 0;
205         Timer->Callback = Callback;
206         Timer->Argument = Argument;
207 //      memset( &Timer->Lock, 0, sizeof(Timer->Lock) );
208         Timer->bActive = 0;
209 }
210
211 /**
212  * \brief Free an allocated timer
213  */
214 void Time_FreeTimer(tTimer *Timer)
215 {
216         if( !Timer )    return ;
217         Time_RemoveTimer( Timer );      // Just in case
218
219         // Ensures that we don't free until the timer callback has started
220         while( Timer->bActive ) Threads_Yield();
221         // Release won't be needed, as nothing should be waiting on it
222 //      Mutex_Acquire( &Timer->Lock );
223         
224         // Free timer
225         free(Timer);
226         LOG("%p deallocated %p", __builtin_return_address(0), Timer);
227 }
228
229 /**
230  * \fn void Time_Delay(int Delay)
231  * \brief Delay for a small ammount of time
232  */
233 void Time_Delay(int Delay)
234 {
235         tTimer  *t = &Proc_GetCurThread()->ThreadTimer;
236         Time_InitTimer(t, NULL, NULL);
237         Time_ScheduleTimer(t, Delay);
238         Threads_WaitEvents(THREAD_EVENT_TIMER);
239 }
240
241 // === EXPORTS ===
242 //EXPORT(Time_CreateTimer);
243 EXPORT(Time_ScheduleTimer);
244 EXPORT(Time_RemoveTimer);
245 EXPORT(Time_AllocateTimer);
246 //EXPORT(Time_InitTimer);
247 EXPORT(Time_FreeTimer);
248 EXPORT(Time_Delay);

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