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
16 #include <debug_hooks.h>
19 #define DEBUG_TRACE_ACTIVEQUEUE 0 // Trace adds/removals from the active queue
20 #define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
21 #define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
22 #define DEBUG_TRACE_SCHEDULE 0 // Trace scheduling
26 #define SCHED_LOTTERY 1 // Lottery scheduler
27 #define SCHED_RR_SIM 2 // Single Queue Round Robin
28 #define SCHED_RR_PRI 3 // Multi Queue Round Robin
30 #define SCHEDULER_TYPE SCHED_RR_PRI
33 #define DEFAULT_QUANTUM 5
34 #define DEFAULT_PRIORITY 5
35 #define MIN_PRIORITY 10
37 #define PRIthread_fmt "%p(%i %s)"
38 #define PRIthread_args(t) (t), ((t)?(t)->TID:-1), ((t)?(t)->ThreadName:"-")
41 extern void User_Signal_Kill(int SigNum);
51 void Threads_Init(void);
53 void Threads_Delete(tThread *Thread);
54 int Threads_SetName(const char *NewName);
56 char *Threads_GetName(tTID ID);
58 void Threads_SetPriority(tThread *Thread, int Pri);
59 tThread *Threads_CloneTCB(Uint Flags);
60 int Threads_WaitTID(int TID, int *status);
61 tThread *Threads_GetThread(Uint TID);
63 tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread);
64 void Threads_int_AddToList(tThreadList *List, tThread *Thread);
66 void Threads_Exit(int TID, int Status);
67 void Threads_Kill(tThread *Thread, int Status);
68 void Threads_Yield(void);
69 int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock);
70 void Threads_Sleep(void);
71 int Threads_Wake(tThread *Thread);
72 void Threads_AddActive(tThread *Thread);
73 tThread *Threads_RemActive(void);
74 void Threads_ToggleTrace(int TID);
76 void Threads_Fault(int Num);
77 void Threads_SegFault(tVAddr Addr);
78 void Threads_PostSignalTo(tThread *Thread, int SignalNum);
80 void Threads_PostSignal(int SignalNum);
81 void Threads_SignalGroup(tPGID PGID, int SignalNum);
83 int Threads_GetPendingSignal(void);
84 void Threads_SetSignalHandler(int SignalNum, void *Handler);
85 void *Threads_GetSignalHandler(int SignalNum);
87 int Threads_GetPID(void);
88 int Threads_GetTID(void);
89 tUID Threads_GetUID(void);
90 tGID Threads_GetGID(void);
91 int Threads_SetUID(Uint *Errno, tUID ID);
92 int Threads_SetGID(Uint *Errno, tUID ID);
94 void Threads_int_DumpThread(tThread *thread);
96 void Threads_Dump(void);
98 void Threads_DumpActive(void);
99 tThread *Threads_int_GetRunnable(void);
103 tProcess gProcessZero = {
105 // Only used for the core kernel
106 tThread gThreadZero = {
107 .Status = THREAD_STAT_ACTIVE, // Status
108 .ThreadName = (char*)"ThreadZero", // Name
109 .Quantum = DEFAULT_QUANTUM, // Default Quantum
110 .Remaining = DEFAULT_QUANTUM, // Current Quantum
111 .Priority = DEFAULT_PRIORITY // Number of tickets
114 tProcess *gAllProcesses = &gProcessZero;
116 tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
117 // --- Current State ---
118 volatile int giNumActiveThreads = 0; // Number of threads on the active queue
119 volatile Uint giNextTID = 1; // Next TID to allocate
120 // --- Thread Lists ---
121 tThread *gAllThreads = NULL; // All allocated threads
122 tThreadList gSleepingThreads; // Sleeping Threads
123 int giNumCPUs = 1; // Number of CPUs
124 BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF)
125 // --- Scheduler Types ---
126 #if SCHEDULER_TYPE == SCHED_LOTTERY
127 const int caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0};
128 volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads
129 tThreadList gActiveThreads; // Currently Running Threads
130 #elif SCHEDULER_TYPE == SCHED_RR_SIM
131 tThreadList gActiveThreads; // Currently Running Threads
132 #elif SCHEDULER_TYPE == SCHED_RR_PRI
133 tThreadList gaActiveThreads[MIN_PRIORITY+1]; // Active threads for each priority level
135 # error "Unkown scheduler type"
140 * \fn void Threads_Init(void)
141 * \brief Initialse the thread list
143 void Threads_Init(void)
147 Log_Debug("Threads", "Offsets of tThread");
148 Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority));
149 Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack));
151 // Create Initial Task
152 gAllThreads = &gThreadZero;
153 giNumActiveThreads = 1;
154 gThreadZero.Process = &gProcessZero;
159 void Threads_Delete(tThread *Thread)
162 Thread->Status = THREAD_STAT_BURIED;
164 // Clear out process state
165 Proc_ClearThread(Thread);
167 Thread->Process->nThreads --;
169 if( Thread->Process->FirstThread == Thread )
171 Thread->Process->FirstThread = Thread->ProcessNext;
175 tThread *prev = Thread->Process->FirstThread;
176 while(prev && prev->ProcessNext != Thread)
177 prev = prev->ProcessNext;
179 Log_Error("Threads", "Thread %p(%i %s) is not on the process's list",
180 Thread, Thread->TID, Thread->ThreadName
183 prev->ProcessNext = Thread->ProcessNext;
186 // If the final thread is being terminated, clean up the process
187 if( Thread->Process->nThreads == 0 )
189 tProcess *proc = Thread->Process;
191 // Remove from global process list
194 proc->Prev->Next = proc->Next;
196 gAllProcesses = proc->Next;
198 proc->Next->Prev = proc->Prev;
201 VFS_CloseAllUserHandles( proc );
202 // Architecture cleanup
203 Proc_ClearProcess( proc );
204 // VFS Configuration strings
205 if( proc->CurrentWorkingDir)
206 free( proc->CurrentWorkingDir );
208 free( proc->RootDir );
209 // Process descriptor
214 if( IsHeap(Thread->ThreadName) )
215 free(Thread->ThreadName);
217 // Remove from global list
218 // TODO: Lock this too
219 if( Thread == gAllThreads )
220 gAllThreads = Thread->GlobalNext;
222 Thread->GlobalPrev->GlobalNext = Thread->GlobalNext;
228 * \fn void Threads_SetName(const char *NewName)
229 * \brief Sets the current thread's name
230 * \param NewName New name for the thread
231 * \return Boolean Failure
233 int Threads_SetName(const char *NewName)
235 tThread *cur = Proc_GetCurThread();
236 char *oldname = cur->ThreadName;
238 // NOTE: There is a possibility of non-thread safety here
239 // A thread could read the current name pointer before it is zeroed
241 cur->ThreadName = NULL;
243 if( IsHeap(oldname) ) free( oldname );
244 cur->ThreadName = strdup(NewName);
246 Log_Debug("Threads", "Thread renamed to '%s'", NewName);
252 * \fn char *Threads_GetName(int ID)
253 * \brief Gets a thread's name
254 * \param ID Thread ID (-1 indicates current thread)
255 * \return Pointer to name
256 * \retval NULL Failure
258 char *Threads_GetName(tTID ID)
261 return Proc_GetCurThread()->ThreadName;
263 return Threads_GetThread(ID)->ThreadName;
267 * \fn void Threads_SetPriority(tThread *Thread, int Pri)
268 * \brief Sets the priority of a task
269 * \param Thread Thread to update ticket count (NULL means current thread)
270 * \param Pri New priority
272 void Threads_SetPriority(tThread *Thread, int Pri)
274 // Get current thread
275 if(Thread == NULL) Thread = Proc_GetCurThread();
277 // - If < 0, set to lowest priority
278 // - Minumum priority is actualy a high number, 0 is highest
279 if(Pri < 0) Pri = MIN_PRIORITY;
280 if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY;
282 // Do we actually have to do anything?
283 if( Pri == Thread->Priority ) return;
285 #if SCHEDULER_TYPE == SCHED_RR_PRI
286 if( Thread != Proc_GetCurThread() )
288 SHORTLOCK( &glThreadListLock );
289 // Remove from old priority
290 Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread );
292 Threads_int_AddToList( &gaActiveThreads[Pri], Thread );
293 Thread->Priority = Pri;
294 SHORTREL( &glThreadListLock );
297 Thread->Priority = Pri;
299 // If this isn't the current thread, we need to lock
300 if( Thread != Proc_GetCurThread() )
302 SHORTLOCK( &glThreadListLock );
304 #if SCHEDULER_TYPE == SCHED_LOTTERY
305 giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri];
306 # if DEBUG_TRACE_TICKETS
307 Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]",
309 caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]);
312 Thread->Priority = Pri;
313 SHORTREL( &glThreadListLock );
316 Thread->Priority = Pri;
319 #if DEBUG_TRACE_STATE
320 Log("Threads_SetPriority: %p(%i %s) pri set %i",
321 Thread, Thread->TID, Thread->ThreadName,
327 * \brief Clone the TCB of the current thread
328 * \param Flags Flags for something... (What is this for?)
330 tThread *Threads_CloneTCB(Uint Flags)
333 cur = Proc_GetCurThread();
335 // Allocate and duplicate
336 new = malloc(sizeof(tThread));
337 if(new == NULL) { errno = -ENOMEM; return NULL; }
338 memcpy(new, cur, sizeof(tThread));
342 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
343 new->Status = THREAD_STAT_PREINIT;
347 new->TID = giNextTID++;
349 new->bInstrTrace = 0;
352 new->ThreadName = strdup(cur->ThreadName);
354 // Set Thread Group ID (PID)
355 if(Flags & CLONE_VM) {
356 tProcess *newproc, *oldproc;
357 oldproc = cur->Process;
358 new->Process = calloc( sizeof(struct sProcess), 1 );
359 newproc = new->Process;
360 newproc->PID = new->TID;
361 if( Flags & CLONE_PGID )
362 newproc->PGID = oldproc->PGID;
364 newproc->PGID = newproc->PID;
365 newproc->UID = oldproc->UID;
366 newproc->GID = oldproc->GID;
367 newproc->MaxFD = oldproc->MaxFD;
368 newproc->CurrentWorkingDir = oldproc->CurrentWorkingDir ? strdup( oldproc->CurrentWorkingDir ) : NULL;
369 newproc->RootDir = oldproc->RootDir ? strdup( oldproc->RootDir ) : NULL;
370 newproc->nThreads = 1;
371 // Reference all handles in the VFS
372 VFS_ReferenceUserHandles();
374 // Add to global list
375 newproc->Prev = NULL;
377 newproc->Next = gAllProcesses;
378 gAllProcesses = newproc;
380 newproc->FirstThread = new;
381 new->ProcessNext = NULL;
384 new->Process->nThreads ++;
385 new->Process = cur->Process;
387 new->ProcessNext = new->Process->FirstThread;
388 new->Process->FirstThread = new;
391 // Messages are not inherited
392 new->Messages = NULL;
393 new->LastMessage = NULL;
396 new->Remaining = new->Quantum = cur->Quantum;
397 new->Priority = cur->Priority;
400 // Set Signal Handlers
401 new->CurFaultNum = 0;
402 new->FaultHandler = cur->FaultHandler;
404 // Maintain a global list of threads
405 SHORTLOCK( &glThreadListLock );
406 new->GlobalPrev = NULL; // Protect against bugs
407 new->GlobalNext = gAllThreads;
408 gAllThreads->GlobalPrev = new;
410 SHORTREL( &glThreadListLock );
416 * \brief Clone the TCB of the kernel thread
418 tThread *Threads_CloneThreadZero(void)
422 // Allocate and duplicate
423 new = malloc(sizeof(tThread));
427 memcpy(new, &gThreadZero, sizeof(tThread));
429 new->Process->nThreads ++;
433 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
434 new->Status = THREAD_STAT_PREINIT;
438 new->TID = giNextTID++;
442 new->ThreadName = NULL;
444 // Messages are not inherited
445 new->Messages = NULL;
446 new->LastMessage = NULL;
449 new->Remaining = new->Quantum = DEFAULT_QUANTUM;
450 new->Priority = DEFAULT_PRIORITY;
451 new->bInstrTrace = 0;
453 // Set Signal Handlers
454 new->CurFaultNum = 0;
455 new->FaultHandler = 0;
457 // Maintain a global list of threads
458 SHORTLOCK( &glThreadListLock );
459 new->GlobalPrev = NULL; // Protect against bugs
460 new->GlobalNext = gAllThreads;
461 gAllThreads->GlobalPrev = new;
463 SHORTREL( &glThreadListLock );
469 * \brief Wait for a task to change state
470 * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
471 * \param Status Thread return status
472 * \return TID of child that changed state
474 tTID Threads_WaitTID(int TID, int *Status)
479 Uint32 ev = Threads_WaitEvents(THREAD_EVENT_DEADCHILD);
481 if( ev & THREAD_EVENT_DEADCHILD )
483 tThread * const us = Proc_GetCurThread();
484 // A child died, get the TID
485 ASSERT(us->LastDeadChild);
486 tThread *dead_thread = us->LastDeadChild;
487 us->LastDeadChild = dead_thread->Next;
488 if( us->LastDeadChild )
489 Threads_PostEvent( us, THREAD_EVENT_DEADCHILD );
491 Threads_ClearEvent( THREAD_EVENT_DEADCHILD );
492 Mutex_Release(&us->DeadChildLock);
494 ret = dead_thread->TID;
495 // - Mark as dead (as opposed to undead)
496 ASSERTC(dead_thread->Status, ==, THREAD_STAT_ZOMBIE);
497 dead_thread->Status = THREAD_STAT_DEAD;
498 // - Set return status
500 *Status = dead_thread->RetStatus;
502 Threads_Delete( dead_thread );
506 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
511 // Any peer/child thread
513 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
519 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
526 // TODO: Register on thread to be poked when it dies
528 // NOTE: Race condition - Other child dies, desired child dies, first death is 'lost'
529 while( (ret = Threads_WaitTID(-1, Status)) != TID )
541 * \brief Gets a thread given its TID
542 * \param TID Thread ID
543 * \return Thread pointer
545 tThread *Threads_GetThread(Uint TID)
549 // Search global list
550 for( thread = gAllThreads; thread; thread = thread->GlobalNext )
552 if(thread->TID == TID)
556 Log_Notice("Threads", "Unable to find TID %i on main list\n", TID);
562 * \brief Deletes an entry from a list
563 * \param List Pointer to the list head
564 * \param Thread Thread to find
567 tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread)
569 tThread *ret, *prev = NULL;
571 for(ret = List->Head;
572 ret && ret != Thread;
573 prev = ret, ret = ret->Next
576 // Is the thread on the list
578 //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
583 List->Head = Thread->Next;
584 //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
587 prev->Next = Thread->Next;
588 //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
590 if( Thread->Next == NULL )
596 void Threads_int_AddToList(tThreadList *List, tThread *Thread)
599 List->Tail->Next = Thread;
607 * \brief Exit the current process (or another?)
608 * \param TID Thread ID to kill
609 * \param Status Exit status
611 void Threads_Exit(int TID, int Status)
614 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
616 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
618 // Halt forever, just in case
623 * \fn void Threads_Kill(tThread *Thread, int Status)
624 * \brief Kill a thread
625 * \param Thread Thread to kill
626 * \param Status Status code to return to the parent
628 void Threads_Kill(tThread *Thread, int Status)
631 int isCurThread = Thread == Proc_GetCurThread();
633 // TODO: Disown all children?
637 // TODO: I should keep a .Children list
638 for(child = gAllThreads;
640 child = child->GlobalNext)
642 if(child->Parent == Thread)
643 child->Parent = &gThreadZero;
648 ///\note Double lock is needed due to overlap of lock areas
650 // Lock thread (stop us recieving messages)
651 SHORTLOCK( &Thread->IsLocked );
653 // Clear Message Queue
654 while( Thread->Messages )
656 msg = Thread->Messages->Next;
657 free( Thread->Messages );
658 Thread->Messages = msg;
662 SHORTLOCK( &glThreadListLock );
664 switch(Thread->Status)
666 case THREAD_STAT_PREINIT: // Only on main list
669 // Currently active thread
670 case THREAD_STAT_ACTIVE:
671 if( Thread != Proc_GetCurThread() )
673 #if SCHEDULER_TYPE == SCHED_RR_PRI
674 tThreadList *list = &gaActiveThreads[Thread->Priority];
676 tThreadList *list = &gActiveThreads;
678 if( Threads_int_DelFromQueue( list, Thread ) )
683 Log_Warning("Threads",
684 "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list",
685 Thread, Thread->TID, Thread->ThreadName
688 #if SCHEDULER_TYPE == SCHED_LOTTERY
689 giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ];
692 // Ensure that we are not rescheduled
693 Thread->Remaining = 0; // Clear Remaining Quantum
694 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
696 // Update bookkeeping
697 giNumActiveThreads --;
699 // Kill it while it sleeps!
700 case THREAD_STAT_SLEEPING:
701 if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) )
703 Log_Warning("Threads",
704 "Threads_Kill - Thread %p(%i,%s) marked as sleeping, but not on list",
705 Thread, Thread->TID, Thread->ThreadName
710 // Brains!... You cannot kill something that is already dead
711 case THREAD_STAT_ZOMBIE:
712 Log_Warning("Threads", "Threads_Kill - Thread %p(%i,%s) is undead, you cannot kill it",
713 Thread, Thread->TID, Thread->ThreadName);
714 SHORTREL( &glThreadListLock );
715 SHORTREL( &Thread->IsLocked );
719 Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)",
725 Thread->RetStatus = Status;
727 SHORTREL( &Thread->IsLocked );
729 Thread->Status = THREAD_STAT_ZOMBIE;
730 SHORTREL( &glThreadListLock );
731 // TODO: It's possible that we could be timer-preempted here, should disable that... somehow
732 Mutex_Acquire( &Thread->Parent->DeadChildLock ); // released by parent
733 Thread->Next = Thread->Parent->LastDeadChild;
734 Thread->Parent->LastDeadChild = Thread;
735 Threads_PostEvent( Thread->Parent, THREAD_EVENT_DEADCHILD );
737 // Process cleanup happens on reaping
738 Log("Thread %i went *hurk* (%i)", Thread->TID, Status);
749 * \brief Yield remainder of the current thread's timeslice
751 void Threads_Yield(void)
753 // Log("Threads_Yield: by %p", __builtin_return_address(0));
758 * \breif Wait for the thread status to not be a specified value
760 void Threads_int_WaitForStatusEnd(enum eThreadStatus Status)
762 tThread *us = Proc_GetCurThread();
763 ASSERT(Status != THREAD_STAT_ACTIVE);
764 ASSERT(Status != THREAD_STAT_DEAD);
765 while( us->Status == Status )
768 if( us->Status == Status )
769 Debug("Thread %p(%i %s) rescheduled while in %s state for %p",
770 us, us->TID, us->ThreadName,
771 casTHREAD_STAT[Status],
772 __builtin_return_address(0));
776 int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock)
778 SHORTLOCK( &glThreadListLock );
779 tThread *us = Threads_RemActive();
781 // - Mark as sleeping
783 us->WaitPointer = Ptr;
784 us->RetStatus = Num; // Use RetStatus as a temp variable
789 (*ListTail)->Next = us;
796 else if( ListHead ) {
797 us->Next = *ListHead;
804 //if( Proc_ThreadSync(us) )
806 SHORTREL( &glThreadListLock );
809 Threads_int_WaitForStatusEnd(Status);
810 us->WaitPointer = NULL;
811 return us->RetStatus;
815 * \fn void Threads_Sleep(void)
816 * \brief Take the current process off the run queue
818 void Threads_Sleep(void)
820 tThread *cur = Proc_GetCurThread();
823 SHORTLOCK( &glThreadListLock );
825 // Don't sleep if there is a message waiting
826 if( cur->Messages ) {
827 SHORTREL( &glThreadListLock );
831 // Remove us from running queue
833 // Mark thread as sleeping
834 cur->Status = THREAD_STAT_SLEEPING;
836 // Add to Sleeping List (at the top)
837 Threads_int_AddToList( &gSleepingThreads, cur );
839 #if DEBUG_TRACE_STATE
840 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
844 SHORTREL( &glThreadListLock );
845 Threads_int_WaitForStatusEnd(THREAD_STAT_SLEEPING);
850 * \brief Wakes a sleeping/waiting thread up
851 * \param Thread Thread to wake
852 * \return Boolean Failure (Returns ERRNO)
853 * \warning This should ONLY be called with task switches disabled
855 int Threads_Wake(tThread *Thread)
860 switch(Thread->Status)
862 case THREAD_STAT_ACTIVE:
863 Log("Threads_Wake - Waking awake thread (%i)", Thread->TID);
866 case THREAD_STAT_SLEEPING:
867 // Remove from sleeping queue
868 SHORTLOCK( &glThreadListLock );
869 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
870 SHORTREL( &glThreadListLock );
872 Threads_AddActive( Thread );
874 #if DEBUG_TRACE_STATE
875 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
879 case THREAD_STAT_SEMAPHORESLEEP: {
881 tThread *th, *prev=NULL;
883 sem = Thread->WaitPointer;
885 SHORTLOCK( &sem->Protector );
887 // Remove from sleeping queue
888 for( th = sem->Waiting; th; prev = th, th = th->Next )
889 if( th == Thread ) break;
893 prev->Next = Thread->Next;
895 sem->Waiting = Thread->Next;
896 if(sem->LastWaiting == Thread)
897 sem->LastWaiting = prev;
902 for( th = sem->Signaling; th; prev = th, th = th->Next )
903 if( th == Thread ) break;
905 Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)",
906 Thread, Thread->TID, Thread->ThreadName,
907 sem, sem->ModName, sem->Name);
912 prev->Next = Thread->Next;
914 sem->Signaling = Thread->Next;
915 if(sem->LastSignaling == Thread)
916 sem->LastSignaling = prev;
919 Thread->RetStatus = 0; // It didn't get anything
920 Threads_AddActive( Thread );
922 #if DEBUG_TRACE_STATE
923 Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName);
925 SHORTREL( &sem->Protector );
928 case THREAD_STAT_WAITING:
929 Warning("Threads_Wake - Waiting threads are not currently supported");
932 case THREAD_STAT_DEAD:
933 Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID);
937 Log_Warning("Threads", "Threads_Wake - Unknown process status (%i)", Thread->Status);
943 * \brief Wake a thread given the TID
944 * \param TID Thread ID to wake
945 * \return Boolean Faulure (errno)
947 int Threads_WakeTID(tTID TID)
949 tThread *thread = Threads_GetThread(TID);
953 ret = Threads_Wake( thread );
954 //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
958 void Threads_ToggleTrace(int TID)
960 tThread *thread = Threads_GetThread(TID);
962 thread->bInstrTrace = !thread->bInstrTrace;
966 * \brief Adds a thread to the active queue
968 void Threads_AddActive(tThread *Thread)
970 #if DEBUG_TRACE_ACTIVEQUEUE
971 Debug("Threads_AddActive("PRIthread_fmt")", PRIthread_args(Thread));
973 SHORTLOCK( &glThreadListLock );
975 if( Thread->Status == THREAD_STAT_ACTIVE )
977 tThread *cur = Proc_GetCurThread();
978 Log_KernelPanic("Threads",
979 "ret=%p CPU%i "PRIthread_fmt" is adding "PRIthread_fmt" when it is active",
980 __builtin_return_address(0),
981 GetCPUNum(), PRIthread_args(cur), PRIthread_args(Thread));
982 SHORTREL( &glThreadListLock );
987 Thread->Status = THREAD_STAT_ACTIVE;
988 // Add to active list
989 // - Thread can be the current thread if we're interrupted just before
990 // Proc_Reschedule in a sleep state.
991 if( Thread != Proc_GetCurThread() )
993 #if SCHEDULER_TYPE == SCHED_RR_PRI
994 tThreadList *list = &gaActiveThreads[Thread->Priority];
996 tThreadList *list = &gActiveThreads;
998 #if DEBUG_TRACE_ACTIVEQUEUE
999 Debug(" - Head="PRIthread_fmt",Tail="PRIthread_fmt"",
1000 PRIthread_args(list->Head),
1001 PRIthread_args(list->Tail)
1004 Threads_int_AddToList( list, Thread );
1007 // Update bookkeeping
1008 giNumActiveThreads ++;
1010 #if SCHEDULER_TYPE == SCHED_LOTTERY
1013 // Only change the ticket count if the thread is un-scheduled
1014 if(Thread->CurCPU != -1)
1017 delta = caiTICKET_COUNTS[ Thread->Priority ];
1019 giFreeTickets += delta;
1020 # if DEBUG_TRACE_TICKETS
1021 Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]",
1022 GetCPUNum(), Thread, Thread->TID, Thread->ThreadName,
1023 giFreeTickets, delta
1029 SHORTREL( &glThreadListLock );
1033 * \brief Removes the current thread from the active queue
1034 * \warning This should ONLY be called with the lock held
1035 * \return Current thread pointer
1037 tThread *Threads_RemActive(void)
1039 tThread *us = Proc_GetCurThread();
1040 #if DEBUG_TRACE_ACTIVEQUEUE
1041 Debug("Threads_RemActive(%p(%i %s))", us, us->TID, us->ThreadName);
1043 giNumActiveThreads --;
1048 * \fn void Threads_SetFaultHandler(Uint Handler)
1049 * \brief Sets the signal handler for a signal
1051 void Threads_SetFaultHandler(Uint Handler)
1053 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
1054 Proc_GetCurThread()->FaultHandler = Handler;
1058 * \fn void Threads_Fault(int Num)
1059 * \brief Calls a fault handler
1061 void Threads_Fault(int Num)
1063 tThread *thread = Proc_GetCurThread();
1065 if(!thread) return ;
1067 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
1069 switch(thread->FaultHandler)
1072 Threads_Kill(thread, -1);
1075 case 1: // Dump Core?
1076 Threads_Kill(thread, -1);
1081 // Double Fault? Oh, F**k
1082 if(thread->CurFaultNum != 0) {
1083 Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID);
1084 Threads_Kill(thread, -1); // For now, just kill
1088 thread->CurFaultNum = Num;
1090 Proc_CallFaultHandler(thread);
1094 * \fn void Threads_SegFault(tVAddr Addr)
1095 * \brief Called when a Segment Fault occurs
1097 void Threads_SegFault(tVAddr Addr)
1099 tThread *cur = Proc_GetCurThread();
1100 cur->bInstrTrace = 0;
1101 Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr);
1102 MM_DumpTables(0, USER_MAX);
1104 //Threads_Exit( 0, -1 );
1108 void Threads_PostSignalTo(tThread *Thread, int SignalNum)
1111 Log_Debug("Threads", "Signalling %i(%s) with %i", Thread->TID, Thread->ThreadName, SignalNum);
1112 Thread->PendingSignal = SignalNum;
1113 Threads_PostEvent(Thread, THREAD_EVENT_SIGNAL);
1115 void Threads_PostSignal(int SignalNum)
1117 Threads_PostSignalTo( Proc_GetCurThread(), SignalNum );
1120 void Threads_SignalGroup(tPGID PGID, int Signal)
1122 for( tProcess *proc = gAllProcesses; proc; proc = proc->Next )
1124 if(proc->PGID == PGID)
1126 Threads_PostSignalTo(proc->FirstThread, Signal);
1133 int Threads_GetPendingSignal(void)
1135 tThread *cur = Proc_GetCurThread();
1137 // Atomic AND with 0 fetches and clears in one operation
1138 int ret = __sync_fetch_and_and( &cur->PendingSignal, 0 );
1141 Log_Debug("Threads", "Thread %i(%s) has signal %i pending",
1142 cur->TID, cur->ThreadName, ret);
1148 * \brief Update the current thread's signal handler
1150 void Threads_SetSignalHandler(int SignalNum, void *Handler)
1152 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1154 if( !MM_IsUser(Handler) )
1156 Proc_GetCurThread()->Process->SignalHandlers[SignalNum] = Handler;
1160 * \brief Gets the registered (or default, if none set) handler for a signal.
1161 * \return Handler function pointer, OR NULL if no signal to be ignored
1163 void *Threads_GetSignalHandler(int SignalNum)
1166 void *User_Signal_Core = User_Signal_Kill;
1168 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1170 void *ret = Proc_GetCurThread()->Process->SignalHandlers[SignalNum];
1171 if( !ret || (SignalNum == SIGKILL || SignalNum == SIGSTOP) )
1178 ret = User_Signal_Kill;
1184 ret = User_Signal_Core;
1187 ret = User_Signal_Kill;
1190 ret = User_Signal_Core;
1195 ret = User_Signal_Kill;
1202 Log_Debug("Threads", "Handler %p for signal %i", ret, SignalNum);
1206 // --- Process Structure Access Functions ---
1207 tPGID Threads_GetPGID(void)
1209 return Proc_GetCurThread()->Process->PGID;
1211 tPID Threads_GetPID(void)
1213 return Proc_GetCurThread()->Process->PID;
1215 tTID Threads_GetTID(void)
1217 return Proc_GetCurThread()->TID;
1219 tUID Threads_GetUID(void)
1221 return Proc_GetCurThread()->Process->UID;
1223 tGID Threads_GetGID(void)
1225 return Proc_GetCurThread()->Process->GID;
1228 int Threads_SetUID(tUID ID)
1230 tThread *t = Proc_GetCurThread();
1231 if( t->Process->UID != 0 ) {
1235 Log_Debug("Threads", "PID %i's UID set to %i", t->Process->PID, ID);
1236 t->Process->UID = ID;
1240 int Threads_SetGID(tGID ID)
1242 tThread *t = Proc_GetCurThread();
1243 if( t->Process->UID != 0 ) {
1247 Log_Debug("Threads", "PID %i's GID set to %i", t->Process->PID, ID);
1248 t->Process->GID = ID;
1252 // --- Per-thread storage ---
1253 int *Threads_GetErrno(void)
1255 return &Proc_GetCurThread()->_errno;
1258 // --- Configuration ---
1259 int *Threads_GetMaxFD(tProcess *Process)
1261 if(!Process) Process = Proc_GetCurThread()->Process;
1262 return &Process->MaxFD;
1264 char **Threads_GetChroot(tProcess *Process)
1266 if(!Process) Process = Proc_GetCurThread()->Process;
1267 return &Process->RootDir;
1269 char **Threads_GetCWD(tProcess *Process)
1271 if(!Process) Process = Proc_GetCurThread()->Process;
1272 return &Process->CurrentWorkingDir;
1276 void Threads_int_DumpThread(tThread *thread)
1279 Log(" %p NULL", thread);
1282 if( !CheckMem(thread, sizeof(tThread)) ) {
1283 Log(" %p INVAL", thread);
1286 tPID pid = (thread->Process ? thread->Process->PID : -1);
1287 const char *statstr = (thread->Status < sizeof(casTHREAD_STAT)/sizeof(casTHREAD_STAT[0])
1288 ? casTHREAD_STAT[thread->Status] : "");
1289 Log(" %p %i (%i) - %s (CPU %i) - %i (%s)",
1290 thread, thread->TID, pid, thread->ThreadName, thread->CurCPU,
1291 thread->Status, statstr
1293 switch(thread->Status)
1295 case THREAD_STAT_MUTEXSLEEP:
1296 Log(" Mutex Pointer: %p", thread->WaitPointer);
1298 case THREAD_STAT_RWLOCKSLEEP:
1299 Log(" Lock Pointer: %p", thread->WaitPointer);
1300 Log(" Lock Name: %s",
1301 ((tRWLock*)thread->WaitPointer)->Name);
1303 case THREAD_STAT_SEMAPHORESLEEP:
1304 Log(" Semaphore Pointer: %p", thread->WaitPointer);
1305 Log(" Semaphore Name: %s:%s",
1306 ((tSemaphore*)thread->WaitPointer)->ModName,
1307 ((tSemaphore*)thread->WaitPointer)->Name
1310 case THREAD_STAT_EVENTSLEEP:
1311 Log(" Event Mask: %x", thread->RetStatus);
1313 case THREAD_STAT_ZOMBIE:
1314 Log(" Return Status: %i", thread->RetStatus);
1318 Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
1319 Log(" KStack %p", thread->KernelStack);
1320 if( thread->bInstrTrace )
1321 Log(" Tracing Enabled");
1322 Proc_DumpThreadCPUState(thread);
1326 * \fn void Threads_Dump(void)
1328 void Threads_DumpActive(void)
1330 Log("Active Threads: (%i reported)", giNumActiveThreads);
1332 #if SCHEDULER_TYPE == SCHED_RR_PRI
1333 for( int i = 0; i < MIN_PRIORITY+1; i++ )
1335 tThreadList *list = &gaActiveThreads[i];
1337 tThreadList *list = &gActiveThreads;
1339 for(tThread *thread = list->Head; thread; thread = thread->Next)
1341 Threads_int_DumpThread(thread);
1342 if(thread->Status != THREAD_STAT_ACTIVE)
1343 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)",
1344 thread->Status, THREAD_STAT_ACTIVE);
1347 #if SCHEDULER_TYPE == SCHED_RR_PRI
1353 * \fn void Threads_Dump(void)
1354 * \brief Dumps a list of currently running threads
1356 void Threads_Dump(void)
1358 Log("--- Thread Dump ---");
1359 Threads_DumpActive();
1361 Log("All Threads:");
1362 for(tThread *thread = gAllThreads; thread; thread = thread->GlobalNext)
1364 Threads_int_DumpThread(thread);
1368 tThread *Threads_int_GetRunnable(void)
1370 #if SCHEDULER_TYPE == SCHED_LOTTERY
1371 // -----------------------------------
1372 // Lottery Scheduler
1373 // -----------------------------------
1374 #if DEBUG_VALIDATE_TICKET_COUNTS
1376 int total_tickets = 0;
1377 for(const tThread *thread = gActiveThreads.Head; thread; thread = thread->Next)
1379 if(thread->Status != THREAD_STAT_ACTIVE)
1380 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
1381 thread, thread->TID, thread->ThreadName, thread->Status);
1382 if(thread->Next == thread) {
1383 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
1384 thread, thread->TID, thread->ThreadName, thread->Status);
1386 total_tickets += caiTICKET_COUNTS[ thread->Priority ];
1388 if(total_tickets != giFreeTickets)
1390 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
1391 giFreeTickets, total_tickets, CPU);
1396 // No free tickets (all tasks delegated to cores)
1397 if( giFreeTickets == 0 )
1402 // Get the ticket number
1403 int ticket = rand() % giFreeTickets;
1404 int number = ticket;
1406 // Find the next thread
1407 tThread **pnp = &gActiveThreads.Head;
1409 for(thread = gActiveThreads.Head; thread; pnp = &thread->Next, thread = thread->Next )
1411 if( caiTICKET_COUNTS[ thread->Priority ] > number)
1413 number -= caiTICKET_COUNTS[ thread->Priority ];
1416 // If we didn't find a thread, something went wrong
1419 int total_tickets = 0;
1420 for(thread=gActiveThreads;thread;thread=thread->Next) {
1421 if(thread->CurCPU >= 0) continue;
1422 total_tickets += caiTICKET_COUNTS[ thread->Priority ];
1424 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
1425 giFreeTickets, total_tickets);
1429 *pnp = thread->Next;
1431 gActiveThreads.Tail = prev;
1433 giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
1434 # if DEBUG_TRACE_TICKETS
1435 LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n",
1436 CPU, thread, thread->TID, thread->ThreadName,
1437 giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]);
1441 #elif SCHEDULER_TYPE == SCHED_RR_PRI
1443 // -----------------------------------
1444 // Priority based round robin scheduler
1445 // -----------------------------------
1446 for( int i = 0; i < MIN_PRIORITY + 1; i ++ )
1448 if( gaActiveThreads[i].Head == NULL )
1451 tThread *thread = gaActiveThreads[i].Head;
1453 if( thread->Status != THREAD_STAT_ACTIVE ) {
1454 for( const tThread *t = gaActiveThreads[i].Head; t; t = t->Next )
1455 LogF("- %p(%i %s)\n", t, t->TID, t->ThreadName);
1456 Panic("Thread %p(%i %s) from pqueue %i is active!",
1457 thread, thread->TID, thread->ThreadName, i);
1461 gaActiveThreads[i].Head = thread->Next;
1463 gaActiveThreads[i].Tail = NULL;
1464 thread->Next = NULL;
1469 #elif SCHEDULER_TYPE == SCHED_RR_SIM
1471 // -----------------------------------
1472 // Single-list round-robin
1473 // -----------------------------------
1474 tThread *thread = gActiveThreads.Head;
1477 gActiveThreads.Head = thread->Next;
1479 gaActiveThreads.Tail = NULL;
1480 thread->Next = NULL;
1484 # error "Unimplemented scheduling algorithm"
1490 * \brief Gets the next thread to run
1491 * \param CPU Current CPU
1492 * \param Last The thread the CPU was running
1494 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
1496 // If this CPU has the lock, we must let it complete
1497 if( CPU_HAS_LOCK( &glThreadListLock ) )
1500 // Don't change threads if the current CPU has switches disabled
1501 if( gaThreads_NoTaskSwitch[CPU] )
1505 SHORTLOCK( &glThreadListLock );
1507 // Make sure the current (well, old) thread is marked as de-scheduled
1508 if(Last) Last->CurCPU = -1;
1510 // No active threads, just take a nap
1511 if(giNumActiveThreads == 0) {
1512 SHORTREL( &glThreadListLock );
1513 #if DEBUG_TRACE_TICKETS
1514 Log("No active threads");
1519 // Allow the old thread to be scheduled again
1522 if( Last->Status == THREAD_STAT_ACTIVE ) {
1524 #if SCHEDULER_TYPE == SCHED_LOTTERY
1525 giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
1526 # if DEBUG_TRACE_TICKETS
1527 LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n",
1528 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets,
1529 caiTICKET_COUNTS[ Last->Priority ]);
1533 #if SCHEDULER_TYPE == SCHED_RR_PRI
1534 list = &gaActiveThreads[ Last->Priority ];
1536 list = &gActiveThreads;
1538 // Add to end of list
1539 Threads_int_AddToList( list, Last );
1540 #if DEBUG_TRACE_ACTIVEQUEUE > 1
1541 Debug("Threads_GetNextToRun: Append thread %p(%i %s)",
1542 Last, Last->TID, Last->ThreadName);
1545 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
1547 LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n",
1548 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
1553 // Call actual scheduler
1554 tThread *thread = Threads_int_GetRunnable();
1559 if( thread->Status != THREAD_STAT_ACTIVE )
1561 LogF("Thread %p(%i %s) scheduled while not active\n",
1562 thread, thread->TID, thread->ThreadName);
1565 // Make the new thread non-schedulable
1566 thread->CurCPU = CPU;
1567 thread->Remaining = thread->Quantum;
1569 #if DEBUG_TRACE_SCHEDULE
1570 Debug("Scheduled "PRIthread_fmt", next = %p",
1571 PRIthread_args(thread),
1577 // No thread possible, warning condition (idle thread should be runnable)
1578 Warning("No runnable thread for CPU%i", CPU);
1581 SHORTREL( &glThreadListLock );
1587 EXPORT(Threads_GetUID);
1588 EXPORT(Threads_GetGID);