Kernel/x86 - Clean up some of the task switching code (possibly a little broken)
[tpg/acess2.git] / KernelLand / Kernel / time.c
index 2c83fa3..478c41f 100644 (file)
 /*
- * Acess 2
+ * Acess2 Kernel
  * - By John Hodge (thePowersGang) 
  *
  * Timer Code
  */
+#define SANITY 1       // Enable ASSERTs
+#define DEBUG  0
 #include <acess.h>
 #include <timers.h>
+#include <timers_int.h>
 #include <events.h>
 #include <hal_proc.h>  // Proc_GetCurThread
-
-// === CONSTANTS ===
-#define        NUM_TIMERS      8
-
-// === TYPEDEFS ===
-struct sTimer {
-       tTimer  *Next;
-       Sint64  FiresAfter;
-       void    (*Callback)(void*);
-       void    *Argument;
-};
+#include <workqueue.h>
+#include <threads_int.h>       // Used to get thread timer
 
 // === PROTOTYPES ===
+void   Timer_CallbackThread(void *Unused);
 void   Timer_CallTimers(void);
+#if 0
+tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument);
+void   Time_ScheduleTimer(tTimer *Timer, int Delta);
+void   Time_RemoveTimer(tTimer *Timer);
+tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument);
+#endif
+void   Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument);
+#if 0
+void   Time_FreeTimer(tTimer *Timer);
+void   Time_Delay(int Time);
+#endif
 
 // === GLOBALS ===
 volatile Uint64        giTicks = 0;
 volatile Sint64        giTimestamp = 0;
 volatile Uint64        giPartMiliseconds = 0;
-tTimer *gTimers;       // TODO: Replace by a ring-list timer
+tTimer *gTimers;
+tWorkqueue     gTimers_CallbackQueue;
+tShortSpinlock gTimers_ListLock;
 
 // === CODE ===
+void Timer_CallbackThread(void *Unused)
+{
+       Threads_SetName("Timer Callback Thread");
+       Workqueue_Init(&gTimers_CallbackQueue, "Timer Callbacks", offsetof(tTimer, Next));
+
+       for(;;)
+       {
+               tTimer *timer = Workqueue_GetWork(&gTimers_CallbackQueue);
+       
+               if( !timer->Callback ) {
+                       LOG("Timer %p doesn't have a callback", timer);
+                       ASSERT( timer->Callback );
+               }
+
+               // Save callback and argument (because once the mutex is released
+               // the timer may no longer be valid)
+               tTimerCallback  *cb = timer->Callback;
+               void    *arg = timer->Argument;
+               
+               LOG("Callback fire %p", timer);
+       
+               // Allow Time_RemoveTimer to be called safely
+               timer->bActive = 0;
+               
+               // Fire callback
+               cb(arg);
+
+               // Mark timer as no longer needed
+       }
+}
+
 /**
  * \fn void Timer_CallTimers()
  */
 void Timer_CallTimers()
 {
+       // Tick the random number generator every time timers are checked
+       rand();
+
+       SHORTLOCK(&gTimers_ListLock);
+       LOG("gTimers = %p (%lli ms)", gTimers, (gTimers ? gTimers->FiresAfter : 0));
        while( gTimers && gTimers->FiresAfter < now() )
        {
-               tTimer  *next;
+               ASSERT( gTimers != gTimers->Next );     
+               // Get timer from list
+               tTimer  *timer = gTimers;
+               gTimers = gTimers->Next;
        
-               if( gTimers->Callback )
-                       gTimers->Callback(gTimers->Argument);
-               else
-                       Threads_PostEvent(gTimers->Argument, THREAD_EVENT_TIMER);
-               
-               next = gTimers->Next;
-               free(gTimers);
-               gTimers = next;
+               // Perform event
+               if( timer->Callback ) {
+                       LOG("Callback schedule %p", timer);
+                       Workqueue_AddWork(&gTimers_CallbackQueue, timer);
+               }
+               else {
+                       LOG("Event fire %p", timer);
+                       ASSERT( timer->Argument );
+                       Threads_PostEvent(timer->Argument, THREAD_EVENT_TIMER);
+                       timer->bActive = 0;
+               }
        }
+       SHORTREL(&gTimers_ListLock);
 }
 
 /**
- * \brief Schedule an action
+ * \brief Schedule an action (Legacy)
  */
 tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
 {
-       tTimer  *ret;
-       tTimer  *t, *p;
-       
-       if(Callback == NULL)
-               Argument = Proc_GetCurThread();
+       tTimer  *ret = malloc(sizeof(tTimer));
+       if( !ret )      return NULL;
+       Time_InitTimer(ret, Callback, Argument);
+       Time_ScheduleTimer(ret, Delta);
+       return ret;
+}
 
-       // TODO: Use a pool instead?
-       ret = malloc(sizeof(tTimer));
+/**
+ * \brief Schedule a timer to fire
+ */
+void Time_ScheduleTimer(tTimer *Timer, int Delta)
+{
+       tTimer  *t;
+       tTimer  **prev_next;
+
+       // Sanity checks
+       if( !Timer )    return ;
+
+       if( Timer->bActive )    return;
+       
+       // Set time
+       Timer->FiresAfter = now() + Delta;
        
-       ret->Callback = Callback;
-       ret->FiresAfter = now() + Delta;
-       ret->Argument = Argument;
+       // Debug
+       LOG("%p added timer %p - %i ms (ts=%lli)",
+               __builtin_return_address(0), Timer, Delta, Timer->FiresAfter);
 
        // Add into list (sorted)
-       for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
+       SHORTLOCK(&gTimers_ListLock);
+//     Mutex_Release( &Timer->Lock );  // Prevent deadlocks
+       for( prev_next = &gTimers, t = gTimers; t; prev_next = &t->Next, t = t->Next )
        {
-               if( t->FiresAfter > ret->FiresAfter )   break;
+               ASSERT( prev_next != &t->Next );
+               ASSERT( CheckMem(t, sizeof(tTimer)) );
+               if( t == Timer )
+               {
+                       LOG("Double schedule - increasing delta");
+                       SHORTREL(&gTimers_ListLock);
+                       return ;
+               }
+               LOG(" t = %p ts:%lli", t, t->FiresAfter);
+               if( t->FiresAfter > Timer->FiresAfter ) break;
        }
-       ret->Next = t;
-       p->Next = ret;
-
-       return ret;
+       Timer->Next = t;
+       *prev_next = Timer;
+       Timer->bActive = 1;
+       LOG(" prev_next=%p Timer=%p next=%p", prev_next, Timer, t);
+       SHORTREL(&gTimers_ListLock);
 }
 
 /**
- * \brief Delete a timer
+ * \brief Delete a timer from the running list
  */
 void Time_RemoveTimer(tTimer *Timer)
 {
-       tTimer  *t, *p;
-       for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
+       tTimer  *t;
+       tTimer  **prev_next;
+
+       if( !Timer )    return ;
+       
+       SHORTLOCK(&gTimers_ListLock);
+       for( prev_next = &gTimers, t = gTimers; t; prev_next = &t->Next, t = t->Next )
        {
+               ASSERT( prev_next != &t->Next );
+               ASSERT( CheckMem(t, sizeof(tTimer)) );
                if( t == Timer )
                {
-                       p->Next = t->Next;
-                       free(Timer);
-                       return ;
+                       *prev_next = t->Next;
+                       break ;
                }
        }
+       SHORTREL(&gTimers_ListLock);
+
+       if( t ) {
+               Timer->bActive = 0;
+               LOG("%p removed %p", __builtin_return_address(0), Timer);
+       }
+       else
+               LOG("%p tried to remove %p (already complete)", __builtin_return_address(0), Timer);
+}
+
+/**
+ * \brief Allocate a timer object, but don't schedule it
+ */
+tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
+{
+       tTimer  *ret = malloc(sizeof(tTimer));
+       if( !ret )      return NULL;
+       Time_InitTimer(ret, Callback, Argument);
+       return ret;
+}
+
+/**
+ * \brief Initialise a timer
+ * \note Mostly an internal function
+ */
+void Time_InitTimer(tTimer *Timer, tTimerCallback *Callback, void *Argument)
+{
+       if(Callback == NULL)
+               Argument = Proc_GetCurThread();
+       Timer->FiresAfter = 0;
+       Timer->Callback = Callback;
+       Timer->Argument = Argument;
+//     memset( &Timer->Lock, 0, sizeof(Timer->Lock) );
+       Timer->bActive = 0;
+       LOG("Initialised timer %p (cb=%p,arg=%p)", Timer, Callback, Argument);
+}
+
+/**
+ * \brief Free an allocated timer
+ */
+void Time_FreeTimer(tTimer *Timer)
+{
+       if( !Timer )    return ;
+       Time_RemoveTimer( Timer );      // Just in case
+
+       // Ensures that we don't free until the timer callback has started
+       while( Timer->bActive ) Threads_Yield();
+       // Release won't be needed, as nothing should be waiting on it
+//     Mutex_Acquire( &Timer->Lock );
+       
+       // Free timer
+       free(Timer);
+       LOG("%p deallocated %p", __builtin_return_address(0), Timer);
+}
+
+void Time_ScheduleEvent(int Delay)
+{
+       tTimer  *t = &Proc_GetCurThread()->ThreadTimer;
+       Time_RemoveTimer(t);
+       Time_InitTimer(t, NULL, NULL);
+       Time_ScheduleTimer(t, Delay);
 }
 
 /**
@@ -102,13 +246,17 @@ void Time_RemoveTimer(tTimer *Timer)
  */
 void Time_Delay(int Delay)
 {
-//     tTime   dest = now() + Delay;
-//     while(dest > now())     Threads_Yield();
-       Time_CreateTimer(Delay, NULL, NULL);
+       LOG("(%i)", Delay);
+       Threads_ClearEvent(THREAD_EVENT_TIMER);
+       Time_ScheduleEvent(Delay);
        Threads_WaitEvents(THREAD_EVENT_TIMER);
 }
 
 // === EXPORTS ===
-EXPORT(Time_CreateTimer);
+//EXPORT(Time_CreateTimer);
+EXPORT(Time_ScheduleTimer);
 EXPORT(Time_RemoveTimer);
+EXPORT(Time_AllocateTimer);
+//EXPORT(Time_InitTimer);
+EXPORT(Time_FreeTimer);
 EXPORT(Time_Delay);

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