3 * - By John Hodge (thePowersGang)
5 * - Common Thread Control
9 #include <threads_int.h>
12 #include <semaphore.h>
14 #include <vfs_threads.h> // VFS Handle maintainence
18 #define DEBUG_TRACE_ACTIVEQUEUE 0 // Trace adds/removals from the active queue
19 #define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
20 #define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
21 #define DEBUG_TRACE_SCHEDULE 0 // Trace scheduling
25 #define SCHED_LOTTERY 1 // Lottery scheduler
26 #define SCHED_RR_SIM 2 // Single Queue Round Robin
27 #define SCHED_RR_PRI 3 // Multi Queue Round Robin
29 #define SCHEDULER_TYPE SCHED_RR_PRI
32 #define DEFAULT_QUANTUM 5
33 #define DEFAULT_PRIORITY 5
34 #define MIN_PRIORITY 10
36 #define PRIthread_fmt "%p(%i %s)"
37 #define PRIthread_args(t) (t), ((t)?(t)->TID:-1), ((t)?(t)->ThreadName:"-")
40 extern void User_Signal_Kill(int SigNum);
50 void Threads_Init(void);
52 void Threads_Delete(tThread *Thread);
53 int Threads_SetName(const char *NewName);
55 char *Threads_GetName(tTID ID);
57 void Threads_SetPriority(tThread *Thread, int Pri);
58 tThread *Threads_CloneTCB(Uint Flags);
59 int Threads_WaitTID(int TID, int *status);
60 tThread *Threads_GetThread(Uint TID);
62 tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread);
63 void Threads_int_AddToList(tThreadList *List, tThread *Thread);
65 void Threads_Exit(int TID, int Status);
66 void Threads_Kill(tThread *Thread, int Status);
67 void Threads_Yield(void);
68 int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock);
69 void Threads_Sleep(void);
70 int Threads_Wake(tThread *Thread);
71 void Threads_AddActive(tThread *Thread);
72 tThread *Threads_RemActive(void);
74 void Threads_ToggleTrace(int TID);
75 void Threads_Fault(int Num);
76 void Threads_SegFault(tVAddr Addr);
77 void Threads_PostSignalTo(tThread *Thread, int SignalNum);
79 void Threads_PostSignal(int SignalNum);
80 void Threads_SignalGroup(tPGID PGID, int SignalNum);
82 int Threads_GetPendingSignal(void);
83 void Threads_SetSignalHandler(int SignalNum, void *Handler);
84 void *Threads_GetSignalHandler(int SignalNum);
86 int Threads_GetPID(void);
87 int Threads_GetTID(void);
88 tUID Threads_GetUID(void);
89 tGID Threads_GetGID(void);
90 int Threads_SetUID(Uint *Errno, tUID ID);
91 int Threads_SetGID(Uint *Errno, tUID ID);
93 void Threads_int_DumpThread(tThread *thread);
94 void Threads_Dump(void);
95 void Threads_DumpActive(void);
96 tThread *Threads_int_GetRunnable(void);
100 tProcess gProcessZero = {
102 // Only used for the core kernel
103 tThread gThreadZero = {
104 .Status = THREAD_STAT_ACTIVE, // Status
105 .ThreadName = (char*)"ThreadZero", // Name
106 .Quantum = DEFAULT_QUANTUM, // Default Quantum
107 .Remaining = DEFAULT_QUANTUM, // Current Quantum
108 .Priority = DEFAULT_PRIORITY // Number of tickets
111 tProcess *gAllProcesses = &gProcessZero;
113 tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
114 // --- Current State ---
115 volatile int giNumActiveThreads = 0; // Number of threads on the active queue
116 volatile Uint giNextTID = 1; // Next TID to allocate
117 // --- Thread Lists ---
118 tThread *gAllThreads = NULL; // All allocated threads
119 tThreadList gSleepingThreads; // Sleeping Threads
120 int giNumCPUs = 1; // Number of CPUs
121 BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF)
122 // --- Scheduler Types ---
123 #if SCHEDULER_TYPE == SCHED_LOTTERY
124 const int caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0};
125 volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads
126 tThreadList gActiveThreads; // Currently Running Threads
127 #elif SCHEDULER_TYPE == SCHED_RR_SIM
128 tThreadList gActiveThreads; // Currently Running Threads
129 #elif SCHEDULER_TYPE == SCHED_RR_PRI
130 tThreadList gaActiveThreads[MIN_PRIORITY+1]; // Active threads for each priority level
132 # error "Unkown scheduler type"
137 * \fn void Threads_Init(void)
138 * \brief Initialse the thread list
140 void Threads_Init(void)
144 Log_Debug("Threads", "Offsets of tThread");
145 Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority));
146 Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack));
148 // Create Initial Task
149 gAllThreads = &gThreadZero;
150 giNumActiveThreads = 1;
151 gThreadZero.Process = &gProcessZero;
156 void Threads_Delete(tThread *Thread)
159 Thread->Status = THREAD_STAT_BURIED;
161 // Clear out process state
162 Proc_ClearThread(Thread);
164 Thread->Process->nThreads --;
166 if( Thread->Process->FirstThread == Thread )
168 Thread->Process->FirstThread = Thread->ProcessNext;
172 tThread *prev = Thread->Process->FirstThread;
173 while(prev && prev->ProcessNext != Thread)
174 prev = prev->ProcessNext;
176 Log_Error("Threads", "Thread %p(%i %s) is not on the process's list",
177 Thread, Thread->TID, Thread->ThreadName
180 prev->ProcessNext = Thread->ProcessNext;
183 // If the final thread is being terminated, clean up the process
184 if( Thread->Process->nThreads == 0 )
186 tProcess *proc = Thread->Process;
188 // Remove from global process list
191 proc->Prev->Next = proc->Next;
193 gAllProcesses = proc->Next;
195 proc->Next->Prev = proc->Prev;
198 VFS_CloseAllUserHandles();
199 // Architecture cleanup
200 Proc_ClearProcess( proc );
201 // VFS Configuration strings
202 if( proc->CurrentWorkingDir)
203 free( proc->CurrentWorkingDir );
205 free( proc->RootDir );
206 // Process descriptor
211 if( IsHeap(Thread->ThreadName) )
212 free(Thread->ThreadName);
214 // Remove from global list
215 // TODO: Lock this too
216 if( Thread == gAllThreads )
217 gAllThreads = Thread->GlobalNext;
219 Thread->GlobalPrev->GlobalNext = Thread->GlobalNext;
225 * \fn void Threads_SetName(const char *NewName)
226 * \brief Sets the current thread's name
227 * \param NewName New name for the thread
228 * \return Boolean Failure
230 int Threads_SetName(const char *NewName)
232 tThread *cur = Proc_GetCurThread();
233 char *oldname = cur->ThreadName;
235 // NOTE: There is a possibility of non-thread safety here
236 // A thread could read the current name pointer before it is zeroed
238 cur->ThreadName = NULL;
240 if( IsHeap(oldname) ) free( oldname );
241 cur->ThreadName = strdup(NewName);
243 Log_Debug("Threads", "Thread renamed to '%s'", NewName);
249 * \fn char *Threads_GetName(int ID)
250 * \brief Gets a thread's name
251 * \param ID Thread ID (-1 indicates current thread)
252 * \return Pointer to name
253 * \retval NULL Failure
255 char *Threads_GetName(tTID ID)
258 return Proc_GetCurThread()->ThreadName;
260 return Threads_GetThread(ID)->ThreadName;
264 * \fn void Threads_SetPriority(tThread *Thread, int Pri)
265 * \brief Sets the priority of a task
266 * \param Thread Thread to update ticket count (NULL means current thread)
267 * \param Pri New priority
269 void Threads_SetPriority(tThread *Thread, int Pri)
271 // Get current thread
272 if(Thread == NULL) Thread = Proc_GetCurThread();
274 // - If < 0, set to lowest priority
275 // - Minumum priority is actualy a high number, 0 is highest
276 if(Pri < 0) Pri = MIN_PRIORITY;
277 if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY;
279 // Do we actually have to do anything?
280 if( Pri == Thread->Priority ) return;
282 #if SCHEDULER_TYPE == SCHED_RR_PRI
283 if( Thread != Proc_GetCurThread() )
285 SHORTLOCK( &glThreadListLock );
286 // Remove from old priority
287 Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread );
289 Threads_int_AddToList( &gaActiveThreads[Pri], Thread );
290 Thread->Priority = Pri;
291 SHORTREL( &glThreadListLock );
294 Thread->Priority = Pri;
296 // If this isn't the current thread, we need to lock
297 if( Thread != Proc_GetCurThread() )
299 SHORTLOCK( &glThreadListLock );
301 #if SCHEDULER_TYPE == SCHED_LOTTERY
302 giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri];
303 # if DEBUG_TRACE_TICKETS
304 Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]",
306 caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]);
309 Thread->Priority = Pri;
310 SHORTREL( &glThreadListLock );
313 Thread->Priority = Pri;
316 #if DEBUG_TRACE_STATE
317 Log("Threads_SetPriority: %p(%i %s) pri set %i",
318 Thread, Thread->TID, Thread->ThreadName,
324 * \brief Clone the TCB of the current thread
325 * \param Flags Flags for something... (What is this for?)
327 tThread *Threads_CloneTCB(Uint Flags)
330 cur = Proc_GetCurThread();
332 // Allocate and duplicate
333 new = malloc(sizeof(tThread));
334 if(new == NULL) { errno = -ENOMEM; return NULL; }
335 memcpy(new, cur, sizeof(tThread));
339 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
340 new->Status = THREAD_STAT_PREINIT;
344 new->TID = giNextTID++;
346 new->bInstrTrace = 0;
349 new->ThreadName = strdup(cur->ThreadName);
351 // Set Thread Group ID (PID)
352 if(Flags & CLONE_VM) {
353 tProcess *newproc, *oldproc;
354 oldproc = cur->Process;
355 new->Process = malloc( sizeof(struct sProcess) );
356 newproc = new->Process;
357 newproc->PID = new->TID;
358 if( Flags & CLONE_PGID )
359 newproc->PGID = oldproc->PGID;
361 newproc->PGID = newproc->PID;
362 newproc->UID = oldproc->UID;
363 newproc->GID = oldproc->GID;
364 newproc->MaxFD = oldproc->MaxFD;
365 newproc->CurrentWorkingDir = oldproc->CurrentWorkingDir ? strdup( oldproc->CurrentWorkingDir ) : NULL;
366 newproc->RootDir = oldproc->RootDir ? strdup( oldproc->RootDir ) : NULL;
367 newproc->nThreads = 1;
368 // Reference all handles in the VFS
369 VFS_ReferenceUserHandles();
371 // Add to global list
372 newproc->Prev = NULL;
374 newproc->Next = gAllProcesses;
375 gAllProcesses = newproc;
377 newproc->FirstThread = new;
378 new->ProcessNext = NULL;
381 new->Process->nThreads ++;
382 new->Process = cur->Process;
384 new->ProcessNext = new->Process->FirstThread;
385 new->Process->FirstThread = new;
388 // Messages are not inherited
389 new->Messages = NULL;
390 new->LastMessage = NULL;
393 new->Remaining = new->Quantum = cur->Quantum;
394 new->Priority = cur->Priority;
397 // Set Signal Handlers
398 new->CurFaultNum = 0;
399 new->FaultHandler = cur->FaultHandler;
401 // Maintain a global list of threads
402 SHORTLOCK( &glThreadListLock );
403 new->GlobalPrev = NULL; // Protect against bugs
404 new->GlobalNext = gAllThreads;
405 gAllThreads->GlobalPrev = new;
407 SHORTREL( &glThreadListLock );
413 * \brief Clone the TCB of the kernel thread
415 tThread *Threads_CloneThreadZero(void)
419 // Allocate and duplicate
420 new = malloc(sizeof(tThread));
424 memcpy(new, &gThreadZero, sizeof(tThread));
426 new->Process->nThreads ++;
430 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
431 new->Status = THREAD_STAT_PREINIT;
435 new->TID = giNextTID++;
439 new->ThreadName = NULL;
441 // Messages are not inherited
442 new->Messages = NULL;
443 new->LastMessage = NULL;
446 new->Remaining = new->Quantum = DEFAULT_QUANTUM;
447 new->Priority = DEFAULT_PRIORITY;
448 new->bInstrTrace = 0;
450 // Set Signal Handlers
451 new->CurFaultNum = 0;
452 new->FaultHandler = 0;
454 // Maintain a global list of threads
455 SHORTLOCK( &glThreadListLock );
456 new->GlobalPrev = NULL; // Protect against bugs
457 new->GlobalNext = gAllThreads;
458 gAllThreads->GlobalPrev = new;
460 SHORTREL( &glThreadListLock );
466 * \brief Wait for a task to change state
467 * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
468 * \param Status Thread return status
469 * \return TID of child that changed state
471 tTID Threads_WaitTID(int TID, int *Status)
476 Uint32 ev = Threads_WaitEvents(THREAD_EVENT_DEADCHILD);
478 if( ev & THREAD_EVENT_DEADCHILD )
480 // A child died, get the TID
481 tThread *us = Proc_GetCurThread();
482 ASSERT(us->LastDeadChild);
483 ret = us->LastDeadChild->TID;
484 // - Mark as dead (as opposed to undead)
485 ASSERT(us->LastDeadChild->Status == THREAD_STAT_ZOMBIE);
486 us->LastDeadChild->Status = THREAD_STAT_DEAD;
487 // - Set return status
489 *Status = us->LastDeadChild->RetStatus;
490 us->LastDeadChild = NULL;
491 Mutex_Release(&us->DeadChildLock);
495 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
500 // Any peer/child thread
502 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
508 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
515 // TODO: Register on thread to be poked when it dies
517 // NOTE: Race condition - Other child dies, desired child dies, first death is 'lost'
518 while( (ret = Threads_WaitTID(-1, Status)) != TID )
530 * \brief Gets a thread given its TID
531 * \param TID Thread ID
532 * \return Thread pointer
534 tThread *Threads_GetThread(Uint TID)
538 // Search global list
539 for( thread = gAllThreads; thread; thread = thread->GlobalNext )
541 if(thread->TID == TID)
545 Log_Notice("Threads", "Unable to find TID %i on main list\n", TID);
551 * \brief Deletes an entry from a list
552 * \param List Pointer to the list head
553 * \param Thread Thread to find
556 tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread)
558 tThread *ret, *prev = NULL;
560 for(ret = List->Head;
561 ret && ret != Thread;
562 prev = ret, ret = ret->Next
565 // Is the thread on the list
567 //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
572 List->Head = Thread->Next;
573 //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
576 prev->Next = Thread->Next;
577 //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
579 if( Thread->Next == NULL )
585 void Threads_int_AddToList(tThreadList *List, tThread *Thread)
588 List->Tail->Next = Thread;
596 * \brief Exit the current process (or another?)
597 * \param TID Thread ID to kill
598 * \param Status Exit status
600 void Threads_Exit(int TID, int Status)
603 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
605 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
607 // Halt forever, just in case
612 * \fn void Threads_Kill(tThread *Thread, int Status)
613 * \brief Kill a thread
614 * \param Thread Thread to kill
615 * \param Status Status code to return to the parent
617 void Threads_Kill(tThread *Thread, int Status)
620 int isCurThread = Thread == Proc_GetCurThread();
622 // TODO: Disown all children?
626 // TODO: I should keep a .Children list
627 for(child = gAllThreads;
629 child = child->GlobalNext)
631 if(child->Parent == Thread)
632 child->Parent = &gThreadZero;
637 ///\note Double lock is needed due to overlap of lock areas
639 // Lock thread (stop us recieving messages)
640 SHORTLOCK( &Thread->IsLocked );
642 // Clear Message Queue
643 while( Thread->Messages )
645 msg = Thread->Messages->Next;
646 free( Thread->Messages );
647 Thread->Messages = msg;
651 SHORTLOCK( &glThreadListLock );
653 switch(Thread->Status)
655 case THREAD_STAT_PREINIT: // Only on main list
658 // Currently active thread
659 case THREAD_STAT_ACTIVE:
660 if( Thread != Proc_GetCurThread() )
662 #if SCHEDULER_TYPE == SCHED_RR_PRI
663 tThreadList *list = &gaActiveThreads[Thread->Priority];
665 tThreadList *list = &gActiveThreads;
667 if( Threads_int_DelFromQueue( list, Thread ) )
672 Log_Warning("Threads",
673 "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list",
674 Thread, Thread->TID, Thread->ThreadName
677 #if SCHEDULER_TYPE == SCHED_LOTTERY
678 giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ];
681 // Ensure that we are not rescheduled
682 Thread->Remaining = 0; // Clear Remaining Quantum
683 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
685 // Update bookkeeping
686 giNumActiveThreads --;
688 // Kill it while it sleeps!
689 case THREAD_STAT_SLEEPING:
690 if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) )
692 Log_Warning("Threads",
693 "Threads_Kill - Thread %p(%i,%s) marked as sleeping, but not on list",
694 Thread, Thread->TID, Thread->ThreadName
699 // Brains!... You cannot kill something that is already dead
700 case THREAD_STAT_ZOMBIE:
701 Log_Warning("Threads", "Threads_Kill - Thread %p(%i,%s) is undead, you cannot kill it",
702 Thread, Thread->TID, Thread->ThreadName);
703 SHORTREL( &glThreadListLock );
704 SHORTREL( &Thread->IsLocked );
708 Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)",
714 Thread->RetStatus = Status;
716 SHORTREL( &Thread->IsLocked );
718 Thread->Status = THREAD_STAT_ZOMBIE;
719 SHORTREL( &glThreadListLock );
720 // TODO: It's possible that we could be timer-preempted here, should disable that... somehow
721 Mutex_Acquire( &Thread->Parent->DeadChildLock ); // released by parent
722 Thread->Parent->LastDeadChild = Thread;
723 Threads_PostEvent( Thread->Parent, THREAD_EVENT_DEADCHILD );
725 Log("Thread %i went *hurk* (%i)", Thread->TID, Status);
736 * \brief Yield remainder of the current thread's timeslice
738 void Threads_Yield(void)
740 // Log("Threads_Yield: by %p", __builtin_return_address(0));
745 * \breif Wait for the thread status to not be a specified value
747 void Threads_int_WaitForStatusEnd(enum eThreadStatus Status)
749 tThread *us = Proc_GetCurThread();
750 ASSERT(Status != THREAD_STAT_ACTIVE);
751 ASSERT(Status != THREAD_STAT_DEAD);
752 while( us->Status == Status )
755 if( us->Status == Status )
756 Debug("Thread %p(%i %s) rescheduled while in %s state for %p",
757 us, us->TID, us->ThreadName,
758 casTHREAD_STAT[Status],
759 __builtin_return_address(0));
763 int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock)
765 SHORTLOCK( &glThreadListLock );
766 tThread *us = Threads_RemActive();
768 // - Mark as sleeping
770 us->WaitPointer = Ptr;
771 us->RetStatus = Num; // Use RetStatus as a temp variable
776 (*ListTail)->Next = us;
787 //if( Proc_ThreadSync(us) )
789 SHORTREL( &glThreadListLock );
792 Threads_int_WaitForStatusEnd(Status);
793 us->WaitPointer = NULL;
794 return us->RetStatus;
798 * \fn void Threads_Sleep(void)
799 * \brief Take the current process off the run queue
801 void Threads_Sleep(void)
803 tThread *cur = Proc_GetCurThread();
806 SHORTLOCK( &glThreadListLock );
808 // Don't sleep if there is a message waiting
809 if( cur->Messages ) {
810 SHORTREL( &glThreadListLock );
814 // Remove us from running queue
816 // Mark thread as sleeping
817 cur->Status = THREAD_STAT_SLEEPING;
819 // Add to Sleeping List (at the top)
820 Threads_int_AddToList( &gSleepingThreads, cur );
822 #if DEBUG_TRACE_STATE
823 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
827 SHORTREL( &glThreadListLock );
828 Threads_int_WaitForStatusEnd(THREAD_STAT_SLEEPING);
833 * \brief Wakes a sleeping/waiting thread up
834 * \param Thread Thread to wake
835 * \return Boolean Failure (Returns ERRNO)
836 * \warning This should ONLY be called with task switches disabled
838 int Threads_Wake(tThread *Thread)
843 switch(Thread->Status)
845 case THREAD_STAT_ACTIVE:
846 Log("Threads_Wake - Waking awake thread (%i)", Thread->TID);
849 case THREAD_STAT_SLEEPING:
850 // Remove from sleeping queue
851 SHORTLOCK( &glThreadListLock );
852 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
853 SHORTREL( &glThreadListLock );
855 Threads_AddActive( Thread );
857 #if DEBUG_TRACE_STATE
858 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
862 case THREAD_STAT_SEMAPHORESLEEP: {
864 tThread *th, *prev=NULL;
866 sem = Thread->WaitPointer;
868 SHORTLOCK( &sem->Protector );
870 // Remove from sleeping queue
871 for( th = sem->Waiting; th; prev = th, th = th->Next )
872 if( th == Thread ) break;
876 prev->Next = Thread->Next;
878 sem->Waiting = Thread->Next;
879 if(sem->LastWaiting == Thread)
880 sem->LastWaiting = prev;
885 for( th = sem->Signaling; th; prev = th, th = th->Next )
886 if( th == Thread ) break;
888 Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)",
889 Thread, Thread->TID, Thread->ThreadName,
890 sem, sem->ModName, sem->Name);
895 prev->Next = Thread->Next;
897 sem->Signaling = Thread->Next;
898 if(sem->LastSignaling == Thread)
899 sem->LastSignaling = prev;
902 Thread->RetStatus = 0; // It didn't get anything
903 Threads_AddActive( Thread );
905 #if DEBUG_TRACE_STATE
906 Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName);
908 SHORTREL( &sem->Protector );
911 case THREAD_STAT_WAITING:
912 Warning("Threads_Wake - Waiting threads are not currently supported");
915 case THREAD_STAT_DEAD:
916 Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID);
920 Log_Warning("Threads", "Threads_Wake - Unknown process status (%i)", Thread->Status);
926 * \brief Wake a thread given the TID
927 * \param TID Thread ID to wake
928 * \return Boolean Faulure (errno)
930 int Threads_WakeTID(tTID TID)
932 tThread *thread = Threads_GetThread(TID);
936 ret = Threads_Wake( thread );
937 //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
941 void Threads_ToggleTrace(int TID)
943 tThread *thread = Threads_GetThread(TID);
945 thread->bInstrTrace = !thread->bInstrTrace;
949 * \brief Adds a thread to the active queue
951 void Threads_AddActive(tThread *Thread)
953 #if DEBUG_TRACE_ACTIVEQUEUE
954 Debug("Threads_AddActive("PRIthread_fmt")", PRIthread_args(Thread));
956 SHORTLOCK( &glThreadListLock );
958 if( Thread->Status == THREAD_STAT_ACTIVE )
960 tThread *cur = Proc_GetCurThread();
961 Log_KernelPanic("Threads",
962 "ret=%p CPU%i "PRIthread_fmt" is adding "PRIthread_fmt" when it is active",
963 __builtin_return_address(0),
964 GetCPUNum(), PRIthread_args(cur), PRIthread_args(Thread));
965 SHORTREL( &glThreadListLock );
970 Thread->Status = THREAD_STAT_ACTIVE;
971 // Add to active list
972 // - Thread can be the current thread if we're interrupted just before
973 // Proc_Reschedule in a sleep state.
974 if( Thread != Proc_GetCurThread() )
976 #if SCHEDULER_TYPE == SCHED_RR_PRI
977 tThreadList *list = &gaActiveThreads[Thread->Priority];
979 tThreadList *list = &gActiveThreads;
981 #if DEBUG_TRACE_ACTIVEQUEUE
982 Debug(" - Head="PRIthread_fmt",Tail="PRIthread_fmt"",
983 PRIthread_args(list->Head),
984 PRIthread_args(list->Tail)
987 Threads_int_AddToList( list, Thread );
990 // Update bookkeeping
991 giNumActiveThreads ++;
993 #if SCHEDULER_TYPE == SCHED_LOTTERY
996 // Only change the ticket count if the thread is un-scheduled
997 if(Thread->CurCPU != -1)
1000 delta = caiTICKET_COUNTS[ Thread->Priority ];
1002 giFreeTickets += delta;
1003 # if DEBUG_TRACE_TICKETS
1004 Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]",
1005 GetCPUNum(), Thread, Thread->TID, Thread->ThreadName,
1006 giFreeTickets, delta
1012 SHORTREL( &glThreadListLock );
1016 * \brief Removes the current thread from the active queue
1017 * \warning This should ONLY be called with the lock held
1018 * \return Current thread pointer
1020 tThread *Threads_RemActive(void)
1022 tThread *us = Proc_GetCurThread();
1023 #if DEBUG_TRACE_ACTIVEQUEUE
1024 Debug("Threads_RemActive(%p(%i %s))", us, us->TID, us->ThreadName);
1026 giNumActiveThreads --;
1031 * \fn void Threads_SetFaultHandler(Uint Handler)
1032 * \brief Sets the signal handler for a signal
1034 void Threads_SetFaultHandler(Uint Handler)
1036 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
1037 Proc_GetCurThread()->FaultHandler = Handler;
1041 * \fn void Threads_Fault(int Num)
1042 * \brief Calls a fault handler
1044 void Threads_Fault(int Num)
1046 tThread *thread = Proc_GetCurThread();
1048 if(!thread) return ;
1050 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
1052 switch(thread->FaultHandler)
1055 Threads_Kill(thread, -1);
1058 case 1: // Dump Core?
1059 Threads_Kill(thread, -1);
1064 // Double Fault? Oh, F**k
1065 if(thread->CurFaultNum != 0) {
1066 Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID);
1067 Threads_Kill(thread, -1); // For now, just kill
1071 thread->CurFaultNum = Num;
1073 Proc_CallFaultHandler(thread);
1077 * \fn void Threads_SegFault(tVAddr Addr)
1078 * \brief Called when a Segment Fault occurs
1080 void Threads_SegFault(tVAddr Addr)
1082 tThread *cur = Proc_GetCurThread();
1083 cur->bInstrTrace = 0;
1084 Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr);
1085 MM_DumpTables(0, USER_MAX);
1087 //Threads_Exit( 0, -1 );
1091 void Threads_PostSignalTo(tThread *Thread, int SignalNum)
1094 Log_Debug("Threads", "Signalling %i(%s) with %i", Thread->TID, Thread->ThreadName, SignalNum);
1095 Thread->PendingSignal = SignalNum;
1096 Threads_PostEvent(Thread, THREAD_EVENT_SIGNAL);
1098 void Threads_PostSignal(int SignalNum)
1100 Threads_PostSignalTo( Proc_GetCurThread(), SignalNum );
1103 void Threads_SignalGroup(tPGID PGID, int Signal)
1105 for( tProcess *proc = gAllProcesses; proc; proc = proc->Next )
1107 if(proc->PGID == PGID)
1109 Threads_PostSignalTo(proc->FirstThread, Signal);
1116 int Threads_GetPendingSignal(void)
1118 tThread *cur = Proc_GetCurThread();
1120 // Atomic AND with 0 fetches and clears in one operation
1121 int ret = __sync_fetch_and_and( &cur->PendingSignal, 0 );
1124 Log_Debug("Threads", "Thread %i(%s) has signal %i pending",
1125 cur->TID, cur->ThreadName, ret);
1131 * \brief Update the current thread's signal handler
1133 void Threads_SetSignalHandler(int SignalNum, void *Handler)
1135 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1137 if( !MM_IsUser(Handler) )
1139 Proc_GetCurThread()->Process->SignalHandlers[SignalNum] = Handler;
1143 * \brief Gets the registered (or default, if none set) handler for a signal.
1144 * \return Handler function pointer, OR NULL if no signal to be ignored
1146 void *Threads_GetSignalHandler(int SignalNum)
1149 void *User_Signal_Core = User_Signal_Kill;
1151 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1153 void *ret = Proc_GetCurThread()->Process->SignalHandlers[SignalNum];
1154 if( !ret || (SignalNum == SIGKILL || SignalNum == SIGSTOP) )
1161 ret = User_Signal_Kill;
1167 ret = User_Signal_Core;
1170 ret = User_Signal_Kill;
1173 ret = User_Signal_Core;
1178 ret = User_Signal_Kill;
1185 Log_Debug("Threads", "Handler %p for signal %i", ret, SignalNum);
1189 // --- Process Structure Access Functions ---
1190 tPGID Threads_GetPGID(void)
1192 return Proc_GetCurThread()->Process->PGID;
1194 tPID Threads_GetPID(void)
1196 return Proc_GetCurThread()->Process->PID;
1198 tTID Threads_GetTID(void)
1200 return Proc_GetCurThread()->TID;
1202 tUID Threads_GetUID(void)
1204 return Proc_GetCurThread()->Process->UID;
1206 tGID Threads_GetGID(void)
1208 return Proc_GetCurThread()->Process->GID;
1211 int Threads_SetUID(tUID ID)
1213 tThread *t = Proc_GetCurThread();
1214 if( t->Process->UID != 0 ) {
1218 Log_Debug("Threads", "PID %i's UID set to %i", t->Process->PID, ID);
1219 t->Process->UID = ID;
1223 int Threads_SetGID(tGID ID)
1225 tThread *t = Proc_GetCurThread();
1226 if( t->Process->UID != 0 ) {
1230 Log_Debug("Threads", "PID %i's GID set to %i", t->Process->PID, ID);
1231 t->Process->GID = ID;
1235 // --- Per-thread storage ---
1236 int *Threads_GetErrno(void)
1238 return &Proc_GetCurThread()->_errno;
1241 // --- Configuration ---
1242 int *Threads_GetMaxFD(void)
1244 return &Proc_GetCurThread()->Process->MaxFD;
1246 char **Threads_GetChroot(void)
1248 return &Proc_GetCurThread()->Process->RootDir;
1250 char **Threads_GetCWD(void)
1252 return &Proc_GetCurThread()->Process->CurrentWorkingDir;
1256 void Threads_int_DumpThread(tThread *thread)
1259 Log(" %p NULL", thread);
1262 if( !CheckMem(thread, sizeof(tThread)) ) {
1263 Log(" %p INVAL", thread);
1266 tPID pid = (thread->Process ? thread->Process->PID : -1);
1267 const char *statstr = (thread->Status < sizeof(casTHREAD_STAT)/sizeof(casTHREAD_STAT[0])
1268 ? casTHREAD_STAT[thread->Status] : "");
1269 Log(" %p %i (%i) - %s (CPU %i) - %i (%s)",
1270 thread, thread->TID, pid, thread->ThreadName, thread->CurCPU,
1271 thread->Status, statstr
1273 switch(thread->Status)
1275 case THREAD_STAT_MUTEXSLEEP:
1276 Log(" Mutex Pointer: %p", thread->WaitPointer);
1278 case THREAD_STAT_RWLOCKSLEEP:
1279 Log(" Lock Pointer: %p", thread->WaitPointer);
1280 Log(" Lock Name: %s",
1281 ((tRWLock*)thread->WaitPointer)->Name);
1283 case THREAD_STAT_SEMAPHORESLEEP:
1284 Log(" Semaphore Pointer: %p", thread->WaitPointer);
1285 Log(" Semaphore Name: %s:%s",
1286 ((tSemaphore*)thread->WaitPointer)->ModName,
1287 ((tSemaphore*)thread->WaitPointer)->Name
1290 case THREAD_STAT_EVENTSLEEP:
1291 Log(" Event Mask: %x", thread->RetStatus);
1293 case THREAD_STAT_ZOMBIE:
1294 Log(" Return Status: %i", thread->RetStatus);
1298 Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
1299 Log(" KStack %p", thread->KernelStack);
1300 if( thread->bInstrTrace )
1301 Log(" Tracing Enabled");
1302 Proc_DumpThreadCPUState(thread);
1306 * \fn void Threads_Dump(void)
1308 void Threads_DumpActive(void)
1312 #if SCHEDULER_TYPE == SCHED_RR_PRI
1316 Log("Active Threads: (%i reported)", giNumActiveThreads);
1318 #if SCHEDULER_TYPE == SCHED_RR_PRI
1319 for( i = 0; i < MIN_PRIORITY+1; i++ )
1321 list = &gaActiveThreads[i];
1323 list = &gActiveThreads;
1325 for(thread=list->Head;thread;thread=thread->Next)
1327 Threads_int_DumpThread(thread);
1328 if(thread->Status != THREAD_STAT_ACTIVE)
1329 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)",
1330 thread->Status, THREAD_STAT_ACTIVE);
1333 #if SCHEDULER_TYPE == SCHED_RR_PRI
1339 * \fn void Threads_Dump(void)
1340 * \brief Dumps a list of currently running threads
1342 void Threads_Dump(void)
1344 Log("--- Thread Dump ---");
1345 Threads_DumpActive();
1347 Log("All Threads:");
1348 for(tThread *thread = gAllThreads; thread; thread = thread->GlobalNext)
1350 Threads_int_DumpThread(thread);
1354 tThread *Threads_int_GetRunnable(void)
1356 #if SCHEDULER_TYPE == SCHED_LOTTERY
1357 // -----------------------------------
1358 // Lottery Scheduler
1359 // -----------------------------------
1360 #if DEBUG_VALIDATE_TICKET_COUNTS
1362 int total_tickets = 0;
1363 for(const tThread *thread = gActiveThreads.Head; thread; thread = thread->Next)
1365 if(thread->Status != THREAD_STAT_ACTIVE)
1366 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
1367 thread, thread->TID, thread->ThreadName, thread->Status);
1368 if(thread->Next == thread) {
1369 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
1370 thread, thread->TID, thread->ThreadName, thread->Status);
1372 total_tickets += caiTICKET_COUNTS[ thread->Priority ];
1374 if(total_tickets != giFreeTickets)
1376 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
1377 giFreeTickets, total_tickets, CPU);
1382 // No free tickets (all tasks delegated to cores)
1383 if( giFreeTickets == 0 )
1388 // Get the ticket number
1389 int ticket = rand() % giFreeTickets;
1390 int number = ticket;
1392 // Find the next thread
1393 tThread **pnp = &gActiveThreads.Head;
1395 for(thread = gActiveThreads.Head; thread; pnp = &thread->Next, thread = thread->Next )
1397 if( caiTICKET_COUNTS[ thread->Priority ] > number)
1399 number -= caiTICKET_COUNTS[ thread->Priority ];
1402 // If we didn't find a thread, something went wrong
1405 int total_tickets = 0;
1406 for(thread=gActiveThreads;thread;thread=thread->Next) {
1407 if(thread->CurCPU >= 0) continue;
1408 total_tickets += caiTICKET_COUNTS[ thread->Priority ];
1410 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
1411 giFreeTickets, total_tickets);
1415 *pnp = thread->Next;
1417 gActiveThreads.Tail = prev;
1419 giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
1420 # if DEBUG_TRACE_TICKETS
1421 LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n",
1422 CPU, thread, thread->TID, thread->ThreadName,
1423 giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]);
1427 #elif SCHEDULER_TYPE == SCHED_RR_PRI
1429 // -----------------------------------
1430 // Priority based round robin scheduler
1431 // -----------------------------------
1432 for( int i = 0; i < MIN_PRIORITY + 1; i ++ )
1434 if( gaActiveThreads[i].Head == NULL )
1437 tThread *thread = gaActiveThreads[i].Head;
1439 if( thread->Status != THREAD_STAT_ACTIVE ) {
1440 for( const tThread *t = gaActiveThreads[i].Head; t; t = t->Next )
1441 LogF("- %p(%i %s)\n", t, t->TID, t->ThreadName);
1442 Panic("Thread %p(%i %s) from pqueue %i is active!",
1443 thread, thread->TID, thread->ThreadName, i);
1447 gaActiveThreads[i].Head = thread->Next;
1449 gaActiveThreads[i].Tail = NULL;
1450 thread->Next = NULL;
1455 #elif SCHEDULER_TYPE == SCHED_RR_SIM
1457 // -----------------------------------
1458 // Single-list round-robin
1459 // -----------------------------------
1460 tThread *thread = gActiveThreads.Head;
1463 gActiveThreads.Head = thread->Next;
1465 gaActiveThreads.Tail = NULL;
1466 thread->Next = NULL;
1470 # error "Unimplemented scheduling algorithm"
1476 * \brief Gets the next thread to run
1477 * \param CPU Current CPU
1478 * \param Last The thread the CPU was running
1480 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
1482 // If this CPU has the lock, we must let it complete
1483 if( CPU_HAS_LOCK( &glThreadListLock ) )
1486 // Don't change threads if the current CPU has switches disabled
1487 if( gaThreads_NoTaskSwitch[CPU] )
1491 SHORTLOCK( &glThreadListLock );
1493 // Make sure the current (well, old) thread is marked as de-scheduled
1494 if(Last) Last->CurCPU = -1;
1496 // No active threads, just take a nap
1497 if(giNumActiveThreads == 0) {
1498 SHORTREL( &glThreadListLock );
1499 #if DEBUG_TRACE_TICKETS
1500 Log("No active threads");
1505 // Allow the old thread to be scheduled again
1508 if( Last->Status == THREAD_STAT_ACTIVE ) {
1510 #if SCHEDULER_TYPE == SCHED_LOTTERY
1511 giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
1512 # if DEBUG_TRACE_TICKETS
1513 LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n",
1514 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets,
1515 caiTICKET_COUNTS[ Last->Priority ]);
1519 #if SCHEDULER_TYPE == SCHED_RR_PRI
1520 list = &gaActiveThreads[ Last->Priority ];
1522 list = &gActiveThreads;
1524 // Add to end of list
1525 Threads_int_AddToList( list, Last );
1526 #if DEBUG_TRACE_ACTIVEQUEUE > 1
1527 Debug("Threads_GetNextToRun: Append thread %p(%i %s)",
1528 Last, Last->TID, Last->ThreadName);
1531 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
1533 LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n",
1534 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
1539 // Call actual scheduler
1540 tThread *thread = Threads_int_GetRunnable();
1545 if( thread->Status != THREAD_STAT_ACTIVE )
1547 LogF("Thread %p(%i %s) scheduled while not active\n",
1548 thread, thread->TID, thread->ThreadName);
1551 // Make the new thread non-schedulable
1552 thread->CurCPU = CPU;
1553 thread->Remaining = thread->Quantum;
1555 #if DEBUG_TRACE_SCHEDULE
1556 Debug("Scheduled "PRIthread_fmt", next = %p",
1557 PRIthread_args(thread),
1563 // No thread possible, warning condition (idle thread should be runnable)
1564 Warning("No runnable thread for CPU%i", CPU);
1567 SHORTREL( &glThreadListLock );
1573 EXPORT(Threads_GetUID);
1574 EXPORT(Threads_GetGID);