+
+/**
+ * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
+ * \brief Gets the next thread to run
+ * \param CPU Current CPU
+ * \param Last The thread the CPU was running
+ */
+tThread *Threads_GetNextToRun(int CPU, tThread *Last)
+{
+ tThread *thread;
+ int ticket;
+ int number;
+
+ // TODO: Enable the code to tell if the current CPU has the lock or
+ // another does.
+
+ // Check if the thread list is locked by other code
+ // - If so, don't switch (give it a chance to complete)
+ if( IS_LOCKED(&glThreadListLock) )
+ return Last;
+
+ // Clear Delete Queue
+ while(gDeleteThreads)
+ {
+ thread = gDeleteThreads->Next;
+ if(gDeleteThreads->IsLocked) { // Only free if structure is unused
+ gDeleteThreads->Status = THREAD_STAT_NULL;
+ free( gDeleteThreads );
+ }
+ gDeleteThreads = thread;
+ }
+
+ // No active threads, just take a nap
+ if(giNumActiveThreads == 0) {
+ #if DEBUG_TRACE_TICKETS
+ Log("No active threads");
+ #endif
+ return NULL;
+ }
+
+ // Lock thread list
+ // - HLT lock (Used because only another CPU can obtain the lock,
+ // but it has a potentially long lock period)
+ // - Well, this CPU can obtain the lock, but that is aliveviated by
+ // the above.
+ TIGHTLOCK( &glThreadListLock );
+
+ // Special case: 1 thread
+ if(giNumActiveThreads == 1) {
+ if( gActiveThreads->CurCPU == -1 )
+ gActiveThreads->CurCPU = CPU;
+ RELEASE( &glThreadListLock );
+ if( gActiveThreads->CurCPU == CPU )
+ return gActiveThreads;
+ return NULL; // CPU has nothing to do
+ }
+
+ // Allow the old thread to be scheduled again
+ if( Last ) {
+ if( Last->Status == THREAD_STAT_ACTIVE ) {
+ giFreeTickets += Last->NumTickets;
+ #if DEBUG_TRACE_TICKETS
+ LogF(" CPU %i released %p (%s) into the pool (%i tickets in pool)\n",
+ CPU, Last, Last->ThreadName, Last->NumTickets);
+ #endif
+ }
+ #if DEBUG_TRACE_TICKETS
+ else
+ LogF(" %p (%s)->Status = %i\n", Last, Last->ThreadName, Last->Status);
+ #endif
+ Last->CurCPU = -1;
+ }
+
+ #if 1
+ number = 0;
+ for(thread=gActiveThreads;thread;thread=thread->Next) {
+ if(thread->CurCPU >= 0) continue;
+ number += thread->NumTickets;
+ }
+ 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 ) {
+ RELEASE(&glThreadListLock);
+ return NULL;
+ }
+
+ // Get the ticket number
+ ticket = number = rand() % giFreeTickets;
+
+ // Find the next thread
+ for(thread=gActiveThreads;thread;thread=thread->Next)
+ {
+ if(thread->CurCPU >= 0) continue;
+ if(thread->NumTickets > number) break;
+ number -= thread->NumTickets;
+ }
+ // Error Check
+ if(thread == NULL)
+ {
+ number = 0;
+ for(thread=gActiveThreads;thread;thread=thread->Next) {
+ if(thread->CurCPU >= 0) continue;
+ number += thread->NumTickets;
+ }
+ Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
+ giFreeTickets, number);
+ }
+ #if DEBUG_TRACE_TICKETS
+ LogF(" CPU%i giFreeTickets = %i\n", CPU, giFreeTickets);
+ #endif
+
+ // Make the new thread non-schedulable
+ giFreeTickets -= thread->NumTickets;
+ thread->CurCPU = CPU;
+
+ //Threads_Dump();
+ #if DEBUG_TRACE_TICKETS
+ LogF(" CPU%i giFreeTickets = %i, giving %p (%s CPU=%i)\n",
+ CPU, giFreeTickets, thread, thread->ThreadName, thread->CurCPU);
+ #endif
+
+ RELEASE( &glThreadListLock );
+
+ return thread;
+}
+
+/**
+ * \fn void Threads_SegFault(tVAddr Addr)
+ * \brief Called when a Segment Fault occurs
+ */
+void Threads_SegFault(tVAddr Addr)
+{
+ Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
+ Threads_Fault( 1 );
+ //Threads_Exit( 0, -1 );
+}
+
+// === EXPORTS ===
+EXPORT(Threads_GetUID);