Kernel/x86 - Clean up some of the task switching code (possibly a little broken)
[tpg/acess2.git] / KernelLand / Kernel / threads.c
index 956eede..469c5ec 100644 (file)
@@ -1,19 +1,26 @@
 /*
- * Acess2
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
  * threads.c
  * - Common Thread Control
  */
+#define DEBUG  0
 #include <acess.h>
 #include <threads.h>
 #include <threads_int.h>
 #include <errno.h>
 #include <hal_proc.h>
 #include <semaphore.h>
+#include <rwlock.h>
 #include <vfs_threads.h>       // VFS Handle maintainence
+#include <events.h>
+#include <debug_hooks.h>
 
 // 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
 #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);
 
 // === TYPE ===
 typedef struct
@@ -46,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
@@ -56,14 +67,23 @@ 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);
+ 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);
+#if 0
+void   Threads_PostSignal(int SignalNum);
+void   Threads_SignalGroup(tPGID PGID, int SignalNum);
+#endif
+ int   Threads_GetPendingSignal(void);
+void   Threads_SetSignalHandler(int SignalNum, void *Handler);
+void   *Threads_GetSignalHandler(int SignalNum);
 #if 0
  int   Threads_GetPID(void);
  int   Threads_GetTID(void);
@@ -72,12 +92,16 @@ tGID        Threads_GetGID(void);
  int   Threads_SetUID(Uint *Errno, tUID ID);
  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 --
-struct sProcess        gProcessZero = {
+tProcess       gProcessZero = {
        };
 // Only used for the core kernel
 tThread        gThreadZero = {
@@ -88,6 +112,7 @@ tThread      gThreadZero = {
        .Priority       = DEFAULT_PRIORITY      // Number of tickets
        };
 // -- Processes --
+tProcess       *gAllProcesses = &gProcessZero;
 // --- Locks ---
 tShortSpinlock glThreadListLock;       ///\note NEVER use a heap function while locked
 // --- Current State ---
@@ -125,14 +150,6 @@ void Threads_Init(void)
        Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack));
        
        // Create Initial Task
-//     #if SCHEDULER_TYPE == SCHED_RR_PRI
-//     gaActiveThreads[gThreadZero.Priority].Head = &gThreadZero;
-//     gaActiveThreads[gThreadZero.Priority].Tail = &gThreadZero;
-//     #else
-//     gActiveThreads.Head = &gThreadZero;
-//     gActiveThreads.Tail = &gThreadZero;
-//     #endif
-       
        gAllThreads = &gThreadZero;
        giNumActiveThreads = 1;
        gThreadZero.Process = &gProcessZero;
@@ -149,11 +166,40 @@ void Threads_Delete(tThread *Thread)
        Proc_ClearThread(Thread);                       
 
        Thread->Process->nThreads --;
+
+       if( Thread->Process->FirstThread == Thread )
+       {
+               Thread->Process->FirstThread = Thread->ProcessNext;
+       }
+       else
+       {
+               tThread *prev = Thread->Process->FirstThread;
+               while(prev && prev->ProcessNext != Thread)
+                       prev = prev->ProcessNext;
+               if( !prev )
+                       Log_Error("Threads", "Thread %p(%i %s) is not on the process's list",
+                               Thread, Thread->TID, Thread->ThreadName
+                               );
+               else
+                       prev->ProcessNext = Thread->ProcessNext;
+       }
+
+       // If the final thread is being terminated, clean up the process
        if( Thread->Process->nThreads == 0 )
        {
                tProcess        *proc = Thread->Process;
+
+               // Remove from global process list
+               // TODO: RWLock
+               if(proc->Prev)
+                       proc->Prev->Next = proc->Next;
+               else
+                       gAllProcesses = proc->Next;
+               if(proc->Next)
+                       proc->Next->Prev = proc->Prev;
+
                // VFS Cleanup
-               VFS_CloseAllUserHandles();
+               VFS_CloseAllUserHandles( proc );
                // Architecture cleanup
                Proc_ClearProcess( proc );
                // VFS Configuration strings
@@ -284,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));
        
@@ -310,26 +355,37 @@ 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 )
+                       newproc->PGID = oldproc->PGID;
+               else
+                       newproc->PGID = newproc->PID;
                newproc->UID = oldproc->UID;
                newproc->GID = oldproc->GID;
                newproc->MaxFD = oldproc->MaxFD;
-               if( oldproc->CurrentWorkingDir )
-                       newproc->CurrentWorkingDir = strdup( oldproc->CurrentWorkingDir );
-               else
-                       newproc->CurrentWorkingDir = NULL;
-               if( oldproc->RootDir )
-                       newproc->RootDir = strdup( oldproc->RootDir );
-               else
-                       newproc->RootDir = NULL;
+               newproc->CurrentWorkingDir = oldproc->CurrentWorkingDir ? strdup( oldproc->CurrentWorkingDir ) : NULL;
+               newproc->RootDir = oldproc->RootDir ? strdup( oldproc->RootDir ) : NULL;
                newproc->nThreads = 1;
                // Reference all handles in the VFS
                VFS_ReferenceUserHandles();
+               
+               // Add to global list
+               newproc->Prev = NULL;
+               // TODO: RWLock
+               newproc->Next = gAllProcesses;
+               gAllProcesses = newproc;
+
+               newproc->FirstThread = new;
+               new->ProcessNext = NULL;
        }
        else {
                new->Process->nThreads ++;
+               new->Process = cur->Process;
+               // TODO: Locking
+               new->ProcessNext = new->Process->FirstThread;
+               new->Process->FirstThread = new;
        }
        
        // Messages are not inherited
@@ -418,9 +474,38 @@ tThread *Threads_CloneThreadZero(void)
 tTID Threads_WaitTID(int TID, int *Status)
 {      
        // Any Child
-       if(TID == -1) {
-               Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
-               return -1;
+       if(TID == -1)
+       {
+               Uint32 ev = Threads_WaitEvents(THREAD_EVENT_DEADCHILD);
+               tTID    ret = -1;
+               if( ev & THREAD_EVENT_DEADCHILD )
+               {
+                       tThread * const us = Proc_GetCurThread();
+                       // A child died, get the TID
+                       ASSERT(us->LastDeadChild);
+                       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)
+                       ASSERTC(dead_thread->Status, ==, THREAD_STAT_ZOMBIE);
+                       dead_thread->Status = THREAD_STAT_DEAD;
+                       // - Set return status
+                       if(Status)
+                               *Status = dead_thread->RetStatus;
+                       
+                       Threads_Delete( dead_thread );
+               }
+               else
+               {
+                       Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
+               }
+               return ret;
        }
        
        // Any peer/child thread
@@ -436,33 +521,15 @@ tTID Threads_WaitTID(int TID, int *Status)
        }
        
        // Specific Thread
-       if(TID > 0) {
-               tThread *t = Threads_GetThread(TID);
+       if(TID > 0)
+       {
+               // TODO: Register on thread to be poked when it dies
                tTID    ret;
-               
-               // Wait for the thread to die!
-               // TODO: Handle child also being suspended if wanted
-               while(t->Status != THREAD_STAT_ZOMBIE) {
-                       Threads_Sleep();
-                       Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
-                               Threads_GetTID(), t->TID, t->Status);
-               }
-               
-               // Set return status
-               ret = t->TID;
-               switch(t->Status)
+               // NOTE: Race condition - Other child dies, desired child dies, first death is 'lost'
+               while( (ret = Threads_WaitTID(-1, Status)) != TID )
                {
-               case THREAD_STAT_ZOMBIE:
-                       // Kill the thread
-                       t->Status = THREAD_STAT_DEAD;
-                       // TODO: Child return value?
-                       if(Status)      *Status = t->RetStatus;
-                       // add to delete queue
-                       Threads_Delete( t );
-                       break;
-               default:
-                       if(Status)      *Status = -1;
-                       break;
+                       if( ret == -1 )
+                               break;
                }
                return ret;
        }
@@ -480,15 +547,13 @@ tThread *Threads_GetThread(Uint TID)
        tThread *thread;
        
        // Search global list
-       for(thread = gAllThreads;
-               thread;
-               thread = thread->GlobalNext)
+       for( thread = gAllThreads; thread; thread = thread->GlobalNext )
        {
                if(thread->TID == TID)
                        return thread;
        }
 
-       Log("Unable to find TID %i on main list\n", TID);
+       Log_Notice("Threads", "Unable to find TID %i on main list\n", TID);
        
        return NULL;
 }
@@ -663,9 +728,13 @@ void Threads_Kill(tThread *Thread, int Status)
 
        Thread->Status = THREAD_STAT_ZOMBIE;
        SHORTREL( &glThreadListLock );
-       // TODO: Send something like SIGCHLD
-       Threads_Wake( Thread->Parent );
+       // 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
@@ -685,6 +754,64 @@ void Threads_Yield(void)
        Proc_Reschedule();
 }
 
+/**
+ * \breif Wait for the thread status to not be a specified value
+ */
+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 )
+       {
+               Proc_Reschedule();
+               if( us->Status == Status )
+                       Debug("Thread %p(%i %s) rescheduled while in %s state for %p",
+                               us, us->TID, us->ThreadName,
+                               casTHREAD_STAT[Status],
+                               __builtin_return_address(0));
+       }
+}
+
+int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock)
+{
+       SHORTLOCK( &glThreadListLock );
+       tThread *us = Threads_RemActive();
+       us->Next = NULL;
+       // - Mark as sleeping
+       us->Status = Status;
+       us->WaitPointer = Ptr;
+       us->RetStatus = Num;    // Use RetStatus as a temp variable
+               
+       // - Add to waiting
+       if( ListTail ) {
+               if(*ListTail) {
+                       (*ListTail)->Next = us;
+               }
+               else {
+                       *ListHead = us;
+               }
+               *ListTail = us;
+       }
+       else if( ListHead ) {
+               us->Next = *ListHead;
+               *ListHead = us;
+       }
+       else {
+               // Nothing
+       }
+       
+       //if( Proc_ThreadSync(us) )
+       //      return ;
+       SHORTREL( &glThreadListLock );
+       if( Lock )
+               SHORTREL( Lock );
+       Threads_int_WaitForStatusEnd(Status);
+       us->WaitPointer = NULL;
+       return us->RetStatus;
+}
+
 /**
  * \fn void Threads_Sleep(void)
  * \brief Take the current process off the run queue
@@ -716,12 +843,7 @@ void Threads_Sleep(void)
        
        // Release Spinlock
        SHORTREL( &glThreadListLock );
-
-       while(cur->Status != THREAD_STAT_ACTIVE) {
-               Proc_Reschedule();
-               if( cur->Status != THREAD_STAT_ACTIVE )
-                       Log("%i - Huh? why am I up? zzzz...", cur->TID);
-       }
+       Threads_int_WaitForStatusEnd(THREAD_STAT_SLEEPING);
 }
 
 
@@ -743,11 +865,11 @@ int Threads_Wake(tThread *Thread)
                return -EALREADY;
        
        case THREAD_STAT_SLEEPING:
-               SHORTLOCK( &glThreadListLock );
                // Remove from sleeping queue
+               SHORTLOCK( &glThreadListLock );
                Threads_int_DelFromQueue(&gSleepingThreads, Thread);
-               
                SHORTREL( &glThreadListLock );
+               
                Threads_AddActive( Thread );
                
                #if DEBUG_TRACE_STATE
@@ -813,7 +935,7 @@ int Threads_Wake(tThread *Thread)
                return -ENOTIMPL;
        
        default:
-               Warning("Threads_Wake - Unknown process status (%i)\n", Thread->Status);
+               Log_Warning("Threads", "Threads_Wake - Unknown process status (%i)", Thread->Status);
                return -EINTERNAL;
        }
 }
@@ -846,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 );
        }
        
@@ -902,42 +1037,12 @@ void Threads_AddActive(tThread *Thread)
  */
 tThread *Threads_RemActive(void)
 {
-       #if 0
-       tThread *ret = Proc_GetCurThread();
-
-       if( !IS_LOCKED(&glThreadListLock) ) {
-               Log_KernelPanic("Threads", "Threads_RemActive called without lock held");
-               return NULL;
-       }
-       
-       // Delete from active queue
-       #if SCHEDULER_TYPE == SCHED_RR_PRI
-       if( !Threads_int_DelFromQueue(&gaActiveThreads[ret->Priority], ret) )
-       #else
-       if( !Threads_int_DelFromQueue(&gActiveThreads, ret) )
+       tThread *us = Proc_GetCurThread();
+       #if DEBUG_TRACE_ACTIVEQUEUE
+       Debug("Threads_RemActive(%p(%i %s))", us, us->TID, us->ThreadName);
        #endif
-       {
-               Log_Warning("Threads", "Current thread %p(%i %s) is not on active queue",
-                       ret, ret->TID, ret->ThreadName
-                       );
-               return NULL;
-       }
-       
-       ret->Next = NULL;
-       ret->Remaining = 0;
-       
        giNumActiveThreads --;
-       // no need to decrement tickets, scheduler did it for us
-       
-       #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
-       Log("CPU%i %p (%i %s) removed, giFreeTickets = %i [nc]",
-               GetCPUNum(), ret, ret->TID, ret->ThreadName, giFreeTickets);
-       #endif
-       
-       return ret;
-       #else
-       return Proc_GetCurThread();
-       #endif
+       return us;
 }
 
 /**
@@ -1000,7 +1105,110 @@ void Threads_SegFault(tVAddr Addr)
        //Threads_Exit( 0, -1 );
 }
 
+
+void Threads_PostSignalTo(tThread *Thread, int SignalNum)
+{
+       ASSERT(Thread);
+       Log_Debug("Threads", "Signalling %i(%s) with %i", Thread->TID, Thread->ThreadName, SignalNum);
+       Thread->PendingSignal = SignalNum;
+       Threads_PostEvent(Thread, THREAD_EVENT_SIGNAL);
+}
+void Threads_PostSignal(int SignalNum)
+{
+       Threads_PostSignalTo( Proc_GetCurThread(), SignalNum );
+}
+
+void Threads_SignalGroup(tPGID PGID, int Signal)
+{
+       for( tProcess *proc = gAllProcesses; proc; proc = proc->Next )
+       {
+               if(proc->PGID == PGID)
+               {
+                       Threads_PostSignalTo(proc->FirstThread, Signal);
+               }
+       }
+}
+
+/**
+ */
+int Threads_GetPendingSignal(void)
+{
+       tThread *cur = Proc_GetCurThread();
+       
+       // Atomic AND with 0 fetches and clears in one operation
+       int ret = __sync_fetch_and_and( &cur->PendingSignal, 0 );
+       if( ret )
+       {
+               Log_Debug("Threads", "Thread %i(%s) has signal %i pending",
+                       cur->TID, cur->ThreadName, ret);
+       }
+       return ret;
+}
+
+/*
+ * \brief Update the current thread's signal handler
+ */
+void Threads_SetSignalHandler(int SignalNum, void *Handler)
+{
+       if( SignalNum <= 0 || SignalNum >= NSIGNALS )
+               return ;
+       if( !MM_IsUser(Handler) )
+               return ;
+       Proc_GetCurThread()->Process->SignalHandlers[SignalNum] = Handler;
+}
+
+/**
+ * \brief Gets the registered (or default, if none set) handler for a signal.
+ * \return Handler function pointer, OR NULL if no signal to be ignored
+ */
+void *Threads_GetSignalHandler(int SignalNum)
+{
+       // TODO: Core dump
+       void *User_Signal_Core = User_Signal_Kill;
+       
+       if( SignalNum <= 0 || SignalNum >= NSIGNALS )
+               return NULL;
+       void *ret = Proc_GetCurThread()->Process->SignalHandlers[SignalNum];
+       if( !ret || (SignalNum == SIGKILL || SignalNum == SIGSTOP) )
+       {
+               // Defaults
+               switch(SignalNum)
+               {
+               case SIGHUP:
+               case SIGINT:
+                       ret = User_Signal_Kill;
+                       break;
+               case SIGQUIT:
+               case SIGILL:
+               case SIGABRT:
+               case SIGFPE:
+                       ret = User_Signal_Core;
+                       break;
+               case SIGKILL:
+                       ret = User_Signal_Kill;
+                       break;
+               case SIGSEGV:
+                       ret = User_Signal_Core;
+                       break;
+               case SIGPIPE:
+               case SIGALRM:
+               case SIGTERM:
+                       ret = User_Signal_Kill;
+                       break;
+               default:
+                       ret = NULL;
+                       break;
+               }
+       }
+       Log_Debug("Threads", "Handler %p for signal %i", ret, SignalNum);
+       return ret;
+}
+
 // --- Process Structure Access Functions ---
+tPGID Threads_GetPGID(void)
+{
+       return Proc_GetCurThread()->Process->PGID;
+}
 tPID Threads_GetPID(void)
 {
        return Proc_GetCurThread()->Process->PID;
@@ -1049,52 +1257,92 @@ 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;
 }
 // ---
 
+void Threads_int_DumpThread(tThread *thread)
+{
+       if( !thread ) {
+               Log(" %p NULL", thread);
+               return ;
+       }
+       if( !CheckMem(thread, sizeof(tThread)) ) {
+               Log(" %p INVAL", thread);
+               return ;
+       }
+       tPID    pid = (thread->Process ? thread->Process->PID : -1);
+       const char      *statstr = (thread->Status < sizeof(casTHREAD_STAT)/sizeof(casTHREAD_STAT[0])
+               ? casTHREAD_STAT[thread->Status] : "");
+       Log(" %p %i (%i) - %s (CPU %i) - %i (%s)",
+               thread, thread->TID, pid, thread->ThreadName, thread->CurCPU,
+               thread->Status, statstr
+               );
+       switch(thread->Status)
+       {
+       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", 
+                       ((tSemaphore*)thread->WaitPointer)->ModName,
+                       ((tSemaphore*)thread->WaitPointer)->Name
+                       );
+               break;
+       case THREAD_STAT_EVENTSLEEP:
+               Log("  Event Mask: %x", thread->RetStatus);
+               break;
+       case THREAD_STAT_ZOMBIE:
+               Log("  Return Status: %i", thread->RetStatus);
+               break;
+       default:        break;
+       }
+       Log("  Priority %i, Quantum %i", thread->Priority, thread->Quantum);
+       Log("  KStack %p", thread->KernelStack);
+       if( thread->bInstrTrace )
+               Log("  Tracing Enabled");
+       Proc_DumpThreadCPUState(thread);
+}
+
 /**
  * \fn void Threads_Dump(void)
  */
 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)
                {
-                       Log(" %p %i (%i) - %s (CPU %i)",
-                               thread, thread->TID, thread->Process->PID, thread->ThreadName, thread->CurCPU);
+                       Threads_int_DumpThread(thread);
                        if(thread->Status != THREAD_STAT_ACTIVE)
                                Log("  ERROR State (%i) != THREAD_STAT_ACTIVE (%i)",
                                        thread->Status, THREAD_STAT_ACTIVE);
-                       Log("  Priority %i, Quantum %i", thread->Priority, thread->Quantum);
-                       Log("  KStack 0x%x", thread->KernelStack);
-                       if( thread->bInstrTrace )
-                               Log("  Tracing Enabled");
-                       Proc_DumpThreadCPUState(thread);
                }
        
        #if SCHEDULER_TYPE == SCHED_RR_PRI
@@ -1108,40 +1356,136 @@ void Threads_DumpActive(void)
  */
 void Threads_Dump(void)
 {
-       tThread *thread;
-       
        Log("--- Thread Dump ---");
        Threads_DumpActive();
        
        Log("All Threads:");
-       for(thread=gAllThreads;thread;thread=thread->GlobalNext)
+       for(tThread *thread = gAllThreads; thread; thread = thread->GlobalNext)
        {
-               Log(" %p %i (%i) - %s (CPU %i)",
-                       thread, thread->TID, thread->Process->PID, thread->ThreadName, thread->CurCPU);
-               Log("  State %i (%s)", thread->Status, casTHREAD_STAT[thread->Status]);
-               switch(thread->Status)
+               Threads_int_DumpThread(thread);
+       }
+}
+
+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)
                {
-               case THREAD_STAT_MUTEXSLEEP:
-                       Log("  Mutex Pointer: %p", thread->WaitPointer);
-                       break;
-               case THREAD_STAT_SEMAPHORESLEEP:
-                       Log("  Semaphore Pointer: %p", thread->WaitPointer);
-                       Log("  Semaphore Name: %s:%s", 
-                               ((tSemaphore*)thread->WaitPointer)->ModName,
-                               ((tSemaphore*)thread->WaitPointer)->Name
-                               );
-                       break;
-               case THREAD_STAT_ZOMBIE:
-                       Log("  Return Status: %i", thread->RetStatus);
+                       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;
-               default:        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);
                }
-               Log("  Priority %i, Quantum %i", thread->Priority, thread->Quantum);
-               Log("  KStack 0x%x", thread->KernelStack);
-               if( thread->bInstrTrace )
-                       Log("  Tracing Enabled");
-               Proc_DumpThreadCPUState(thread);
+
+               // 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
 }
 
 /**
@@ -1151,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
@@ -1213,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
@@ -1221,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;
 }

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