Kernel - Fixed freeze (I think)
[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 <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         Workqueue_Init(&gTimers_CallbackQueue, "Timer Callbacks", offsetof(tTimer, Next));
53
54         for(;;)
55         {
56                 tTimer *timer = Workqueue_GetWork(&gTimers_CallbackQueue);
57         
58                 if( !timer->Callback ) {
59                         LOG("Timer %p doesn't have a callback", timer);
60                         ASSERT( timer->Callback );
61                 }
62
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;
67                 
68                 LOG("Callback fire %p", timer);
69         
70                 // Allow Time_RemoveTimer to be called safely
71                 timer->bActive = 0;
72                 
73                 // Fire callback
74                 cb(arg);
75
76                 // Mark timer as no longer needed
77         }
78 }
79
80 /**
81  * \fn void Timer_CallTimers()
82  */
83 void Timer_CallTimers()
84 {
85         SHORTLOCK(&gTimers_ListLock);
86         while( gTimers && gTimers->FiresAfter < now() )
87         {
88                 // Get timer from list
89                 tTimer  *timer = gTimers;
90         
91                 ASSERT( gTimers != gTimers->Next );     
92                 gTimers = gTimers->Next;
93         
94                 // Perform event
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);
100                 }
101                 else {
102                         LOG("Event fire %p", timer);
103                         ASSERT( timer->Argument );
104                         Threads_PostEvent(timer->Argument, THREAD_EVENT_TIMER);
105                         timer->bActive = 0;
106                 }
107         }
108         SHORTREL(&gTimers_ListLock);
109 }
110
111 /**
112  * \brief Schedule an action (Legacy)
113  */
114 tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
115 {
116         tTimer  *ret = malloc(sizeof(tTimer));
117         if( !ret )      return NULL;
118         Time_InitTimer(ret, Callback, Argument);
119         Time_ScheduleTimer(ret, Delta);
120         return ret;
121 }
122
123 /**
124  * \brief Schedule a timer to fire
125  */
126 void Time_ScheduleTimer(tTimer *Timer, int Delta)
127 {
128         tTimer  *t, *p;
129
130         // Sanity checks
131         if( !Timer )    return ;
132
133         if( Timer->bActive )    return;
134         
135         // Set time
136         Timer->FiresAfter = now() + Delta;
137         
138         // Debug
139         LOG("%p added timer %p - %i ms (ts=%lli)",
140                 __builtin_return_address(0), Timer, Delta, Timer->FiresAfter);
141
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 )
146         {
147                 ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
148                 if( t == Timer )
149                 {
150                         LOG("Double schedule - increasing delta");
151                         SHORTREL(&gTimers_ListLock);
152                         return ;
153                 }
154                 LOG(" t = %p ts:%lli", t, t->FiresAfter);
155                 if( t->FiresAfter > Timer->FiresAfter ) break;
156         }
157         Timer->Next = t;
158         p->Next = Timer;
159         Timer->bActive = 1;
160         LOG(" %p %p %p", p, Timer, t);
161         SHORTREL(&gTimers_ListLock);
162 }
163
164 /**
165  * \brief Delete a timer from the running list
166  */
167 void Time_RemoveTimer(tTimer *Timer)
168 {
169         tTimer  *t, *p;
170
171         if( !Timer )    return ;
172         
173         SHORTLOCK(&gTimers_ListLock);
174         for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
175         {
176                 ASSERT( p != t ); ASSERT( CheckMem(t, sizeof(tTimer)) );
177                 if( t == Timer )
178                 {
179                         p->Next = t->Next;
180                         break ;
181                 }
182         }
183         SHORTREL(&gTimers_ListLock);
184
185         if( t ) {
186                 Timer->bActive = 0;
187                 LOG("%p removed %p", __builtin_return_address(0), Timer);
188         }
189         else
190                 LOG("%p tried to remove %p (already complete)", __builtin_return_address(0), Timer);
191 }
192
193 /**
194  * \brief Allocate a timer object, but don't schedule it
195  */
196 tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
197 {
198         tTimer  *ret = malloc(sizeof(tTimer));
199         if( !ret )      return NULL;
200         Time_InitTimer(ret, Callback, Argument);
201         return ret;
202 }
203
204 /**
205  * \brief Initialise a timer
206  * \note Mostly an internal function
207  */
208 void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument)
209 {
210         if(Callback == NULL)
211                 Argument = Proc_GetCurThread();
212         Timer->FiresAfter = 0;
213         Timer->Callback = Callback;
214         Timer->Argument = Argument;
215 //      memset( &Timer->Lock, 0, sizeof(Timer->Lock) );
216         Timer->bActive = 0;
217 }
218
219 /**
220  * \brief Free an allocated timer
221  */
222 void Time_FreeTimer(tTimer *Timer)
223 {
224         if( !Timer )    return ;
225         Time_RemoveTimer( Timer );      // Just in case
226
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 );
231         
232         // Free timer
233         free(Timer);
234         LOG("%p deallocated %p", __builtin_return_address(0), Timer);
235 }
236
237 /**
238  * \fn void Time_Delay(int Delay)
239  * \brief Delay for a small ammount of time
240  */
241 void Time_Delay(int Delay)
242 {
243         tTimer  *t;
244         t = Time_AllocateTimer(NULL, NULL);
245         Time_ScheduleTimer(t, Delay);
246         Threads_WaitEvents(THREAD_EVENT_TIMER);
247         Time_FreeTimer(t);
248 }
249
250 // === EXPORTS ===
251 //EXPORT(Time_CreateTimer);
252 EXPORT(Time_ScheduleTimer);
253 EXPORT(Time_RemoveTimer);
254 EXPORT(Time_AllocateTimer);
255 //EXPORT(Time_InitTimer);
256 EXPORT(Time_FreeTimer);
257 EXPORT(Time_Delay);

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