X-Git-Url: https://git.ucc.asn.au/?p=tpg%2Facess2.git;a=blobdiff_plain;f=KernelLand%2FKernel%2Fthreads.c;h=469c5ec18dd3beae6afe01d71d9f4c14a1a67009;hp=280f7f286cd20cca9bce0796d0b3f60e6b23cf2e;hb=14d0ba44433f0f828aff710184fd3c597ab73999;hpb=d41554e6b486a0a50f23720869582f0b1fae3f6f diff --git a/KernelLand/Kernel/threads.c b/KernelLand/Kernel/threads.c index 280f7f28..469c5ec1 100644 --- a/KernelLand/Kernel/threads.c +++ b/KernelLand/Kernel/threads.c @@ -4,18 +4,23 @@ * threads.c * - Common Thread Control */ +#define DEBUG 0 #include #include #include #include #include #include +#include #include // VFS Handle maintainence #include +#include // Configuration +#define DEBUG_TRACE_ACTIVEQUEUE 0 // Trace adds/removals from the active queue #define DEBUG_TRACE_TICKETS 0 // Trace ticket counts #define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake) +#define DEBUG_TRACE_SCHEDULE 0 // Trace scheduling // --- Schedulers --- #define SCHED_UNDEF 0 @@ -30,6 +35,9 @@ #define DEFAULT_PRIORITY 5 #define MIN_PRIORITY 10 +#define PRIthread_fmt "%p(%i %s)" +#define PRIthread_args(t) (t), ((t)?(t)->TID:-1), ((t)?(t)->ThreadName:"-") + // === IMPORTS === extern void User_Signal_Kill(int SigNum); @@ -49,7 +57,7 @@ void Threads_Delete(tThread *Thread); char *Threads_GetName(tTID ID); #if 0 void Threads_SetPriority(tThread *Thread, int Pri); -tThread *Threads_CloneTCB(Uint *Err, Uint Flags); +tThread *Threads_CloneTCB(Uint Flags); int Threads_WaitTID(int TID, int *status); tThread *Threads_GetThread(Uint TID); #endif @@ -59,15 +67,13 @@ void Threads_int_AddToList(tThreadList *List, tThread *Thread); void Threads_Exit(int TID, int Status); void Threads_Kill(tThread *Thread, int Status); void Threads_Yield(void); -#endif -void Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock); -#if 0 + int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock); void Threads_Sleep(void); int Threads_Wake(tThread *Thread); void Threads_AddActive(tThread *Thread); tThread *Threads_RemActive(void); -#endif void Threads_ToggleTrace(int TID); +#endif void Threads_Fault(int Num); void Threads_SegFault(tVAddr Addr); void Threads_PostSignalTo(tThread *Thread, int SignalNum); @@ -87,8 +93,11 @@ tGID Threads_GetGID(void); int Threads_SetGID(Uint *Errno, tUID ID); #endif void Threads_int_DumpThread(tThread *thread); +#if 0 void Threads_Dump(void); +#endif void Threads_DumpActive(void); +tThread *Threads_int_GetRunnable(void); // === GLOBALS === // -- Core Thread -- @@ -190,7 +199,7 @@ void Threads_Delete(tThread *Thread) proc->Next->Prev = proc->Prev; // VFS Cleanup - VFS_CloseAllUserHandles(); + VFS_CloseAllUserHandles( proc ); // Architecture cleanup Proc_ClearProcess( proc ); // VFS Configuration strings @@ -321,11 +330,10 @@ void Threads_SetPriority(tThread *Thread, int Pri) */ tThread *Threads_CloneTCB(Uint Flags) { - tThread *cur, *new; - cur = Proc_GetCurThread(); + tThread *cur = Proc_GetCurThread(); // Allocate and duplicate - new = malloc(sizeof(tThread)); + tThread *new = malloc(sizeof(tThread)); if(new == NULL) { errno = -ENOMEM; return NULL; } memcpy(new, cur, sizeof(tThread)); @@ -347,7 +355,7 @@ tThread *Threads_CloneTCB(Uint Flags) if(Flags & CLONE_VM) { tProcess *newproc, *oldproc; oldproc = cur->Process; - new->Process = malloc( sizeof(struct sProcess) ); + new->Process = calloc( sizeof(struct sProcess), 1 ); newproc = new->Process; newproc->PID = new->TID; if( Flags & CLONE_PGID ) @@ -472,18 +480,26 @@ tTID Threads_WaitTID(int TID, int *Status) tTID ret = -1; if( ev & THREAD_EVENT_DEADCHILD ) { + tThread * const us = Proc_GetCurThread(); // A child died, get the TID - tThread *us = Proc_GetCurThread(); ASSERT(us->LastDeadChild); - ret = us->LastDeadChild->TID; + tThread *dead_thread = us->LastDeadChild; + us->LastDeadChild = dead_thread->Next; + if( us->LastDeadChild ) + Threads_PostEvent( us, THREAD_EVENT_DEADCHILD ); + else + Threads_ClearEvent( THREAD_EVENT_DEADCHILD ); + Mutex_Release(&us->DeadChildLock); + + ret = dead_thread->TID; // - Mark as dead (as opposed to undead) - ASSERT(us->LastDeadChild->Status == THREAD_STAT_ZOMBIE); - us->LastDeadChild->Status = THREAD_STAT_DEAD; + ASSERTC(dead_thread->Status, ==, THREAD_STAT_ZOMBIE); + dead_thread->Status = THREAD_STAT_DEAD; // - Set return status if(Status) - *Status = us->LastDeadChild->RetStatus; - us->LastDeadChild = NULL; - Mutex_Release(&us->DeadChildLock); + *Status = dead_thread->RetStatus; + + Threads_Delete( dead_thread ); } else { @@ -507,6 +523,7 @@ tTID Threads_WaitTID(int TID, int *Status) // Specific Thread if(TID > 0) { + // TODO: Register on thread to be poked when it dies tTID ret; // NOTE: Race condition - Other child dies, desired child dies, first death is 'lost' while( (ret = Threads_WaitTID(-1, Status)) != TID ) @@ -713,9 +730,11 @@ void Threads_Kill(tThread *Thread, int Status) SHORTREL( &glThreadListLock ); // TODO: It's possible that we could be timer-preempted here, should disable that... somehow Mutex_Acquire( &Thread->Parent->DeadChildLock ); // released by parent + Thread->Next = Thread->Parent->LastDeadChild; Thread->Parent->LastDeadChild = Thread; Threads_PostEvent( Thread->Parent, THREAD_EVENT_DEADCHILD ); + // Process cleanup happens on reaping Log("Thread %i went *hurk* (%i)", Thread->TID, Status); // And, reschedule @@ -741,6 +760,7 @@ void Threads_Yield(void) void Threads_int_WaitForStatusEnd(enum eThreadStatus Status) { tThread *us = Proc_GetCurThread(); + LOG("us = %p(%i %s), status=%i", us, us->TID, us->ThreadName, Status); ASSERT(Status != THREAD_STAT_ACTIVE); ASSERT(Status != THREAD_STAT_DEAD); while( us->Status == Status ) @@ -754,7 +774,7 @@ void Threads_int_WaitForStatusEnd(enum eThreadStatus Status) } } -void Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock) +int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock) { SHORTLOCK( &glThreadListLock ); tThread *us = Threads_RemActive(); @@ -768,23 +788,28 @@ void Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread ** if( ListTail ) { if(*ListTail) { (*ListTail)->Next = us; - *ListTail = us; } else { *ListHead = us; - *ListTail = us; } + *ListTail = us; } - else { + else if( ListHead ) { + us->Next = *ListHead; *ListHead = us; } + else { + // Nothing + } //if( Proc_ThreadSync(us) ) // return ; SHORTREL( &glThreadListLock ); if( Lock ) - SHORTLOCK( Lock ); + SHORTREL( Lock ); Threads_int_WaitForStatusEnd(Status); + us->WaitPointer = NULL; + return us->RetStatus; } /** @@ -943,27 +968,40 @@ void Threads_ToggleTrace(int TID) */ void Threads_AddActive(tThread *Thread) { + #if DEBUG_TRACE_ACTIVEQUEUE + Debug("Threads_AddActive("PRIthread_fmt")", PRIthread_args(Thread)); + #endif SHORTLOCK( &glThreadListLock ); - if( Thread->Status == THREAD_STAT_ACTIVE ) { + if( Thread->Status == THREAD_STAT_ACTIVE ) + { tThread *cur = Proc_GetCurThread(); - Log_Warning("Threads", "WTF, %p CPU%i %p (%i %s) is adding %p (%i %s) when it is active", + Log_KernelPanic("Threads", + "ret=%p CPU%i "PRIthread_fmt" is adding "PRIthread_fmt" when it is active", __builtin_return_address(0), - GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName); + GetCPUNum(), PRIthread_args(cur), PRIthread_args(Thread)); SHORTREL( &glThreadListLock ); return ; } // Set state Thread->Status = THREAD_STAT_ACTIVE; -// Thread->CurCPU = -1; // Add to active list + // - Thread can be the current thread if we're interrupted just before + // Proc_Reschedule in a sleep state. + if( Thread != Proc_GetCurThread() ) { #if SCHEDULER_TYPE == SCHED_RR_PRI tThreadList *list = &gaActiveThreads[Thread->Priority]; #else tThreadList *list = &gActiveThreads; #endif + #if DEBUG_TRACE_ACTIVEQUEUE + Debug(" - Head="PRIthread_fmt",Tail="PRIthread_fmt"", + PRIthread_args(list->Head), + PRIthread_args(list->Tail) + ); + #endif Threads_int_AddToList( list, Thread ); } @@ -999,8 +1037,12 @@ void Threads_AddActive(tThread *Thread) */ tThread *Threads_RemActive(void) { + tThread *us = Proc_GetCurThread(); + #if DEBUG_TRACE_ACTIVEQUEUE + Debug("Threads_RemActive(%p(%i %s))", us, us->TID, us->ThreadName); + #endif giNumActiveThreads --; - return Proc_GetCurThread(); + return us; } /** @@ -1215,17 +1257,20 @@ int *Threads_GetErrno(void) } // --- Configuration --- -int *Threads_GetMaxFD(void) +int *Threads_GetMaxFD(tProcess *Process) { - return &Proc_GetCurThread()->Process->MaxFD; + if(!Process) Process = Proc_GetCurThread()->Process; + return &Process->MaxFD; } -char **Threads_GetChroot(void) +char **Threads_GetChroot(tProcess *Process) { - return &Proc_GetCurThread()->Process->RootDir; + if(!Process) Process = Proc_GetCurThread()->Process; + return &Process->RootDir; } -char **Threads_GetCWD(void) +char **Threads_GetCWD(tProcess *Process) { - return &Proc_GetCurThread()->Process->CurrentWorkingDir; + if(!Process) Process = Proc_GetCurThread()->Process; + return &Process->CurrentWorkingDir; } // --- @@ -1251,6 +1296,11 @@ void Threads_int_DumpThread(tThread *thread) case THREAD_STAT_MUTEXSLEEP: Log(" Mutex Pointer: %p", thread->WaitPointer); break; + case THREAD_STAT_RWLOCKSLEEP: + Log(" Lock Pointer: %p", thread->WaitPointer); + Log(" Lock Name: %s", + ((tRWLock*)thread->WaitPointer)->Name); + break; case THREAD_STAT_SEMAPHORESLEEP: Log(" Semaphore Pointer: %p", thread->WaitPointer); Log(" Semaphore Name: %s:%s", @@ -1259,7 +1309,7 @@ void Threads_int_DumpThread(tThread *thread) ); break; case THREAD_STAT_EVENTSLEEP: - // TODO: Event mask + Log(" Event Mask: %x", thread->RetStatus); break; case THREAD_STAT_ZOMBIE: Log(" Return Status: %i", thread->RetStatus); @@ -1278,22 +1328,16 @@ void Threads_int_DumpThread(tThread *thread) */ void Threads_DumpActive(void) { - tThread *thread; - tThreadList *list; - #if SCHEDULER_TYPE == SCHED_RR_PRI - int i; - #endif - Log("Active Threads: (%i reported)", giNumActiveThreads); #if SCHEDULER_TYPE == SCHED_RR_PRI - for( i = 0; i < MIN_PRIORITY+1; i++ ) + for( int i = 0; i < MIN_PRIORITY+1; i++ ) { - list = &gaActiveThreads[i]; + tThreadList *list = &gaActiveThreads[i]; #else - list = &gActiveThreads; + tThreadList *list = &gActiveThreads; #endif - for(thread=list->Head;thread;thread=thread->Next) + for(tThread *thread = list->Head; thread; thread = thread->Next) { Threads_int_DumpThread(thread); if(thread->Status != THREAD_STAT_ACTIVE) @@ -1322,6 +1366,128 @@ void Threads_Dump(void) } } +tThread *Threads_int_GetRunnable(void) +{ + #if SCHEDULER_TYPE == SCHED_LOTTERY + // ----------------------------------- + // Lottery Scheduler + // ----------------------------------- + #if DEBUG_VALIDATE_TICKET_COUNTS + { + int total_tickets = 0; + for(const tThread *thread = gActiveThreads.Head; thread; thread = thread->Next) + { + if(thread->Status != THREAD_STAT_ACTIVE) + Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i", + thread, thread->TID, thread->ThreadName, thread->Status); + if(thread->Next == thread) { + Panic("Bookkeeping fail - %p %i(%s) loops back on itself", + thread, thread->TID, thread->ThreadName, thread->Status); + } + total_tickets += caiTICKET_COUNTS[ thread->Priority ]; + } + if(total_tickets != giFreeTickets) + { + Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i", + giFreeTickets, total_tickets, CPU); + } + } + # endif + + // No free tickets (all tasks delegated to cores) + if( giFreeTickets == 0 ) + { + return NULL; + } + + // Get the ticket number + int ticket = rand() % giFreeTickets; + int number = ticket; + + // Find the next thread + tThread **pnp = &gActiveThreads.Head; + tThread *thread; + for(thread = gActiveThreads.Head; thread; pnp = &thread->Next, thread = thread->Next ) + { + if( caiTICKET_COUNTS[ thread->Priority ] > number) + break; + number -= caiTICKET_COUNTS[ thread->Priority ]; + } + + // If we didn't find a thread, something went wrong + if(thread == NULL) + { + int total_tickets = 0; + for(thread=gActiveThreads;thread;thread=thread->Next) { + if(thread->CurCPU >= 0) continue; + total_tickets += caiTICKET_COUNTS[ thread->Priority ]; + } + Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)", + giFreeTickets, total_tickets); + } + + // Remove + *pnp = thread->Next; + if( !thread->Next ) + gActiveThreads.Tail = prev; + + giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ]; + # if DEBUG_TRACE_TICKETS + LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n", + CPU, thread, thread->TID, thread->ThreadName, + giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]); + # endif + return thread; + + #elif SCHEDULER_TYPE == SCHED_RR_PRI + + // ----------------------------------- + // Priority based round robin scheduler + // ----------------------------------- + for( int i = 0; i < MIN_PRIORITY + 1; i ++ ) + { + if( gaActiveThreads[i].Head == NULL ) + continue ; + + tThread *thread = gaActiveThreads[i].Head; + + if( thread->Status != THREAD_STAT_ACTIVE ) { + for( const tThread *t = gaActiveThreads[i].Head; t; t = t->Next ) + LogF("- %p(%i %s)\n", t, t->TID, t->ThreadName); + Panic("Thread %p(%i %s) from pqueue %i is active!", + thread, thread->TID, thread->ThreadName, i); + } + + // Remove from head + gaActiveThreads[i].Head = thread->Next; + if(!thread->Next) + gaActiveThreads[i].Tail = NULL; + thread->Next = NULL; + return thread; + } + return NULL; + + #elif SCHEDULER_TYPE == SCHED_RR_SIM + + // ----------------------------------- + // Single-list round-robin + // ----------------------------------- + tThread *thread = gActiveThreads.Head; + LOG("thread = %p", thread); + if( thread ) + { + gActiveThreads.Head = thread->Next; + if(!thread->Next) + gaActiveThreads.Tail = NULL; + thread->Next = NULL; + } + return thread; + #else + # error "Unimplemented scheduling algorithm" + return NULL; + #endif +} + /** * \brief Gets the next thread to run * \param CPU Current CPU @@ -1329,50 +1495,29 @@ void Threads_Dump(void) */ tThread *Threads_GetNextToRun(int CPU, tThread *Last) { - tThread *thread; - - // If this CPU has the lock, we must let it complete - if( CPU_HAS_LOCK( &glThreadListLock ) ) - return Last; + ASSERT( CPU_HAS_LOCK(&glThreadListLock) ); // Don't change threads if the current CPU has switches disabled - if( gaThreads_NoTaskSwitch[CPU] ) + if( gaThreads_NoTaskSwitch[CPU] ) { + LOG("- Denied"); return Last; - - // Lock thread list - SHORTLOCK( &glThreadListLock ); + } // Make sure the current (well, old) thread is marked as de-scheduled if(Last) Last->CurCPU = -1; // No active threads, just take a nap if(giNumActiveThreads == 0) { - SHORTREL( &glThreadListLock ); + LOG("- No active"); #if DEBUG_TRACE_TICKETS Log("No active threads"); #endif return NULL; } - #if 0 - #if SCHEDULER_TYPE != SCHED_RR_PRI - // Special case: 1 thread - if(giNumActiveThreads == 1) { - if( gActiveThreads.Head->CurCPU == -1 ) - gActiveThreads.Head->CurCPU = CPU; - - SHORTREL( &glThreadListLock ); - - if( gActiveThreads.Head->CurCPU == CPU ) - return gActiveThreads.Head; - - return NULL; // CPU has nothing to do - } - #endif - #endif - // Allow the old thread to be scheduled again - if( Last ) { + if( Last ) + { if( Last->Status == THREAD_STAT_ACTIVE ) { tThreadList *list; #if SCHEDULER_TYPE == SCHED_LOTTERY @@ -1391,6 +1536,10 @@ tThread *Threads_GetNextToRun(int CPU, tThread *Last) #endif // Add to end of list Threads_int_AddToList( list, Last ); + #if DEBUG_TRACE_ACTIVEQUEUE > 1 + Debug("Threads_GetNextToRun: Append thread %p(%i %s)", + Last, Last->TID, Last->ThreadName); + #endif } #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS else @@ -1399,131 +1548,34 @@ tThread *Threads_GetNextToRun(int CPU, tThread *Last) #endif Last->CurCPU = -1; } - - // --- - // Lottery Scheduler - // --- - #if SCHEDULER_TYPE == SCHED_LOTTERY - { - int ticket, number; - # if 1 - number = 0; - for(thread = gActiveThreads.Head; thread; thread = thread->Next) - { - if(thread->Status != THREAD_STAT_ACTIVE) - Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i", - thread, thread->TID, thread->ThreadName, thread->Status); - if(thread->Next == thread) { - Panic("Bookkeeping fail - %p %i(%s) loops back on itself", - thread, thread->TID, thread->ThreadName, thread->Status); - } - number += caiTICKET_COUNTS[ thread->Priority ]; - } - if(number != giFreeTickets) { - Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i", - giFreeTickets, number, CPU); - } - # endif - - // No free tickets (all tasks delegated to cores) - if( giFreeTickets == 0 ) { - SHORTREL(&glThreadListLock); - return NULL; - } - - // Get the ticket number - ticket = number = rand() % giFreeTickets; - - // Find the next thread - for(thread = gActiveThreads.Head; thread; prev = thread, thread = thread->Next ) - { - if( caiTICKET_COUNTS[ thread->Priority ] > number) break; - number -= caiTICKET_COUNTS[ thread->Priority ]; - } - - // If we didn't find a thread, something went wrong - if(thread == NULL) - { - number = 0; - for(thread=gActiveThreads;thread;thread=thread->Next) { - if(thread->CurCPU >= 0) continue; - number += caiTICKET_COUNTS[ thread->Priority ]; - } - Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)", - giFreeTickets, number); - } - // Remove - if(prev) - prev->Next = thread->Next; - else - gActiveThreads.Head = thread->Next; - if(!thread->Next) - gActiveThreads.Tail = prev; - - giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ]; - # if DEBUG_TRACE_TICKETS - LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n", - CPU, thread, thread->TID, thread->ThreadName, - giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]); - # endif - } + // Call actual scheduler + tThread *thread = Threads_int_GetRunnable(); - // --- - // Priority based round robin scheduler - // --- - #elif SCHEDULER_TYPE == SCHED_RR_PRI + // Anything to do? + if( thread ) { - int i; - thread = NULL; - for( i = 0; i < MIN_PRIORITY + 1; i ++ ) + if( thread->Status != THREAD_STAT_ACTIVE ) { - if( !gaActiveThreads[i].Head ) - continue ; - - thread = gaActiveThreads[i].Head; - - // Remove from head - gaActiveThreads[i].Head = thread->Next; - if(!thread->Next) - gaActiveThreads[i].Tail = NULL; - thread->Next = NULL; - break; + LogF("Thread %p(%i %s) scheduled while not active\n", + thread, thread->TID, thread->ThreadName); } + + // Make the new thread non-schedulable + thread->CurCPU = CPU; + thread->Remaining = thread->Quantum; - // Anything to do? - if( !thread ) { - SHORTREL(&glThreadListLock); - return NULL; - } - if( thread->Status != THREAD_STAT_ACTIVE ) { - LogF("Oops, Thread %i (%s) is not active\n", thread->TID, thread->ThreadName); - } + #if DEBUG_TRACE_SCHEDULE + Debug("Scheduled "PRIthread_fmt", next = %p", + PRIthread_args(thread), + thread->Next); + #endif } - #elif SCHEDULER_TYPE == SCHED_RR_SIM + else { - // Get the next thread off the list - thread = gActiveThreads.Head; - gActiveThreads.Head = thread->Next; - if(!thread->Next) - gaActiveThreads.Tail = NULL; - thread->Next = NULL; - - // Anything to do? - if( !thread ) { - SHORTREL(&glThreadListLock); - return NULL; - } + // No thread possible, warning condition (idle thread should be runnable) + Warning("No runnable thread for CPU%i", CPU); } - #else - # error "Unimplemented scheduling algorithm" - #endif - - // Make the new thread non-schedulable - thread->CurCPU = CPU; - thread->Remaining = thread->Quantum; - - SHORTREL( &glThreadListLock ); return thread; }