3 * - By John Hodge (thePowersGang)
5 * - Common Thread Control
10 #include <threads_int.h>
13 #include <semaphore.h>
15 #include <vfs_threads.h> // VFS Handle maintainence
17 #include <debug_hooks.h>
20 #define DEBUG_TRACE_ACTIVEQUEUE 0 // Trace adds/removals from the active queue
21 #define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
22 #define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
23 #define DEBUG_TRACE_SCHEDULE 0 // Trace scheduling
27 #define SCHED_LOTTERY 1 // Lottery scheduler
28 #define SCHED_RR_SIM 2 // Single Queue Round Robin
29 #define SCHED_RR_PRI 3 // Multi Queue Round Robin
31 #define SCHEDULER_TYPE SCHED_RR_PRI
34 #define DEFAULT_QUANTUM 5
35 #define DEFAULT_PRIORITY 5
36 #define MIN_PRIORITY 10
38 #define PRIthread_fmt "%p(%i %s)"
39 #define PRIthread_args(t) (t), ((t)?(t)->TID:-1), ((t)?(t)->ThreadName:"-")
42 extern void User_Signal_Kill(int SigNum);
52 void Threads_Init(void);
54 void Threads_Delete(tThread *Thread);
55 int Threads_SetName(const char *NewName);
57 char *Threads_GetName(tTID ID);
59 void Threads_SetPriority(tThread *Thread, int Pri);
60 tThread *Threads_CloneTCB(Uint Flags);
61 int Threads_WaitTID(int TID, int *status);
62 tThread *Threads_GetThread(Uint TID);
64 tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread);
65 void Threads_int_AddToList(tThreadList *List, tThread *Thread);
67 void Threads_Exit(int TID, int Status);
68 void Threads_Kill(tThread *Thread, int Status);
69 void Threads_Yield(void);
70 int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock);
71 void Threads_Sleep(void);
72 int Threads_Wake(tThread *Thread);
73 void Threads_AddActive(tThread *Thread);
74 tThread *Threads_RemActive(void);
75 void Threads_ToggleTrace(int TID);
77 void Threads_Fault(int Num);
78 void Threads_SegFault(tVAddr Addr);
79 void Threads_PostSignalTo(tThread *Thread, int SignalNum);
81 void Threads_PostSignal(int SignalNum);
82 void Threads_SignalGroup(tPGID PGID, int SignalNum);
84 int Threads_GetPendingSignal(void);
85 void Threads_SetSignalHandler(int SignalNum, void *Handler);
86 void *Threads_GetSignalHandler(int SignalNum);
88 int Threads_GetPID(void);
89 int Threads_GetTID(void);
90 tUID Threads_GetUID(void);
91 tGID Threads_GetGID(void);
92 int Threads_SetUID(Uint *Errno, tUID ID);
93 int Threads_SetGID(Uint *Errno, tUID ID);
95 void Threads_int_DumpThread(tThread *thread);
97 void Threads_Dump(void);
99 void Threads_DumpActive(void);
100 tThread *Threads_int_GetRunnable(void);
104 tProcess gProcessZero = {
106 // Only used for the core kernel
107 tThread gThreadZero = {
108 .Status = THREAD_STAT_ACTIVE, // Status
109 .ThreadName = (char*)"ThreadZero", // Name
110 .Quantum = DEFAULT_QUANTUM, // Default Quantum
111 .Remaining = DEFAULT_QUANTUM, // Current Quantum
112 .Priority = DEFAULT_PRIORITY // Number of tickets
115 tProcess *gAllProcesses = &gProcessZero;
117 tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
118 // --- Current State ---
119 volatile int giNumActiveThreads = 0; // Number of threads on the active queue
120 volatile Uint giNextTID = 1; // Next TID to allocate
121 // --- Thread Lists ---
122 tThread *gAllThreads = NULL; // All allocated threads
123 tThreadList gSleepingThreads; // Sleeping Threads
124 int giNumCPUs = 1; // Number of CPUs
125 BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF)
126 // --- Scheduler Types ---
127 #if SCHEDULER_TYPE == SCHED_LOTTERY
128 const int caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0};
129 volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads
130 tThreadList gActiveThreads; // Currently Running Threads
131 #elif SCHEDULER_TYPE == SCHED_RR_SIM
132 tThreadList gActiveThreads; // Currently Running Threads
133 #elif SCHEDULER_TYPE == SCHED_RR_PRI
134 tThreadList gaActiveThreads[MIN_PRIORITY+1]; // Active threads for each priority level
136 # error "Unkown scheduler type"
141 * \fn void Threads_Init(void)
142 * \brief Initialse the thread list
144 void Threads_Init(void)
148 Log_Debug("Threads", "Offsets of tThread");
149 Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority));
150 Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack));
152 // Create Initial Task
153 gAllThreads = &gThreadZero;
154 giNumActiveThreads = 1;
155 gThreadZero.Process = &gProcessZero;
160 void Threads_Delete(tThread *Thread)
163 Thread->Status = THREAD_STAT_BURIED;
165 // Clear out process state
166 Proc_ClearThread(Thread);
168 Thread->Process->nThreads --;
170 if( Thread->Process->FirstThread == Thread )
172 Thread->Process->FirstThread = Thread->ProcessNext;
176 tThread *prev = Thread->Process->FirstThread;
177 while(prev && prev->ProcessNext != Thread)
178 prev = prev->ProcessNext;
180 Log_Error("Threads", "Thread %p(%i %s) is not on the process's list",
181 Thread, Thread->TID, Thread->ThreadName
184 prev->ProcessNext = Thread->ProcessNext;
187 // If the final thread is being terminated, clean up the process
188 if( Thread->Process->nThreads == 0 )
190 tProcess *proc = Thread->Process;
192 // Remove from global process list
195 proc->Prev->Next = proc->Next;
197 gAllProcesses = proc->Next;
199 proc->Next->Prev = proc->Prev;
202 VFS_CloseAllUserHandles( proc );
203 // Architecture cleanup
204 Proc_ClearProcess( proc );
205 // VFS Configuration strings
206 if( proc->CurrentWorkingDir)
207 free( proc->CurrentWorkingDir );
209 free( proc->RootDir );
210 // Process descriptor
215 if( IsHeap(Thread->ThreadName) )
216 free(Thread->ThreadName);
218 // Remove from global list
219 // TODO: Lock this too
220 if( Thread == gAllThreads )
221 gAllThreads = Thread->GlobalNext;
223 Thread->GlobalPrev->GlobalNext = Thread->GlobalNext;
229 * \fn void Threads_SetName(const char *NewName)
230 * \brief Sets the current thread's name
231 * \param NewName New name for the thread
232 * \return Boolean Failure
234 int Threads_SetName(const char *NewName)
236 tThread *cur = Proc_GetCurThread();
237 char *oldname = cur->ThreadName;
239 // NOTE: There is a possibility of non-thread safety here
240 // A thread could read the current name pointer before it is zeroed
242 cur->ThreadName = NULL;
244 if( IsHeap(oldname) ) free( oldname );
245 cur->ThreadName = strdup(NewName);
247 Log_Debug("Threads", "Thread renamed to '%s'", NewName);
253 * \fn char *Threads_GetName(int ID)
254 * \brief Gets a thread's name
255 * \param ID Thread ID (-1 indicates current thread)
256 * \return Pointer to name
257 * \retval NULL Failure
259 char *Threads_GetName(tTID ID)
262 return Proc_GetCurThread()->ThreadName;
264 return Threads_GetThread(ID)->ThreadName;
268 * \fn void Threads_SetPriority(tThread *Thread, int Pri)
269 * \brief Sets the priority of a task
270 * \param Thread Thread to update ticket count (NULL means current thread)
271 * \param Pri New priority
273 void Threads_SetPriority(tThread *Thread, int Pri)
275 // Get current thread
276 if(Thread == NULL) Thread = Proc_GetCurThread();
278 // - If < 0, set to lowest priority
279 // - Minumum priority is actualy a high number, 0 is highest
280 if(Pri < 0) Pri = MIN_PRIORITY;
281 if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY;
283 // Do we actually have to do anything?
284 if( Pri == Thread->Priority ) return;
286 #if SCHEDULER_TYPE == SCHED_RR_PRI
287 if( Thread != Proc_GetCurThread() )
289 SHORTLOCK( &glThreadListLock );
290 // Remove from old priority
291 Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread );
293 Threads_int_AddToList( &gaActiveThreads[Pri], Thread );
294 Thread->Priority = Pri;
295 SHORTREL( &glThreadListLock );
298 Thread->Priority = Pri;
300 // If this isn't the current thread, we need to lock
301 if( Thread != Proc_GetCurThread() )
303 SHORTLOCK( &glThreadListLock );
305 #if SCHEDULER_TYPE == SCHED_LOTTERY
306 giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri];
307 # if DEBUG_TRACE_TICKETS
308 Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]",
310 caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]);
313 Thread->Priority = Pri;
314 SHORTREL( &glThreadListLock );
317 Thread->Priority = Pri;
320 #if DEBUG_TRACE_STATE
321 Log("Threads_SetPriority: %p(%i %s) pri set %i",
322 Thread, Thread->TID, Thread->ThreadName,
328 * \brief Clone the TCB of the current thread
329 * \param Flags Flags for something... (What is this for?)
331 tThread *Threads_CloneTCB(Uint Flags)
333 tThread *cur = Proc_GetCurThread();
335 // Allocate and duplicate
336 tThread *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 LOG("us = %p(%i %s), status=%i", us, us->TID, us->ThreadName, Status);
764 ASSERT(Status != THREAD_STAT_ACTIVE);
765 ASSERT(Status != THREAD_STAT_DEAD);
766 while( us->Status == Status )
769 if( us->Status == Status )
770 Debug("Thread %p(%i %s) rescheduled while in %s state for %p",
771 us, us->TID, us->ThreadName,
772 casTHREAD_STAT[Status],
773 __builtin_return_address(0));
777 int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock)
779 SHORTLOCK( &glThreadListLock );
780 tThread *us = Threads_RemActive();
782 // - Mark as sleeping
784 us->WaitPointer = Ptr;
785 us->RetStatus = Num; // Use RetStatus as a temp variable
790 (*ListTail)->Next = us;
797 else if( ListHead ) {
798 us->Next = *ListHead;
805 //if( Proc_ThreadSync(us) )
807 SHORTREL( &glThreadListLock );
810 Threads_int_WaitForStatusEnd(Status);
811 us->WaitPointer = NULL;
812 return us->RetStatus;
816 * \fn void Threads_Sleep(void)
817 * \brief Take the current process off the run queue
819 void Threads_Sleep(void)
821 tThread *cur = Proc_GetCurThread();
824 SHORTLOCK( &glThreadListLock );
826 // Don't sleep if there is a message waiting
827 if( cur->Messages ) {
828 SHORTREL( &glThreadListLock );
832 // Remove us from running queue
834 // Mark thread as sleeping
835 cur->Status = THREAD_STAT_SLEEPING;
837 // Add to Sleeping List (at the top)
838 Threads_int_AddToList( &gSleepingThreads, cur );
840 #if DEBUG_TRACE_STATE
841 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
845 SHORTREL( &glThreadListLock );
846 Threads_int_WaitForStatusEnd(THREAD_STAT_SLEEPING);
851 * \brief Wakes a sleeping/waiting thread up
852 * \param Thread Thread to wake
853 * \return Boolean Failure (Returns ERRNO)
854 * \warning This should ONLY be called with task switches disabled
856 int Threads_Wake(tThread *Thread)
861 switch(Thread->Status)
863 case THREAD_STAT_ACTIVE:
864 Log("Threads_Wake - Waking awake thread (%i)", Thread->TID);
867 case THREAD_STAT_SLEEPING:
868 // Remove from sleeping queue
869 SHORTLOCK( &glThreadListLock );
870 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
871 SHORTREL( &glThreadListLock );
873 Threads_AddActive( Thread );
875 #if DEBUG_TRACE_STATE
876 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
880 case THREAD_STAT_SEMAPHORESLEEP: {
882 tThread *th, *prev=NULL;
884 sem = Thread->WaitPointer;
886 SHORTLOCK( &sem->Protector );
888 // Remove from sleeping queue
889 for( th = sem->Waiting; th; prev = th, th = th->Next )
890 if( th == Thread ) break;
894 prev->Next = Thread->Next;
896 sem->Waiting = Thread->Next;
897 if(sem->LastWaiting == Thread)
898 sem->LastWaiting = prev;
903 for( th = sem->Signaling; th; prev = th, th = th->Next )
904 if( th == Thread ) break;
906 Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)",
907 Thread, Thread->TID, Thread->ThreadName,
908 sem, sem->ModName, sem->Name);
913 prev->Next = Thread->Next;
915 sem->Signaling = Thread->Next;
916 if(sem->LastSignaling == Thread)
917 sem->LastSignaling = prev;
920 Thread->RetStatus = 0; // It didn't get anything
921 Threads_AddActive( Thread );
923 #if DEBUG_TRACE_STATE
924 Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName);
926 SHORTREL( &sem->Protector );
929 case THREAD_STAT_WAITING:
930 Warning("Threads_Wake - Waiting threads are not currently supported");
933 case THREAD_STAT_DEAD:
934 Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID);
938 Log_Warning("Threads", "Threads_Wake - Unknown process status (%i)", Thread->Status);
944 * \brief Wake a thread given the TID
945 * \param TID Thread ID to wake
946 * \return Boolean Faulure (errno)
948 int Threads_WakeTID(tTID TID)
950 tThread *thread = Threads_GetThread(TID);
954 ret = Threads_Wake( thread );
955 //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
959 void Threads_ToggleTrace(int TID)
961 tThread *thread = Threads_GetThread(TID);
963 thread->bInstrTrace = !thread->bInstrTrace;
967 * \brief Adds a thread to the active queue
969 void Threads_AddActive(tThread *Thread)
971 #if DEBUG_TRACE_ACTIVEQUEUE
972 Debug("Threads_AddActive("PRIthread_fmt")", PRIthread_args(Thread));
974 SHORTLOCK( &glThreadListLock );
976 if( Thread->Status == THREAD_STAT_ACTIVE )
978 tThread *cur = Proc_GetCurThread();
979 Log_KernelPanic("Threads",
980 "ret=%p CPU%i "PRIthread_fmt" is adding "PRIthread_fmt" when it is active",
981 __builtin_return_address(0),
982 GetCPUNum(), PRIthread_args(cur), PRIthread_args(Thread));
983 SHORTREL( &glThreadListLock );
988 Thread->Status = THREAD_STAT_ACTIVE;
989 // Add to active list
990 // - Thread can be the current thread if we're interrupted just before
991 // Proc_Reschedule in a sleep state.
992 if( Thread != Proc_GetCurThread() )
994 #if SCHEDULER_TYPE == SCHED_RR_PRI
995 tThreadList *list = &gaActiveThreads[Thread->Priority];
997 tThreadList *list = &gActiveThreads;
999 #if DEBUG_TRACE_ACTIVEQUEUE
1000 Debug(" - Head="PRIthread_fmt",Tail="PRIthread_fmt"",
1001 PRIthread_args(list->Head),
1002 PRIthread_args(list->Tail)
1005 Threads_int_AddToList( list, Thread );
1008 // Update bookkeeping
1009 giNumActiveThreads ++;
1011 #if SCHEDULER_TYPE == SCHED_LOTTERY
1014 // Only change the ticket count if the thread is un-scheduled
1015 if(Thread->CurCPU != -1)
1018 delta = caiTICKET_COUNTS[ Thread->Priority ];
1020 giFreeTickets += delta;
1021 # if DEBUG_TRACE_TICKETS
1022 Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]",
1023 GetCPUNum(), Thread, Thread->TID, Thread->ThreadName,
1024 giFreeTickets, delta
1030 SHORTREL( &glThreadListLock );
1034 * \brief Removes the current thread from the active queue
1035 * \warning This should ONLY be called with the lock held
1036 * \return Current thread pointer
1038 tThread *Threads_RemActive(void)
1040 tThread *us = Proc_GetCurThread();
1041 #if DEBUG_TRACE_ACTIVEQUEUE
1042 Debug("Threads_RemActive(%p(%i %s))", us, us->TID, us->ThreadName);
1044 giNumActiveThreads --;
1049 * \fn void Threads_SetFaultHandler(Uint Handler)
1050 * \brief Sets the signal handler for a signal
1052 void Threads_SetFaultHandler(Uint Handler)
1054 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
1055 Proc_GetCurThread()->FaultHandler = Handler;
1059 * \fn void Threads_Fault(int Num)
1060 * \brief Calls a fault handler
1062 void Threads_Fault(int Num)
1064 tThread *thread = Proc_GetCurThread();
1066 if(!thread) return ;
1068 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
1070 switch(thread->FaultHandler)
1073 Threads_Kill(thread, -1);
1076 case 1: // Dump Core?
1077 Threads_Kill(thread, -1);
1082 // Double Fault? Oh, F**k
1083 if(thread->CurFaultNum != 0) {
1084 Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID);
1085 Threads_Kill(thread, -1); // For now, just kill
1089 thread->CurFaultNum = Num;
1091 Proc_CallFaultHandler(thread);
1095 * \fn void Threads_SegFault(tVAddr Addr)
1096 * \brief Called when a Segment Fault occurs
1098 void Threads_SegFault(tVAddr Addr)
1100 tThread *cur = Proc_GetCurThread();
1101 cur->bInstrTrace = 0;
1102 Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr);
1103 MM_DumpTables(0, USER_MAX);
1105 //Threads_Exit( 0, -1 );
1109 void Threads_PostSignalTo(tThread *Thread, int SignalNum)
1112 Log_Debug("Threads", "Signalling %i(%s) with %i", Thread->TID, Thread->ThreadName, SignalNum);
1113 Thread->PendingSignal = SignalNum;
1114 Threads_PostEvent(Thread, THREAD_EVENT_SIGNAL);
1116 void Threads_PostSignal(int SignalNum)
1118 Threads_PostSignalTo( Proc_GetCurThread(), SignalNum );
1121 void Threads_SignalGroup(tPGID PGID, int Signal)
1123 for( tProcess *proc = gAllProcesses; proc; proc = proc->Next )
1125 if(proc->PGID == PGID)
1127 Threads_PostSignalTo(proc->FirstThread, Signal);
1134 int Threads_GetPendingSignal(void)
1136 tThread *cur = Proc_GetCurThread();
1138 // Atomic AND with 0 fetches and clears in one operation
1139 int ret = __sync_fetch_and_and( &cur->PendingSignal, 0 );
1142 Log_Debug("Threads", "Thread %i(%s) has signal %i pending",
1143 cur->TID, cur->ThreadName, ret);
1149 * \brief Update the current thread's signal handler
1151 void Threads_SetSignalHandler(int SignalNum, void *Handler)
1153 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1155 if( !MM_IsUser(Handler) )
1157 Proc_GetCurThread()->Process->SignalHandlers[SignalNum] = Handler;
1161 * \brief Gets the registered (or default, if none set) handler for a signal.
1162 * \return Handler function pointer, OR NULL if no signal to be ignored
1164 void *Threads_GetSignalHandler(int SignalNum)
1167 void *User_Signal_Core = User_Signal_Kill;
1169 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1171 void *ret = Proc_GetCurThread()->Process->SignalHandlers[SignalNum];
1172 if( !ret || (SignalNum == SIGKILL || SignalNum == SIGSTOP) )
1179 ret = User_Signal_Kill;
1185 ret = User_Signal_Core;
1188 ret = User_Signal_Kill;
1191 ret = User_Signal_Core;
1196 ret = User_Signal_Kill;
1203 Log_Debug("Threads", "Handler %p for signal %i", ret, SignalNum);
1207 // --- Process Structure Access Functions ---
1208 tPGID Threads_GetPGID(void)
1210 return Proc_GetCurThread()->Process->PGID;
1212 tPID Threads_GetPID(void)
1214 return Proc_GetCurThread()->Process->PID;
1216 tTID Threads_GetTID(void)
1218 return Proc_GetCurThread()->TID;
1220 tUID Threads_GetUID(void)
1222 return Proc_GetCurThread()->Process->UID;
1224 tGID Threads_GetGID(void)
1226 return Proc_GetCurThread()->Process->GID;
1229 int Threads_SetUID(tUID ID)
1231 tThread *t = Proc_GetCurThread();
1232 if( t->Process->UID != 0 ) {
1236 Log_Debug("Threads", "PID %i's UID set to %i", t->Process->PID, ID);
1237 t->Process->UID = ID;
1241 int Threads_SetGID(tGID ID)
1243 tThread *t = Proc_GetCurThread();
1244 if( t->Process->UID != 0 ) {
1248 Log_Debug("Threads", "PID %i's GID set to %i", t->Process->PID, ID);
1249 t->Process->GID = ID;
1253 // --- Per-thread storage ---
1254 int *Threads_GetErrno(void)
1256 return &Proc_GetCurThread()->_errno;
1259 // --- Configuration ---
1260 int *Threads_GetMaxFD(tProcess *Process)
1262 if(!Process) Process = Proc_GetCurThread()->Process;
1263 return &Process->MaxFD;
1265 char **Threads_GetChroot(tProcess *Process)
1267 if(!Process) Process = Proc_GetCurThread()->Process;
1268 return &Process->RootDir;
1270 char **Threads_GetCWD(tProcess *Process)
1272 if(!Process) Process = Proc_GetCurThread()->Process;
1273 return &Process->CurrentWorkingDir;
1277 void Threads_int_DumpThread(tThread *thread)
1280 Log(" %p NULL", thread);
1283 if( !CheckMem(thread, sizeof(tThread)) ) {
1284 Log(" %p INVAL", thread);
1287 tPID pid = (thread->Process ? thread->Process->PID : -1);
1288 const char *statstr = (thread->Status < sizeof(casTHREAD_STAT)/sizeof(casTHREAD_STAT[0])
1289 ? casTHREAD_STAT[thread->Status] : "");
1290 Log(" %p %i (%i) - %s (CPU %i) - %i (%s)",
1291 thread, thread->TID, pid, thread->ThreadName, thread->CurCPU,
1292 thread->Status, statstr
1294 switch(thread->Status)
1296 case THREAD_STAT_MUTEXSLEEP:
1297 Log(" Mutex Pointer: %p", thread->WaitPointer);
1299 case THREAD_STAT_RWLOCKSLEEP:
1300 Log(" Lock Pointer: %p", thread->WaitPointer);
1301 Log(" Lock Name: %s",
1302 ((tRWLock*)thread->WaitPointer)->Name);
1304 case THREAD_STAT_SEMAPHORESLEEP:
1305 Log(" Semaphore Pointer: %p", thread->WaitPointer);
1306 Log(" Semaphore Name: %s:%s",
1307 ((tSemaphore*)thread->WaitPointer)->ModName,
1308 ((tSemaphore*)thread->WaitPointer)->Name
1311 case THREAD_STAT_EVENTSLEEP:
1312 Log(" Event Mask: %x", thread->RetStatus);
1314 case THREAD_STAT_ZOMBIE:
1315 Log(" Return Status: %i", thread->RetStatus);
1319 Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
1320 Log(" KStack %p", thread->KernelStack);
1321 if( thread->bInstrTrace )
1322 Log(" Tracing Enabled");
1323 Proc_DumpThreadCPUState(thread);
1327 * \fn void Threads_Dump(void)
1329 void Threads_DumpActive(void)
1331 Log("Active Threads: (%i reported)", giNumActiveThreads);
1333 #if SCHEDULER_TYPE == SCHED_RR_PRI
1334 for( int i = 0; i < MIN_PRIORITY+1; i++ )
1336 tThreadList *list = &gaActiveThreads[i];
1338 tThreadList *list = &gActiveThreads;
1340 for(tThread *thread = list->Head; thread; thread = thread->Next)
1342 Threads_int_DumpThread(thread);
1343 if(thread->Status != THREAD_STAT_ACTIVE)
1344 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)",
1345 thread->Status, THREAD_STAT_ACTIVE);
1348 #if SCHEDULER_TYPE == SCHED_RR_PRI
1354 * \fn void Threads_Dump(void)
1355 * \brief Dumps a list of currently running threads
1357 void Threads_Dump(void)
1359 Log("--- Thread Dump ---");
1360 Threads_DumpActive();
1362 Log("All Threads:");
1363 for(tThread *thread = gAllThreads; thread; thread = thread->GlobalNext)
1365 Threads_int_DumpThread(thread);
1369 tThread *Threads_int_GetRunnable(void)
1371 #if SCHEDULER_TYPE == SCHED_LOTTERY
1372 // -----------------------------------
1373 // Lottery Scheduler
1374 // -----------------------------------
1375 #if DEBUG_VALIDATE_TICKET_COUNTS
1377 int total_tickets = 0;
1378 for(const tThread *thread = gActiveThreads.Head; thread; thread = thread->Next)
1380 if(thread->Status != THREAD_STAT_ACTIVE)
1381 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
1382 thread, thread->TID, thread->ThreadName, thread->Status);
1383 if(thread->Next == thread) {
1384 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
1385 thread, thread->TID, thread->ThreadName, thread->Status);
1387 total_tickets += caiTICKET_COUNTS[ thread->Priority ];
1389 if(total_tickets != giFreeTickets)
1391 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
1392 giFreeTickets, total_tickets, CPU);
1397 // No free tickets (all tasks delegated to cores)
1398 if( giFreeTickets == 0 )
1403 // Get the ticket number
1404 int ticket = rand() % giFreeTickets;
1405 int number = ticket;
1407 // Find the next thread
1408 tThread **pnp = &gActiveThreads.Head;
1410 for(thread = gActiveThreads.Head; thread; pnp = &thread->Next, thread = thread->Next )
1412 if( caiTICKET_COUNTS[ thread->Priority ] > number)
1414 number -= caiTICKET_COUNTS[ thread->Priority ];
1417 // If we didn't find a thread, something went wrong
1420 int total_tickets = 0;
1421 for(thread=gActiveThreads;thread;thread=thread->Next) {
1422 if(thread->CurCPU >= 0) continue;
1423 total_tickets += caiTICKET_COUNTS[ thread->Priority ];
1425 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
1426 giFreeTickets, total_tickets);
1430 *pnp = thread->Next;
1432 gActiveThreads.Tail = prev;
1434 giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
1435 # if DEBUG_TRACE_TICKETS
1436 LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n",
1437 CPU, thread, thread->TID, thread->ThreadName,
1438 giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]);
1442 #elif SCHEDULER_TYPE == SCHED_RR_PRI
1444 // -----------------------------------
1445 // Priority based round robin scheduler
1446 // -----------------------------------
1447 for( int i = 0; i < MIN_PRIORITY + 1; i ++ )
1449 if( gaActiveThreads[i].Head == NULL )
1452 tThread *thread = gaActiveThreads[i].Head;
1454 if( thread->Status != THREAD_STAT_ACTIVE ) {
1455 for( const tThread *t = gaActiveThreads[i].Head; t; t = t->Next )
1456 LogF("- %p(%i %s)\n", t, t->TID, t->ThreadName);
1457 Panic("Thread %p(%i %s) from pqueue %i is active!",
1458 thread, thread->TID, thread->ThreadName, i);
1462 gaActiveThreads[i].Head = thread->Next;
1464 gaActiveThreads[i].Tail = NULL;
1465 thread->Next = NULL;
1470 #elif SCHEDULER_TYPE == SCHED_RR_SIM
1472 // -----------------------------------
1473 // Single-list round-robin
1474 // -----------------------------------
1475 tThread *thread = gActiveThreads.Head;
1476 LOG("thread = %p", thread);
1479 gActiveThreads.Head = thread->Next;
1481 gaActiveThreads.Tail = NULL;
1482 thread->Next = NULL;
1486 # error "Unimplemented scheduling algorithm"
1492 * \brief Gets the next thread to run
1493 * \param CPU Current CPU
1494 * \param Last The thread the CPU was running
1496 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
1498 ASSERT( CPU_HAS_LOCK(&glThreadListLock) );
1500 // Don't change threads if the current CPU has switches disabled
1501 if( gaThreads_NoTaskSwitch[CPU] ) {
1506 // Make sure the current (well, old) thread is marked as de-scheduled
1507 if(Last) Last->CurCPU = -1;
1509 // No active threads, just take a nap
1510 if(giNumActiveThreads == 0) {
1512 #if DEBUG_TRACE_TICKETS
1513 Log("No active threads");
1518 // Allow the old thread to be scheduled again
1521 if( Last->Status == THREAD_STAT_ACTIVE ) {
1523 #if SCHEDULER_TYPE == SCHED_LOTTERY
1524 giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
1525 # if DEBUG_TRACE_TICKETS
1526 LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n",
1527 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets,
1528 caiTICKET_COUNTS[ Last->Priority ]);
1532 #if SCHEDULER_TYPE == SCHED_RR_PRI
1533 list = &gaActiveThreads[ Last->Priority ];
1535 list = &gActiveThreads;
1537 // Add to end of list
1538 Threads_int_AddToList( list, Last );
1539 #if DEBUG_TRACE_ACTIVEQUEUE > 1
1540 Debug("Threads_GetNextToRun: Append thread %p(%i %s)",
1541 Last, Last->TID, Last->ThreadName);
1544 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
1546 LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n",
1547 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
1552 // Call actual scheduler
1553 tThread *thread = Threads_int_GetRunnable();
1558 if( thread->Status != THREAD_STAT_ACTIVE )
1560 LogF("Thread %p(%i %s) scheduled while not active\n",
1561 thread, thread->TID, thread->ThreadName);
1564 // Make the new thread non-schedulable
1565 thread->CurCPU = CPU;
1566 thread->Remaining = thread->Quantum;
1568 #if DEBUG_TRACE_SCHEDULE
1569 Debug("Scheduled "PRIthread_fmt", next = %p",
1570 PRIthread_args(thread),
1576 // No thread possible, warning condition (idle thread should be runnable)
1577 Warning("No runnable thread for CPU%i", CPU);
1584 EXPORT(Threads_GetUID);
1585 EXPORT(Threads_GetGID);