X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=KernelLand%2FKernel%2Ftime.c;h=478c41f3d1410070e3bea1ddd922f1fd5eab70e1;hb=14d0ba44433f0f828aff710184fd3c597ab73999;hp=2c83fa32d75981194535f712183c7739a1133a23;hpb=48743e39650eb1ef988380e9d95f27fd40d3a9ce;p=tpg%2Facess2.git diff --git a/KernelLand/Kernel/time.c b/KernelLand/Kernel/time.c index 2c83fa32..478c41f3 100644 --- a/KernelLand/Kernel/time.c +++ b/KernelLand/Kernel/time.c @@ -1,99 +1,243 @@ /* - * Acess 2 + * Acess2 Kernel * - By John Hodge (thePowersGang) * * Timer Code */ +#define SANITY 1 // Enable ASSERTs +#define DEBUG 0 #include #include +#include #include #include // Proc_GetCurThread - -// === CONSTANTS === -#define NUM_TIMERS 8 - -// === TYPEDEFS === -struct sTimer { - tTimer *Next; - Sint64 FiresAfter; - void (*Callback)(void*); - void *Argument; -}; +#include +#include // 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);