4 * - Common Thread Control
11 #define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
12 #define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
16 #define SCHED_LOTTERY 1 // Lottery scheduler
17 #define SCHED_RR_SIM 2 // Single Queue Round Robin
18 #define SCHED_RR_PRI 3 // Multi Queue Round Robin
20 #define SCHEDULER_TYPE SCHED_LOTTERY
23 #define DEFAULT_QUANTUM 10
24 #define DEFAULT_PRIORITY 5
25 #define MIN_PRIORITY 10
26 const enum eConfigTypes cCONFIG_TYPES[] = {
27 CFGT_HEAPSTR, // e.g. CFG_VFS_CWD
28 CFGT_INT, // e.g. CFG_VFS_MAXFILES
33 extern void ArchThreads_Init(void);
34 extern void Proc_Start(void);
35 extern tThread *Proc_GetCurThread(void);
36 extern int Proc_Clone(Uint *Err, Uint Flags);
37 extern void Proc_CallFaultHandler(tThread *Thread);
38 extern int GetCPUNum(void);
41 void Threads_Init(void);
42 int Threads_SetName(const char *NewName);
43 char *Threads_GetName(int ID);
44 void Threads_SetPriority(tThread *Thread, int Pri);
45 tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
46 int Threads_WaitTID(int TID, int *status);
47 tThread *Threads_GetThread(Uint TID);
48 void Threads_AddToDelete(tThread *Thread);
49 tThread *Threads_int_DelFromQueue(tThread **List, tThread *Thread);
50 void Threads_Exit(int TID, int Status);
51 void Threads_Kill(tThread *Thread, int Status);
52 void Threads_Yield(void);
53 void Threads_Sleep(void);
54 int Threads_Wake(tThread *Thread);
55 void Threads_AddActive(tThread *Thread);
56 tThread *Threads_RemActive(void);
57 int Threads_GetPID(void);
58 int Threads_GetTID(void);
59 tUID Threads_GetUID(void);
60 int Threads_SetUID(Uint *Errno, tUID ID);
61 tGID Threads_GetGID(void);
62 int Threads_SetGID(Uint *Errno, tUID ID);
63 void Threads_Dump(void);
64 void Threads_DumpActive(void);
65 void Mutex_Acquire(tMutex *Mutex);
66 void Mutex_Release(tMutex *Mutex);
67 int Mutex_IsLocked(tMutex *Mutex);
71 // Only used for the core kernel
72 tThread gThreadZero = {
73 Status: THREAD_STAT_ACTIVE, // Status
74 ThreadName: "ThreadZero", // Name
75 Quantum: DEFAULT_QUANTUM, // Default Quantum
76 Remaining: DEFAULT_QUANTUM, // Current Quantum
77 Priority: DEFAULT_PRIORITY // Number of tickets
81 tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
82 // --- Current State ---
83 volatile int giNumActiveThreads = 0; // Number of threads on the active queue
84 volatile Uint giNextTID = 1; // Next TID to allocate
85 // --- Thread Lists ---
86 tThread *gAllThreads = NULL; // All allocated threads
87 tThread *gSleepingThreads = NULL; // Sleeping Threads
88 tThread *gDeleteThreads = NULL; // Threads to delete
89 int giNumCPUs = 1; // Number of CPUs
90 BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF)
91 // --- Scheduler Types ---
92 #if SCHEDULER_TYPE == SCHED_LOTTERY
93 const int caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0};
94 volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads
95 tThread *gActiveThreads = NULL; // Currently Running Threads
96 #elif SCHEDULER_TYPE == SCHED_RR_SIM
97 tThread *gActiveThreads = NULL; // Currently Running Threads
98 #elif SCHEDULER_TYPE == SCHED_RR_PRI
99 tThread *gaActiveThreads[MIN_PRIORITY+1]; // Active threads for each priority level
101 # error "Unkown scheduler type"
106 * \fn void Threads_Init(void)
107 * \brief Initialse the thread list
109 void Threads_Init(void)
113 // Create Initial Task
114 #if SCHEDULER_TYPE == SCHED_RR_PRI
115 gaActiveThreads[gThreadZero.Priority] = &gThreadZero;
117 gActiveThreads = &gThreadZero;
120 gAllThreads = &gThreadZero;
121 giNumActiveThreads = 1;
127 * \fn void Threads_SetName(const char *NewName)
128 * \brief Sets the current thread's name
129 * \param NewName New name for the thread
130 * \return Boolean Failure
132 int Threads_SetName(const char *NewName)
134 tThread *cur = Proc_GetCurThread();
135 char *oldname = cur->ThreadName;
137 // NOTE: There is a possibility of non-thread safety here
138 // A thread could read the current name pointer before it is zeroed
140 cur->ThreadName = NULL;
142 if( IsHeap(oldname) ) free( oldname );
144 cur->ThreadName = strdup(NewName);
149 * \fn char *Threads_GetName(int ID)
150 * \brief Gets a thread's name
151 * \param ID Thread ID (-1 indicates current thread)
152 * \return Pointer to name
153 * \retval NULL Failure
155 char *Threads_GetName(tTID ID)
158 return Proc_GetCurThread()->ThreadName;
160 return Threads_GetThread(ID)->ThreadName;
164 * \fn void Threads_SetPriority(tThread *Thread, int Pri)
165 * \brief Sets the priority of a task
166 * \param Thread Thread to update ticket count (NULL means current thread)
167 * \param Pri New priority
169 void Threads_SetPriority(tThread *Thread, int Pri)
171 // Get current thread
172 if(Thread == NULL) Thread = Proc_GetCurThread();
174 // - If < 0, set to lowest priority
175 if(Pri < 0) Pri = MIN_PRIORITY;
176 if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY;
178 // Do we actually have to do anything?
179 if( Pri == Thread->Priority ) return;
181 #if SCHEDULER_TYPE == SCHED_RR_PRI
182 SHORTLOCK( &glThreadListLock );
183 // Remove from old priority
184 Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread );
186 Thread->Next = gaActiveThreads[Pri];
187 gaActiveThreads[Pri] = Thread;
188 Thread->Priority = Pri;
189 SHORTREL( &glThreadListLock );
191 // If this isn't the current thread, we need to lock
192 if( Thread != Proc_GetCurThread() )
194 SHORTLOCK( &glThreadListLock );
196 #if SCHEDULER_TYPE == SCHED_LOTTERY
197 giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri];
198 # if DEBUG_TRACE_TICKETS
199 Log("Threads_SetTickets: new giFreeTickets = %i", giFreeTickets);
202 Thread->Priority = Pri;
203 SHORTREL( &glThreadListLock );
206 Thread->Priority = Pri;
211 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
212 * \brief Clone the TCB of the current thread
213 * \param Err Error pointer
214 * \param Flags Flags for something... (What is this for?)
216 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
220 cur = Proc_GetCurThread();
222 // Allocate and duplicate
223 new = malloc(sizeof(tThread));
228 memcpy(new, cur, sizeof(tThread));
232 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
233 new->Status = THREAD_STAT_PREINIT;
237 new->TID = giNextTID++;
241 new->ThreadName = strdup(cur->ThreadName);
243 // Set Thread Group ID (PID)
245 new->TGID = new->TID;
247 new->TGID = cur->TGID;
249 // Messages are not inherited
250 new->Messages = NULL;
251 new->LastMessage = NULL;
254 new->Remaining = new->Quantum = cur->Quantum;
255 new->Priority = cur->Priority;
257 // Set Signal Handlers
258 new->CurFaultNum = 0;
259 new->FaultHandler = cur->FaultHandler;
261 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
263 switch(cCONFIG_TYPES[i])
266 new->Config[i] = cur->Config[i];
270 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
277 // Maintain a global list of threads
278 SHORTLOCK( &glThreadListLock );
279 new->GlobalPrev = NULL; // Protect against bugs
280 new->GlobalNext = gAllThreads;
282 SHORTREL( &glThreadListLock );
288 * \brief Get a configuration pointer from the Per-Thread data area
289 * \param ID Config slot ID
290 * \return Pointer at ID
292 Uint *Threads_GetCfgPtr(int ID)
294 if(ID < 0 || ID >= NUM_CFG_ENTRIES) {
295 Warning("Threads_GetCfgPtr: Index %i is out of bounds", ID);
299 return &Proc_GetCurThread()->Config[ID];
303 * \brief Wait for a task to change state
304 * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
305 * \param Status Thread return status
306 * \return TID of child that changed state
308 tTID Threads_WaitTID(int TID, int *Status)
312 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
316 // Any peer/child thread
318 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
324 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
330 tThread *t = Threads_GetThread(TID);
331 int initStatus = t->Status;
334 // Wait for the thread to die!
335 if(initStatus != THREAD_STAT_ZOMBIE) {
336 // TODO: Handle child also being suspended if wanted
337 while(t->Status != THREAD_STAT_ZOMBIE) {
339 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
340 Threads_GetTID(), t->TID, t->Status);
345 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
346 Threads_GetTID(), t->TID, t->Status);
350 case THREAD_STAT_ZOMBIE:
352 t->Status = THREAD_STAT_DEAD;
353 // TODO: Child return value?
354 if(Status) *Status = t->RetStatus;
355 // add to delete queue
356 Threads_AddToDelete( t );
359 if(Status) *Status = -1;
369 * \brief Gets a thread given its TID
370 * \param TID Thread ID
371 * \return Thread pointer
373 tThread *Threads_GetThread(Uint TID)
377 // Search global list
378 for(thread = gAllThreads;
380 thread = thread->GlobalNext)
382 if(thread->TID == TID)
386 Log("Unable to find TID %i on main list\n", TID);
392 * \brief Adds a thread to the delete queue
393 * \param Thread Thread to delete
395 void Threads_AddToDelete(tThread *Thread)
397 // Add to delete queue
398 // TODO: Is locking needed?
400 Thread->Next = gDeleteThreads;
401 gDeleteThreads = Thread;
404 gDeleteThreads = Thread;
409 * \brief Deletes an entry from a list
410 * \param List Pointer to the list head
411 * \param Thread Thread to find
414 tThread *Threads_int_DelFromQueue(tThread **List, tThread *Thread)
416 tThread *ret, *prev = NULL;
419 ret && ret != Thread;
420 prev = ret, ret = ret->Next
423 // Is the thread on the list
425 //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
430 *List = Thread->Next;
431 //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
434 prev->Next = Thread->Next;
435 //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
442 * \brief Exit the current process (or another?)
443 * \param TID Thread ID to kill
444 * \param Status Exit status
446 void Threads_Exit(int TID, int Status)
449 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
451 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
453 // Halt forever, just in case
458 * \fn void Threads_Kill(tThread *Thread, int Status)
459 * \brief Kill a thread
460 * \param Thread Thread to kill
461 * \param Status Status code to return to the parent
463 void Threads_Kill(tThread *Thread, int Status)
467 // TODO: Kill all children
471 // TODO: I should keep a .Parent pointer, and a .Children list
472 for(child = gActiveThreads;
476 if(child->PTID == Thread->TID)
477 Threads_Kill(child, -1);
482 ///\note Double lock is needed due to overlap of lock areas
484 // Lock thread (stop us recieving messages)
485 SHORTLOCK( &Thread->IsLocked );
487 // Clear Message Queue
488 while( Thread->Messages )
490 msg = Thread->Messages->Next;
491 free( Thread->Messages );
492 Thread->Messages = msg;
496 SHORTLOCK( &glThreadListLock );
498 // Delete from active list
499 #if SCHEDULER_TYPE == SCHED_RR_PRI
500 if( !Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread ) )
502 if( !Threads_int_DelFromQueue( &gActiveThreads, Thread ) )
505 Warning("Proc_Exit - Current thread is not on the active queue");
506 SHORTREL( &glThreadListLock );
507 SHORTREL( &Thread->IsLocked );
511 // Ensure that we are not rescheduled
512 Thread->Remaining = 0; // Clear Remaining Quantum
513 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
515 // Update bookkeeping
516 giNumActiveThreads --;
517 #if SCHEDULER_TYPE == SCHED_LOTTERY
518 if( Thread != Proc_GetCurThread() )
519 giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ];
523 Thread->RetStatus = Status;
525 // Don't Zombie if we are being killed because our parent is
528 Thread->Status = THREAD_STAT_DEAD;
529 Threads_AddToDelete( Thread );
531 Thread->Status = THREAD_STAT_ZOMBIE;
533 Threads_Wake( Thread->Parent );
536 Log("Thread %i went *hurk* (%i)", Thread->TID, Thread->Status);
539 SHORTREL( &glThreadListLock );
540 SHORTREL( &Thread->IsLocked ); // TODO: We may not actually be released...
550 * \brief Yield remainder of the current thread's timeslice
552 void Threads_Yield(void)
554 tThread *thread = Proc_GetCurThread();
555 thread->Remaining = 0;
556 //while(thread->Remaining == 0)
561 * \fn void Threads_Sleep(void)
562 * \brief Take the current process off the run queue
564 void Threads_Sleep(void)
566 tThread *cur = Proc_GetCurThread();
569 SHORTLOCK( &glThreadListLock );
571 // Don't sleep if there is a message waiting
572 if( cur->Messages ) {
573 SHORTREL( &glThreadListLock );
577 // Remove us from running queue
579 // Mark thread as sleeping
580 cur->Status = THREAD_STAT_SLEEPING;
582 // Add to Sleeping List (at the top)
583 cur->Next = gSleepingThreads;
584 gSleepingThreads = cur;
587 #if DEBUG_TRACE_STATE
588 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
592 SHORTREL( &glThreadListLock );
594 while(cur->Status != THREAD_STAT_ACTIVE) HALT();
599 * \fn int Threads_Wake( tThread *Thread )
600 * \brief Wakes a sleeping/waiting thread up
601 * \param Thread Thread to wake
602 * \return Boolean Failure (Returns ERRNO)
603 * \warning This should ONLY be called with task switches disabled
605 int Threads_Wake(tThread *Thread)
610 switch(Thread->Status)
612 case THREAD_STAT_ACTIVE:
613 Log("Thread_Wake: Waking awake thread (%i)", Thread->TID);
616 case THREAD_STAT_SLEEPING:
617 SHORTLOCK( &glThreadListLock );
618 // Remove from sleeping queue
619 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
621 Threads_AddActive( Thread );
623 #if DEBUG_TRACE_STATE
624 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
626 SHORTREL( &glThreadListLock );
629 case THREAD_STAT_WAITING:
630 Warning("Thread_Wake - Waiting threads are not currently supported");
633 case THREAD_STAT_DEAD:
634 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
638 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
644 * \brief Wake a thread given the TID
645 * \param TID Thread ID to wake
646 * \return Boolean Faulure (errno)
648 int Threads_WakeTID(tTID TID)
650 tThread *thread = Threads_GetThread(TID);
654 ret = Threads_Wake( thread );
655 //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
660 * \brief Adds a thread to the active queue
662 void Threads_AddActive(tThread *Thread)
664 SHORTLOCK( &glThreadListLock );
666 if( Thread->Status == THREAD_STAT_ACTIVE ) {
667 tThread *cur = Proc_GetCurThread();
668 Warning("WTF, CPU%i %p (%i %s) is adding %p (%i %s) when it is active",
669 GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName);
673 Thread->Status = THREAD_STAT_ACTIVE;
675 // Add to active list
676 #if SCHEDULER_TYPE == SCHED_RR_PRI
677 Thread->Next = gaActiveThreads[Thread->Priority];
678 gaActiveThreads[Thread->Priority] = Thread;
680 Thread->Next = gActiveThreads;
681 gActiveThreads = Thread;
684 // Update bookkeeping
685 giNumActiveThreads ++;
687 #if SCHEDULER_TYPE == SCHED_LOTTERY
688 giFreeTickets += caiTICKET_COUNTS[ Thread->Priority ];
689 # if DEBUG_TRACE_TICKETS
690 Log("Threads_AddActive: CPU%i %p %i (%s) added, new giFreeTickets = %i",
691 GetCPUNum(), Thread, Thread->TID, Thread->ThreadName, giFreeTickets);
695 SHORTREL( &glThreadListLock );
699 * \brief Removes the current thread from the active queue
700 * \warning This should ONLY be called with task switches disabled
701 * \return Current thread pointer
703 tThread *Threads_RemActive(void)
705 tThread *ret = Proc_GetCurThread();
707 SHORTLOCK( &glThreadListLock );
709 // Delete from active queue
710 #if SCHEDULER_TYPE == SCHED_RR_PRI
711 if( !Threads_int_DelFromQueue(&gaActiveThreads[ret->Priority], ret) )
713 if( !Threads_int_DelFromQueue(&gActiveThreads, ret) )
716 SHORTREL( &glThreadListLock );
724 giNumActiveThreads --;
725 // no need to decrement tickets, scheduler did it for us
727 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
728 Log("Threads_RemActive: CPU%i %p %i (%s) removed, giFreeTickets = %i",
729 GetCPUNum(), ret, ret->TID, ret->ThreadName, giFreeTickets);
732 SHORTREL( &glThreadListLock );
738 * \fn void Threads_SetFaultHandler(Uint Handler)
739 * \brief Sets the signal handler for a signal
741 void Threads_SetFaultHandler(Uint Handler)
743 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
744 Proc_GetCurThread()->FaultHandler = Handler;
748 * \fn void Threads_Fault(int Num)
749 * \brief Calls a fault handler
751 void Threads_Fault(int Num)
753 tThread *thread = Proc_GetCurThread();
755 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
759 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
761 switch(thread->FaultHandler)
764 Threads_Kill(thread, -1);
767 case 1: // Dump Core?
768 Threads_Kill(thread, -1);
773 // Double Fault? Oh, F**k
774 if(thread->CurFaultNum != 0) {
775 Threads_Kill(thread, -1); // For now, just kill
779 thread->CurFaultNum = Num;
781 Proc_CallFaultHandler(thread);
784 // --- Process Structure Access Functions ---
785 tPID Threads_GetPID(void)
787 return Proc_GetCurThread()->TGID;
789 tTID Threads_GetTID(void)
791 return Proc_GetCurThread()->TID;
793 tUID Threads_GetUID(void)
795 return Proc_GetCurThread()->UID;
797 tGID Threads_GetGID(void)
799 return Proc_GetCurThread()->GID;
802 int Threads_SetUID(Uint *Errno, tUID ID)
804 tThread *t = Proc_GetCurThread();
809 Log_Debug("Threads", "TID %i's UID set to %i", t->TID, ID);
814 int Threads_SetGID(Uint *Errno, tGID ID)
816 tThread *t = Proc_GetCurThread();
821 Log_Debug("Threads", "TID %i's GID set to %i", t->TID, ID);
827 * \fn void Threads_Dump(void)
829 void Threads_DumpActive(void)
832 #if SCHEDULER_TYPE == SCHED_RR_PRI
836 Log("Active Threads: (%i reported)", giNumActiveThreads);
838 #if SCHEDULER_TYPE == SCHED_RR_PRI
839 for( i = 0; i < MIN_PRIORITY+1; i++ )
841 for(thread=gaActiveThreads[i];thread;thread=thread->Next)
843 for(thread=gActiveThreads;thread;thread=thread->Next)
846 Log(" %p %i (%i) - %s (CPU %i)",
847 thread, thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
848 if(thread->Status != THREAD_STAT_ACTIVE)
849 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
850 Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
851 Log(" KStack 0x%x", thread->KernelStack);
854 #if SCHEDULER_TYPE == SCHED_RR_PRI
860 * \fn void Threads_Dump(void)
861 * \brief Dumps a list of currently running threads
863 void Threads_Dump(void)
867 Log("--- Thread Dump ---");
868 Threads_DumpActive();
871 for(thread=gAllThreads;thread;thread=thread->GlobalNext)
873 Log(" %p %i (%i) - %s (CPU %i)",
874 thread, thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
875 Log(" State %i (%s)", thread->Status, casTHREAD_STAT[thread->Status]);
876 switch(thread->Status)
878 case THREAD_STAT_MUTEXSLEEP:
879 Log(" Mutex Pointer: %p", thread->WaitPointer);
881 case THREAD_STAT_ZOMBIE:
882 Log(" Return Status: %i", thread->RetStatus);
886 Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
887 Log(" KStack 0x%x", thread->KernelStack);
892 * \brief Gets the next thread to run
893 * \param CPU Current CPU
894 * \param Last The thread the CPU was running
896 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
900 // If this CPU has the lock, we must let it complete
901 if( CPU_HAS_LOCK( &glThreadListLock ) )
904 // Don't change threads if the current CPU has switches disabled
905 if( gaThreads_NoTaskSwitch[CPU] )
910 SHORTLOCK( &glThreadListLock );
912 // Clear Delete Queue
913 // - I should probably put this in a worker thread to avoid calling free() in the scheduler
914 while(gDeleteThreads)
916 thread = gDeleteThreads->Next;
917 if( IS_LOCKED(&gDeleteThreads->IsLocked) ) { // Only free if structure is unused
919 gDeleteThreads->Status = THREAD_STAT_BURIED;
921 if( IsHeap(gDeleteThreads->ThreadName) )
922 free(gDeleteThreads->ThreadName);
923 // Remove from global list
924 if( gDeleteThreads == gAllThreads )
925 gAllThreads = gDeleteThreads->GlobalNext;
927 gDeleteThreads->GlobalPrev->GlobalNext = gDeleteThreads->GlobalNext;
928 free( gDeleteThreads );
930 gDeleteThreads = thread;
933 // No active threads, just take a nap
934 if(giNumActiveThreads == 0) {
935 SHORTREL( &glThreadListLock );
936 #if DEBUG_TRACE_TICKETS
937 Log("No active threads");
942 #if SCHEDULER_TYPE != SCHED_RR_PRI
943 // Special case: 1 thread
944 if(giNumActiveThreads == 1) {
945 if( gActiveThreads->CurCPU == -1 )
946 gActiveThreads->CurCPU = CPU;
948 SHORTREL( &glThreadListLock );
950 if( gActiveThreads->CurCPU == CPU )
951 return gActiveThreads;
953 return NULL; // CPU has nothing to do
957 // Allow the old thread to be scheduled again
959 if( Last->Status == THREAD_STAT_ACTIVE ) {
960 #if SCHEDULER_TYPE == SCHED_LOTTERY
961 giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
962 # if DEBUG_TRACE_TICKETS
963 LogF(" CPU %i released %p (%i %s) into the pool (%i tickets in pool)\n",
964 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets);
968 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
970 LogF(" CPU %i released %p (%i %s)->Status = %i (Released)\n",
971 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
979 #if SCHEDULER_TYPE == SCHED_LOTTERY
984 for(thread = gActiveThreads; thread; thread = thread->Next) {
985 if(thread->CurCPU >= 0) continue;
986 if(thread->Status != THREAD_STAT_ACTIVE)
987 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
988 thread, thread->TID, thread->ThreadName, thread->Status);
989 if(thread->Next == thread) {
990 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
991 thread, thread->TID, thread->ThreadName, thread->Status);
993 number += caiTICKET_COUNTS[ thread->Priority ];
995 if(number != giFreeTickets) {
996 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
997 giFreeTickets, number, CPU);
1001 // No free tickets (all tasks delegated to cores)
1002 if( giFreeTickets == 0 ) {
1003 SHORTREL(&glThreadListLock);
1007 // Get the ticket number
1008 ticket = number = rand() % giFreeTickets;
1010 // Find the next thread
1011 for(thread=gActiveThreads;thread;thread=thread->Next)
1013 if(thread->CurCPU >= 0) continue;
1014 if( caiTICKET_COUNTS[ thread->Priority ] > number) break;
1015 number -= caiTICKET_COUNTS[ thread->Priority ];
1018 // If we didn't find a thread, something went wrong
1022 for(thread=gActiveThreads;thread;thread=thread->Next) {
1023 if(thread->CurCPU >= 0) continue;
1024 number += caiTICKET_COUNTS[ thread->Priority ];
1026 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
1027 giFreeTickets, number);
1029 # if DEBUG_TRACE_TICKETS
1030 LogF(" CPU%i giFreeTickets = %i, running %p (%i %s CPU=%i)\n",
1031 CPU, giFreeTickets, thread, thread->TID, thread->ThreadName, thread->CurCPU);
1034 giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
1038 // Priority based round robin scheduler
1040 #elif SCHEDULER_TYPE == SCHED_RR_PRI
1043 for( i = 0; i < MIN_PRIORITY + 1; i ++ )
1045 for(thread = gaActiveThreads[i]; thread; thread = thread->Next)
1047 if( thread->CurCPU == -1 ) break;
1049 // If we fall onto the same queue again, special handling is
1051 if( i == Last->Priority ) {
1052 tThread *savedThread = thread;
1054 // Find the next unscheduled thread in the list
1055 for( thread = Last->Next; thread; thread = thread->Next )
1057 if( thread->CurCPU == -1 ) break;
1059 // If we don't find anything after, just use the one
1061 if( !thread ) thread = savedThread;
1063 // Found a thread? Schedule it!
1069 SHORTREL(&glThreadListLock);
1073 #elif SCHEDULER_TYPE == SCHED_RR_SIM
1075 // Find the next unscheduled thread in the list
1076 for( thread = Last->Next; thread; thread = thread->Next )
1078 if( thread->CurCPU == -1 ) break;
1080 // If we don't find anything after, search from the beginning
1083 for(thread = gActiveThreads; thread; thread = thread->Next)
1085 if( thread->CurCPU == -1 ) break;
1091 SHORTREL(&glThreadListLock);
1096 # error "Unimplemented scheduling algorithm"
1099 // Make the new thread non-schedulable
1100 thread->CurCPU = CPU;
1102 SHORTREL( &glThreadListLock );
1108 * \fn void Threads_SegFault(tVAddr Addr)
1109 * \brief Called when a Segment Fault occurs
1111 void Threads_SegFault(tVAddr Addr)
1113 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
1115 //Threads_Exit( 0, -1 );
1119 * \brief Acquire a heavy mutex
1120 * \param Mutex Mutex to acquire
1122 * This type of mutex checks if the mutex is avaliable, and acquires it
1123 * if it is. Otherwise, the current thread is added to the mutex's wait
1124 * queue and the thread suspends. When the holder of the mutex completes,
1125 * the oldest thread (top thread) on the queue is given the lock and
1128 void Mutex_Acquire(tMutex *Mutex)
1130 tThread *us = Proc_GetCurThread();
1133 SHORTLOCK( &Mutex->Protector );
1135 //Log("Mutex_Acquire: (%p)", Mutex);
1137 // Check if the lock is already held
1138 if( Mutex->Owner ) {
1139 SHORTLOCK( &glThreadListLock );
1140 // - Remove from active list
1141 us = Threads_RemActive();
1143 // - Mark as sleeping
1144 us->Status = THREAD_STAT_MUTEXSLEEP;
1145 us->WaitPointer = Mutex;
1148 if(Mutex->LastWaiting) {
1149 Mutex->LastWaiting->Next = us;
1150 Mutex->LastWaiting = us;
1153 Mutex->Waiting = us;
1154 Mutex->LastWaiting = us;
1160 for( t = Mutex->Waiting; t; t = t->Next, i++ )
1161 Log("[%i] (tMutex)%p->Waiting[%i] = %p (%i %s)", us->TID, Mutex, i,
1162 t, t->TID, t->ThreadName);
1166 SHORTREL( &glThreadListLock );
1167 SHORTREL( &Mutex->Protector );
1168 while(us->Status == THREAD_STAT_MUTEXSLEEP) Threads_Yield();
1169 // We're only woken when we get the lock
1170 us->WaitPointer = NULL;
1172 // Ooh, let's take it!
1175 SHORTREL( &Mutex->Protector );
1179 extern tMutex glPhysAlloc;
1180 if( Mutex != &glPhysAlloc )
1181 LogF("Mutex %p taken by %i %p\n", Mutex, us->TID, __builtin_return_address(0));
1186 * \brief Release a held mutex
1187 * \param Mutex Mutex to release
1189 void Mutex_Release(tMutex *Mutex)
1191 SHORTLOCK( &Mutex->Protector );
1192 //Log("Mutex_Release: (%p)", Mutex);
1193 if( Mutex->Waiting ) {
1194 Mutex->Owner = Mutex->Waiting; // Set owner
1195 Mutex->Waiting = Mutex->Waiting->Next; // Next!
1196 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
1197 // 2010-10-02 21:50 - Comemerating the death of the longest single
1198 // blocker in the Acess2 history. REMEMBER TO
1199 // FUCKING MAINTAIN YOUR FUCKING LISTS DIPWIT
1200 if( Mutex->LastWaiting == Mutex->Owner )
1201 Mutex->LastWaiting = NULL;
1204 SHORTLOCK( &glThreadListLock );
1205 if( Mutex->Owner->Status != THREAD_STAT_ACTIVE )
1206 Threads_AddActive(Mutex->Owner);
1207 SHORTREL( &glThreadListLock );
1210 Mutex->Owner = NULL;
1212 SHORTREL( &Mutex->Protector );
1215 extern tMutex glPhysAlloc;
1216 if( Mutex != &glPhysAlloc )
1217 LogF("Mutex %p released by %i %p\n", Mutex, Threads_GetTID(), __builtin_return_address(0));
1222 * \brief Is this mutex locked?
1223 * \param Mutex Mutex pointer
1225 int Mutex_IsLocked(tMutex *Mutex)
1227 return Mutex->Owner != NULL;
1231 EXPORT(Threads_GetUID);
1232 EXPORT(Threads_GetGID);
1233 EXPORT(Mutex_Acquire);
1234 EXPORT(Mutex_Release);
1235 EXPORT(Mutex_IsLocked);