3 * - By John Hodge (thePowersGang)
5 * - Common Thread Control
9 #include <threads_int.h>
12 #include <semaphore.h>
13 #include <vfs_threads.h> // VFS Handle maintainence
17 #define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
18 #define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
22 #define SCHED_LOTTERY 1 // Lottery scheduler
23 #define SCHED_RR_SIM 2 // Single Queue Round Robin
24 #define SCHED_RR_PRI 3 // Multi Queue Round Robin
26 #define SCHEDULER_TYPE SCHED_RR_PRI
29 #define DEFAULT_QUANTUM 5
30 #define DEFAULT_PRIORITY 5
31 #define MIN_PRIORITY 10
43 void Threads_Init(void);
45 void Threads_Delete(tThread *Thread);
46 int Threads_SetName(const char *NewName);
48 char *Threads_GetName(tTID ID);
50 void Threads_SetPriority(tThread *Thread, int Pri);
51 tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
52 int Threads_WaitTID(int TID, int *status);
53 tThread *Threads_GetThread(Uint TID);
55 tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread);
56 void Threads_int_AddToList(tThreadList *List, tThread *Thread);
58 void Threads_Exit(int TID, int Status);
59 void Threads_Kill(tThread *Thread, int Status);
60 void Threads_Yield(void);
61 void Threads_Sleep(void);
62 int Threads_Wake(tThread *Thread);
63 void Threads_AddActive(tThread *Thread);
64 tThread *Threads_RemActive(void);
66 void Threads_ToggleTrace(int TID);
67 void Threads_Fault(int Num);
68 void Threads_SegFault(tVAddr Addr);
69 void Threads_PostSignal(int SignalNum);
70 int Threads_GetPendingSignal(void);
71 void Threads_SetSignalHandler(int SignalNum, void *Handler);
72 void *Threads_GetSignalHandler(int SignalNum);
74 int Threads_GetPID(void);
75 int Threads_GetTID(void);
76 tUID Threads_GetUID(void);
77 tGID Threads_GetGID(void);
78 int Threads_SetUID(Uint *Errno, tUID ID);
79 int Threads_SetGID(Uint *Errno, tUID ID);
81 void Threads_int_DumpThread(tThread *thread);
82 void Threads_Dump(void);
83 void Threads_DumpActive(void);
87 struct sProcess gProcessZero = {
89 // Only used for the core kernel
90 tThread gThreadZero = {
91 .Status = THREAD_STAT_ACTIVE, // Status
92 .ThreadName = (char*)"ThreadZero", // Name
93 .Quantum = DEFAULT_QUANTUM, // Default Quantum
94 .Remaining = DEFAULT_QUANTUM, // Current Quantum
95 .Priority = DEFAULT_PRIORITY // Number of tickets
99 tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
100 // --- Current State ---
101 volatile int giNumActiveThreads = 0; // Number of threads on the active queue
102 volatile Uint giNextTID = 1; // Next TID to allocate
103 // --- Thread Lists ---
104 tThread *gAllThreads = NULL; // All allocated threads
105 tThreadList gSleepingThreads; // Sleeping Threads
106 int giNumCPUs = 1; // Number of CPUs
107 BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF)
108 // --- Scheduler Types ---
109 #if SCHEDULER_TYPE == SCHED_LOTTERY
110 const int caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0};
111 volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads
112 tThreadList gActiveThreads; // Currently Running Threads
113 #elif SCHEDULER_TYPE == SCHED_RR_SIM
114 tThreadList gActiveThreads; // Currently Running Threads
115 #elif SCHEDULER_TYPE == SCHED_RR_PRI
116 tThreadList gaActiveThreads[MIN_PRIORITY+1]; // Active threads for each priority level
118 # error "Unkown scheduler type"
123 * \fn void Threads_Init(void)
124 * \brief Initialse the thread list
126 void Threads_Init(void)
130 Log_Debug("Threads", "Offsets of tThread");
131 Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority));
132 Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack));
134 // Create Initial Task
135 gAllThreads = &gThreadZero;
136 giNumActiveThreads = 1;
137 gThreadZero.Process = &gProcessZero;
142 void Threads_Delete(tThread *Thread)
145 Thread->Status = THREAD_STAT_BURIED;
147 // Clear out process state
148 Proc_ClearThread(Thread);
150 Thread->Process->nThreads --;
152 if( Thread->Process->FirstThread == Thread )
154 Thread->Process->FirstThread = Thread->ProcessNext;
158 tThread *prev = Thread->Process->FirstThread;
159 while(prev && prev->ProcessNext != Thread)
160 prev = prev->ProcessNext;
162 Log_Error("Threads", "Thread %p(%i %s) is not on the process's list",
163 Thread, Thread->TID, Thread->ThreadName
166 prev->ProcessNext = Thread->ProcessNext;
169 // If the final thread is being terminated, clean up the process
170 if( Thread->Process->nThreads == 0 )
172 tProcess *proc = Thread->Process;
174 VFS_CloseAllUserHandles();
175 // Architecture cleanup
176 Proc_ClearProcess( proc );
177 // VFS Configuration strings
178 if( proc->CurrentWorkingDir)
179 free( proc->CurrentWorkingDir );
181 free( proc->RootDir );
182 // Process descriptor
187 if( IsHeap(Thread->ThreadName) )
188 free(Thread->ThreadName);
190 // Remove from global list
191 // TODO: Lock this too
192 if( Thread == gAllThreads )
193 gAllThreads = Thread->GlobalNext;
195 Thread->GlobalPrev->GlobalNext = Thread->GlobalNext;
201 * \fn void Threads_SetName(const char *NewName)
202 * \brief Sets the current thread's name
203 * \param NewName New name for the thread
204 * \return Boolean Failure
206 int Threads_SetName(const char *NewName)
208 tThread *cur = Proc_GetCurThread();
209 char *oldname = cur->ThreadName;
211 // NOTE: There is a possibility of non-thread safety here
212 // A thread could read the current name pointer before it is zeroed
214 cur->ThreadName = NULL;
216 if( IsHeap(oldname) ) free( oldname );
217 cur->ThreadName = strdup(NewName);
219 Log_Debug("Threads", "Thread renamed to '%s'", NewName);
225 * \fn char *Threads_GetName(int ID)
226 * \brief Gets a thread's name
227 * \param ID Thread ID (-1 indicates current thread)
228 * \return Pointer to name
229 * \retval NULL Failure
231 char *Threads_GetName(tTID ID)
234 return Proc_GetCurThread()->ThreadName;
236 return Threads_GetThread(ID)->ThreadName;
240 * \fn void Threads_SetPriority(tThread *Thread, int Pri)
241 * \brief Sets the priority of a task
242 * \param Thread Thread to update ticket count (NULL means current thread)
243 * \param Pri New priority
245 void Threads_SetPriority(tThread *Thread, int Pri)
247 // Get current thread
248 if(Thread == NULL) Thread = Proc_GetCurThread();
250 // - If < 0, set to lowest priority
251 // - Minumum priority is actualy a high number, 0 is highest
252 if(Pri < 0) Pri = MIN_PRIORITY;
253 if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY;
255 // Do we actually have to do anything?
256 if( Pri == Thread->Priority ) return;
258 #if SCHEDULER_TYPE == SCHED_RR_PRI
259 if( Thread != Proc_GetCurThread() )
261 SHORTLOCK( &glThreadListLock );
262 // Remove from old priority
263 Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread );
265 Threads_int_AddToList( &gaActiveThreads[Pri], Thread );
266 Thread->Priority = Pri;
267 SHORTREL( &glThreadListLock );
270 Thread->Priority = Pri;
272 // If this isn't the current thread, we need to lock
273 if( Thread != Proc_GetCurThread() )
275 SHORTLOCK( &glThreadListLock );
277 #if SCHEDULER_TYPE == SCHED_LOTTERY
278 giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri];
279 # if DEBUG_TRACE_TICKETS
280 Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]",
282 caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]);
285 Thread->Priority = Pri;
286 SHORTREL( &glThreadListLock );
289 Thread->Priority = Pri;
292 #if DEBUG_TRACE_STATE
293 Log("Threads_SetPriority: %p(%i %s) pri set %i",
294 Thread, Thread->TID, Thread->ThreadName,
300 * \brief Clone the TCB of the current thread
301 * \param Flags Flags for something... (What is this for?)
303 tThread *Threads_CloneTCB(Uint Flags)
306 cur = Proc_GetCurThread();
308 // Allocate and duplicate
309 new = malloc(sizeof(tThread));
310 if(new == NULL) { errno = -ENOMEM; return NULL; }
311 memcpy(new, cur, sizeof(tThread));
315 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
316 new->Status = THREAD_STAT_PREINIT;
320 new->TID = giNextTID++;
322 new->bInstrTrace = 0;
325 new->ThreadName = strdup(cur->ThreadName);
327 // Set Thread Group ID (PID)
328 if(Flags & CLONE_VM) {
329 tProcess *newproc, *oldproc;
330 oldproc = cur->Process;
331 new->Process = malloc( sizeof(struct sProcess) );
332 newproc = new->Process;
333 newproc->PID = new->TID;
334 if( Flags & CLONE_PGID )
335 newproc->PGID = oldproc->PGID;
337 newproc->PGID = newproc->PID;
338 newproc->UID = oldproc->UID;
339 newproc->GID = oldproc->GID;
340 newproc->MaxFD = oldproc->MaxFD;
341 if( oldproc->CurrentWorkingDir )
342 newproc->CurrentWorkingDir = strdup( oldproc->CurrentWorkingDir );
344 newproc->CurrentWorkingDir = NULL;
345 if( oldproc->RootDir )
346 newproc->RootDir = strdup( oldproc->RootDir );
348 newproc->RootDir = NULL;
349 newproc->nThreads = 1;
350 // Reference all handles in the VFS
351 VFS_ReferenceUserHandles();
353 newproc->FirstThread = new;
354 new->ProcessNext = NULL;
357 new->Process->nThreads ++;
358 new->Process = cur->Process;
360 new->ProcessNext = new->Process->FirstThread;
361 new->Process->FirstThread = new;
364 // Messages are not inherited
365 new->Messages = NULL;
366 new->LastMessage = NULL;
369 new->Remaining = new->Quantum = cur->Quantum;
370 new->Priority = cur->Priority;
373 // Set Signal Handlers
374 new->CurFaultNum = 0;
375 new->FaultHandler = cur->FaultHandler;
377 // Maintain a global list of threads
378 SHORTLOCK( &glThreadListLock );
379 new->GlobalPrev = NULL; // Protect against bugs
380 new->GlobalNext = gAllThreads;
381 gAllThreads->GlobalPrev = new;
383 SHORTREL( &glThreadListLock );
389 * \brief Clone the TCB of the kernel thread
391 tThread *Threads_CloneThreadZero(void)
395 // Allocate and duplicate
396 new = malloc(sizeof(tThread));
400 memcpy(new, &gThreadZero, sizeof(tThread));
402 new->Process->nThreads ++;
406 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
407 new->Status = THREAD_STAT_PREINIT;
411 new->TID = giNextTID++;
415 new->ThreadName = NULL;
417 // Messages are not inherited
418 new->Messages = NULL;
419 new->LastMessage = NULL;
422 new->Remaining = new->Quantum = DEFAULT_QUANTUM;
423 new->Priority = DEFAULT_PRIORITY;
424 new->bInstrTrace = 0;
426 // Set Signal Handlers
427 new->CurFaultNum = 0;
428 new->FaultHandler = 0;
430 // Maintain a global list of threads
431 SHORTLOCK( &glThreadListLock );
432 new->GlobalPrev = NULL; // Protect against bugs
433 new->GlobalNext = gAllThreads;
434 gAllThreads->GlobalPrev = new;
436 SHORTREL( &glThreadListLock );
442 * \brief Wait for a task to change state
443 * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
444 * \param Status Thread return status
445 * \return TID of child that changed state
447 tTID Threads_WaitTID(int TID, int *Status)
452 Uint32 ev = Threads_WaitEvents(THREAD_EVENT_DEADCHILD);
454 if( ev & THREAD_EVENT_DEADCHILD )
456 // A child died, get the TID
457 tThread *us = Proc_GetCurThread();
458 ASSERT(us->LastDeadChild);
459 ret = us->LastDeadChild->TID;
460 // - Mark as dead (as opposed to undead)
461 ASSERT(us->LastDeadChild->Status == THREAD_STAT_ZOMBIE);
462 us->LastDeadChild->Status = THREAD_STAT_DEAD;
463 // - Set return status
465 *Status = us->LastDeadChild->RetStatus;
466 us->LastDeadChild = NULL;
467 Mutex_Release(&us->DeadChildLock);
471 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
476 // Any peer/child thread
478 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
484 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
492 // NOTE: Race condition - Other child dies, desired child dies, first death is 'lost'
493 while( (ret = Threads_WaitTID(-1, Status)) != TID )
505 * \brief Gets a thread given its TID
506 * \param TID Thread ID
507 * \return Thread pointer
509 tThread *Threads_GetThread(Uint TID)
513 // Search global list
514 for( thread = gAllThreads; thread; thread = thread->GlobalNext )
516 if(thread->TID == TID)
520 Log_Notice("Threads", "Unable to find TID %i on main list\n", TID);
526 * \brief Deletes an entry from a list
527 * \param List Pointer to the list head
528 * \param Thread Thread to find
531 tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread)
533 tThread *ret, *prev = NULL;
535 for(ret = List->Head;
536 ret && ret != Thread;
537 prev = ret, ret = ret->Next
540 // Is the thread on the list
542 //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
547 List->Head = Thread->Next;
548 //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
551 prev->Next = Thread->Next;
552 //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
554 if( Thread->Next == NULL )
560 void Threads_int_AddToList(tThreadList *List, tThread *Thread)
563 List->Tail->Next = Thread;
571 * \brief Exit the current process (or another?)
572 * \param TID Thread ID to kill
573 * \param Status Exit status
575 void Threads_Exit(int TID, int Status)
578 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
580 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
582 // Halt forever, just in case
587 * \fn void Threads_Kill(tThread *Thread, int Status)
588 * \brief Kill a thread
589 * \param Thread Thread to kill
590 * \param Status Status code to return to the parent
592 void Threads_Kill(tThread *Thread, int Status)
595 int isCurThread = Thread == Proc_GetCurThread();
597 // TODO: Disown all children?
601 // TODO: I should keep a .Children list
602 for(child = gAllThreads;
604 child = child->GlobalNext)
606 if(child->Parent == Thread)
607 child->Parent = &gThreadZero;
612 ///\note Double lock is needed due to overlap of lock areas
614 // Lock thread (stop us recieving messages)
615 SHORTLOCK( &Thread->IsLocked );
617 // Clear Message Queue
618 while( Thread->Messages )
620 msg = Thread->Messages->Next;
621 free( Thread->Messages );
622 Thread->Messages = msg;
626 SHORTLOCK( &glThreadListLock );
628 switch(Thread->Status)
630 case THREAD_STAT_PREINIT: // Only on main list
633 // Currently active thread
634 case THREAD_STAT_ACTIVE:
635 if( Thread != Proc_GetCurThread() )
637 #if SCHEDULER_TYPE == SCHED_RR_PRI
638 tThreadList *list = &gaActiveThreads[Thread->Priority];
640 tThreadList *list = &gActiveThreads;
642 if( Threads_int_DelFromQueue( list, Thread ) )
647 Log_Warning("Threads",
648 "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list",
649 Thread, Thread->TID, Thread->ThreadName
652 #if SCHEDULER_TYPE == SCHED_LOTTERY
653 giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ];
656 // Ensure that we are not rescheduled
657 Thread->Remaining = 0; // Clear Remaining Quantum
658 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
660 // Update bookkeeping
661 giNumActiveThreads --;
663 // Kill it while it sleeps!
664 case THREAD_STAT_SLEEPING:
665 if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) )
667 Log_Warning("Threads",
668 "Threads_Kill - Thread %p(%i,%s) marked as sleeping, but not on list",
669 Thread, Thread->TID, Thread->ThreadName
674 // Brains!... You cannot kill something that is already dead
675 case THREAD_STAT_ZOMBIE:
676 Log_Warning("Threads", "Threads_Kill - Thread %p(%i,%s) is undead, you cannot kill it",
677 Thread, Thread->TID, Thread->ThreadName);
678 SHORTREL( &glThreadListLock );
679 SHORTREL( &Thread->IsLocked );
683 Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)",
689 Thread->RetStatus = Status;
691 SHORTREL( &Thread->IsLocked );
693 Thread->Status = THREAD_STAT_ZOMBIE;
694 SHORTREL( &glThreadListLock );
695 // TODO: It's possible that we could be timer-preempted here, should disable that... somehow
696 Mutex_Acquire( &Thread->Parent->DeadChildLock ); // released by parent
697 Thread->Parent->LastDeadChild = Thread;
698 Threads_PostEvent( Thread->Parent, THREAD_EVENT_DEADCHILD );
700 Log("Thread %i went *hurk* (%i)", Thread->TID, Status);
711 * \brief Yield remainder of the current thread's timeslice
713 void Threads_Yield(void)
715 // Log("Threads_Yield: by %p", __builtin_return_address(0));
720 * \breif Wait for the thread status to not be a specified value
722 void Threads_int_WaitForStatusEnd(enum eThreadStatus Status)
724 tThread *us = Proc_GetCurThread();
725 ASSERT(Status != THREAD_STAT_ACTIVE);
726 ASSERT(Status != THREAD_STAT_DEAD);
727 while( us->Status == Status )
730 if( us->Status == Status )
731 Debug("Thread %p(%i %s) rescheduled while in %s state for %p",
732 us, us->TID, us->ThreadName,
733 casTHREAD_STAT[Status],
734 __builtin_return_address(0));
738 void Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock)
740 SHORTLOCK( &glThreadListLock );
741 tThread *us = Threads_RemActive();
743 // - Mark as sleeping
745 us->WaitPointer = Ptr;
746 us->RetStatus = Num; // Use RetStatus as a temp variable
751 (*ListTail)->Next = us;
763 //if( Proc_ThreadSync(us) )
765 SHORTREL( &glThreadListLock );
768 Threads_int_WaitForStatusEnd(Status);
772 * \fn void Threads_Sleep(void)
773 * \brief Take the current process off the run queue
775 void Threads_Sleep(void)
777 tThread *cur = Proc_GetCurThread();
780 SHORTLOCK( &glThreadListLock );
782 // Don't sleep if there is a message waiting
783 if( cur->Messages ) {
784 SHORTREL( &glThreadListLock );
788 // Remove us from running queue
790 // Mark thread as sleeping
791 cur->Status = THREAD_STAT_SLEEPING;
793 // Add to Sleeping List (at the top)
794 Threads_int_AddToList( &gSleepingThreads, cur );
796 #if DEBUG_TRACE_STATE
797 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
801 SHORTREL( &glThreadListLock );
802 Threads_int_WaitForStatusEnd(THREAD_STAT_SLEEPING);
807 * \brief Wakes a sleeping/waiting thread up
808 * \param Thread Thread to wake
809 * \return Boolean Failure (Returns ERRNO)
810 * \warning This should ONLY be called with task switches disabled
812 int Threads_Wake(tThread *Thread)
817 switch(Thread->Status)
819 case THREAD_STAT_ACTIVE:
820 Log("Threads_Wake - Waking awake thread (%i)", Thread->TID);
823 case THREAD_STAT_SLEEPING:
824 // Remove from sleeping queue
825 SHORTLOCK( &glThreadListLock );
826 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
827 SHORTREL( &glThreadListLock );
829 Threads_AddActive( Thread );
831 #if DEBUG_TRACE_STATE
832 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
836 case THREAD_STAT_SEMAPHORESLEEP: {
838 tThread *th, *prev=NULL;
840 sem = Thread->WaitPointer;
842 SHORTLOCK( &sem->Protector );
844 // Remove from sleeping queue
845 for( th = sem->Waiting; th; prev = th, th = th->Next )
846 if( th == Thread ) break;
850 prev->Next = Thread->Next;
852 sem->Waiting = Thread->Next;
853 if(sem->LastWaiting == Thread)
854 sem->LastWaiting = prev;
859 for( th = sem->Signaling; th; prev = th, th = th->Next )
860 if( th == Thread ) break;
862 Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)",
863 Thread, Thread->TID, Thread->ThreadName,
864 sem, sem->ModName, sem->Name);
869 prev->Next = Thread->Next;
871 sem->Signaling = Thread->Next;
872 if(sem->LastSignaling == Thread)
873 sem->LastSignaling = prev;
876 Thread->RetStatus = 0; // It didn't get anything
877 Threads_AddActive( Thread );
879 #if DEBUG_TRACE_STATE
880 Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName);
882 SHORTREL( &sem->Protector );
885 case THREAD_STAT_WAITING:
886 Warning("Threads_Wake - Waiting threads are not currently supported");
889 case THREAD_STAT_DEAD:
890 Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID);
894 Log_Warning("Threads", "Threads_Wake - Unknown process status (%i)", Thread->Status);
900 * \brief Wake a thread given the TID
901 * \param TID Thread ID to wake
902 * \return Boolean Faulure (errno)
904 int Threads_WakeTID(tTID TID)
906 tThread *thread = Threads_GetThread(TID);
910 ret = Threads_Wake( thread );
911 //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
915 void Threads_ToggleTrace(int TID)
917 tThread *thread = Threads_GetThread(TID);
919 thread->bInstrTrace = !thread->bInstrTrace;
923 * \brief Adds a thread to the active queue
925 void Threads_AddActive(tThread *Thread)
927 SHORTLOCK( &glThreadListLock );
929 if( Thread->Status == THREAD_STAT_ACTIVE ) {
930 tThread *cur = Proc_GetCurThread();
931 Log_Warning("Threads", "WTF, %p CPU%i %p (%i %s) is adding %p (%i %s) when it is active",
932 __builtin_return_address(0),
933 GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName);
934 SHORTREL( &glThreadListLock );
939 Thread->Status = THREAD_STAT_ACTIVE;
940 // Thread->CurCPU = -1;
941 // Add to active list
943 #if SCHEDULER_TYPE == SCHED_RR_PRI
944 tThreadList *list = &gaActiveThreads[Thread->Priority];
946 tThreadList *list = &gActiveThreads;
948 Threads_int_AddToList( list, Thread );
951 // Update bookkeeping
952 giNumActiveThreads ++;
954 #if SCHEDULER_TYPE == SCHED_LOTTERY
957 // Only change the ticket count if the thread is un-scheduled
958 if(Thread->CurCPU != -1)
961 delta = caiTICKET_COUNTS[ Thread->Priority ];
963 giFreeTickets += delta;
964 # if DEBUG_TRACE_TICKETS
965 Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]",
966 GetCPUNum(), Thread, Thread->TID, Thread->ThreadName,
973 SHORTREL( &glThreadListLock );
977 * \brief Removes the current thread from the active queue
978 * \warning This should ONLY be called with the lock held
979 * \return Current thread pointer
981 tThread *Threads_RemActive(void)
983 giNumActiveThreads --;
984 return Proc_GetCurThread();
988 * \fn void Threads_SetFaultHandler(Uint Handler)
989 * \brief Sets the signal handler for a signal
991 void Threads_SetFaultHandler(Uint Handler)
993 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
994 Proc_GetCurThread()->FaultHandler = Handler;
998 * \fn void Threads_Fault(int Num)
999 * \brief Calls a fault handler
1001 void Threads_Fault(int Num)
1003 tThread *thread = Proc_GetCurThread();
1005 if(!thread) return ;
1007 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
1009 switch(thread->FaultHandler)
1012 Threads_Kill(thread, -1);
1015 case 1: // Dump Core?
1016 Threads_Kill(thread, -1);
1021 // Double Fault? Oh, F**k
1022 if(thread->CurFaultNum != 0) {
1023 Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID);
1024 Threads_Kill(thread, -1); // For now, just kill
1028 thread->CurFaultNum = Num;
1030 Proc_CallFaultHandler(thread);
1034 * \fn void Threads_SegFault(tVAddr Addr)
1035 * \brief Called when a Segment Fault occurs
1037 void Threads_SegFault(tVAddr Addr)
1039 tThread *cur = Proc_GetCurThread();
1040 cur->bInstrTrace = 0;
1041 Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr);
1042 MM_DumpTables(0, USER_MAX);
1044 //Threads_Exit( 0, -1 );
1048 void Threads_PostSignal(int SignalNum)
1050 tThread *cur = Proc_GetCurThread();
1051 cur->PendingSignal = SignalNum;
1052 Threads_PostEvent(cur, THREAD_EVENT_SIGNAL);
1057 int Threads_GetPendingSignal(void)
1059 tThread *cur = Proc_GetCurThread();
1061 // Atomic AND with 0 fetches and clears in one operation
1062 return __sync_fetch_and_and( &cur->PendingSignal, 0 );
1066 * \brief Update the current thread's signal handler
1068 void Threads_SetSignalHandler(int SignalNum, void *Handler)
1070 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1072 if( !MM_IsUser(Handler) )
1074 Proc_GetCurThread()->Process->SignalHandlers[SignalNum] = Handler;
1080 void *Threads_GetSignalHandler(int SignalNum)
1082 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1084 void *ret = Proc_GetCurThread()->Process->SignalHandlers[SignalNum];
1093 // ret = User_Signal_Kill;
1103 // --- Process Structure Access Functions ---
1104 tPGID Threads_GetPGID(void)
1106 return Proc_GetCurThread()->Process->PGID;
1108 tPID Threads_GetPID(void)
1110 return Proc_GetCurThread()->Process->PID;
1112 tTID Threads_GetTID(void)
1114 return Proc_GetCurThread()->TID;
1116 tUID Threads_GetUID(void)
1118 return Proc_GetCurThread()->Process->UID;
1120 tGID Threads_GetGID(void)
1122 return Proc_GetCurThread()->Process->GID;
1125 int Threads_SetUID(tUID ID)
1127 tThread *t = Proc_GetCurThread();
1128 if( t->Process->UID != 0 ) {
1132 Log_Debug("Threads", "PID %i's UID set to %i", t->Process->PID, ID);
1133 t->Process->UID = ID;
1137 int Threads_SetGID(tGID ID)
1139 tThread *t = Proc_GetCurThread();
1140 if( t->Process->UID != 0 ) {
1144 Log_Debug("Threads", "PID %i's GID set to %i", t->Process->PID, ID);
1145 t->Process->GID = ID;
1149 // --- Per-thread storage ---
1150 int *Threads_GetErrno(void)
1152 return &Proc_GetCurThread()->_errno;
1155 // --- Configuration ---
1156 int *Threads_GetMaxFD(void)
1158 return &Proc_GetCurThread()->Process->MaxFD;
1160 char **Threads_GetChroot(void)
1162 return &Proc_GetCurThread()->Process->RootDir;
1164 char **Threads_GetCWD(void)
1166 return &Proc_GetCurThread()->Process->CurrentWorkingDir;
1170 void Threads_int_DumpThread(tThread *thread)
1173 Log(" %p NULL", thread);
1176 if( !CheckMem(thread, sizeof(tThread)) ) {
1177 Log(" %p INVAL", thread);
1180 tPID pid = (thread->Process ? thread->Process->PID : -1);
1181 const char *statstr = (thread->Status < sizeof(casTHREAD_STAT)/sizeof(casTHREAD_STAT[0])
1182 ? casTHREAD_STAT[thread->Status] : "");
1183 Log(" %p %i (%i) - %s (CPU %i) - %i (%s)",
1184 thread, thread->TID, pid, thread->ThreadName, thread->CurCPU,
1185 thread->Status, statstr
1187 switch(thread->Status)
1189 case THREAD_STAT_MUTEXSLEEP:
1190 Log(" Mutex Pointer: %p", thread->WaitPointer);
1192 case THREAD_STAT_SEMAPHORESLEEP:
1193 Log(" Semaphore Pointer: %p", thread->WaitPointer);
1194 Log(" Semaphore Name: %s:%s",
1195 ((tSemaphore*)thread->WaitPointer)->ModName,
1196 ((tSemaphore*)thread->WaitPointer)->Name
1199 case THREAD_STAT_EVENTSLEEP:
1202 case THREAD_STAT_ZOMBIE:
1203 Log(" Return Status: %i", thread->RetStatus);
1207 Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
1208 Log(" KStack %p", thread->KernelStack);
1209 if( thread->bInstrTrace )
1210 Log(" Tracing Enabled");
1211 Proc_DumpThreadCPUState(thread);
1215 * \fn void Threads_Dump(void)
1217 void Threads_DumpActive(void)
1221 #if SCHEDULER_TYPE == SCHED_RR_PRI
1225 Log("Active Threads: (%i reported)", giNumActiveThreads);
1227 #if SCHEDULER_TYPE == SCHED_RR_PRI
1228 for( i = 0; i < MIN_PRIORITY+1; i++ )
1230 list = &gaActiveThreads[i];
1232 list = &gActiveThreads;
1234 for(thread=list->Head;thread;thread=thread->Next)
1236 Threads_int_DumpThread(thread);
1237 if(thread->Status != THREAD_STAT_ACTIVE)
1238 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)",
1239 thread->Status, THREAD_STAT_ACTIVE);
1242 #if SCHEDULER_TYPE == SCHED_RR_PRI
1248 * \fn void Threads_Dump(void)
1249 * \brief Dumps a list of currently running threads
1251 void Threads_Dump(void)
1253 Log("--- Thread Dump ---");
1254 Threads_DumpActive();
1256 Log("All Threads:");
1257 for(tThread *thread = gAllThreads; thread; thread = thread->GlobalNext)
1259 Threads_int_DumpThread(thread);
1264 * \brief Gets the next thread to run
1265 * \param CPU Current CPU
1266 * \param Last The thread the CPU was running
1268 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
1272 // If this CPU has the lock, we must let it complete
1273 if( CPU_HAS_LOCK( &glThreadListLock ) )
1276 // Don't change threads if the current CPU has switches disabled
1277 if( gaThreads_NoTaskSwitch[CPU] )
1281 SHORTLOCK( &glThreadListLock );
1283 // Make sure the current (well, old) thread is marked as de-scheduled
1284 if(Last) Last->CurCPU = -1;
1286 // No active threads, just take a nap
1287 if(giNumActiveThreads == 0) {
1288 SHORTREL( &glThreadListLock );
1289 #if DEBUG_TRACE_TICKETS
1290 Log("No active threads");
1296 #if SCHEDULER_TYPE != SCHED_RR_PRI
1297 // Special case: 1 thread
1298 if(giNumActiveThreads == 1) {
1299 if( gActiveThreads.Head->CurCPU == -1 )
1300 gActiveThreads.Head->CurCPU = CPU;
1302 SHORTREL( &glThreadListLock );
1304 if( gActiveThreads.Head->CurCPU == CPU )
1305 return gActiveThreads.Head;
1307 return NULL; // CPU has nothing to do
1312 // Allow the old thread to be scheduled again
1314 if( Last->Status == THREAD_STAT_ACTIVE ) {
1316 #if SCHEDULER_TYPE == SCHED_LOTTERY
1317 giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
1318 # if DEBUG_TRACE_TICKETS
1319 LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n",
1320 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets,
1321 caiTICKET_COUNTS[ Last->Priority ]);
1325 #if SCHEDULER_TYPE == SCHED_RR_PRI
1326 list = &gaActiveThreads[ Last->Priority ];
1328 list = &gActiveThreads;
1330 // Add to end of list
1331 Threads_int_AddToList( list, Last );
1333 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
1335 LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n",
1336 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
1342 // Lottery Scheduler
1344 #if SCHEDULER_TYPE == SCHED_LOTTERY
1349 for(thread = gActiveThreads.Head; thread; thread = thread->Next)
1351 if(thread->Status != THREAD_STAT_ACTIVE)
1352 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
1353 thread, thread->TID, thread->ThreadName, thread->Status);
1354 if(thread->Next == thread) {
1355 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
1356 thread, thread->TID, thread->ThreadName, thread->Status);
1358 number += caiTICKET_COUNTS[ thread->Priority ];
1360 if(number != giFreeTickets) {
1361 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
1362 giFreeTickets, number, CPU);
1366 // No free tickets (all tasks delegated to cores)
1367 if( giFreeTickets == 0 ) {
1368 SHORTREL(&glThreadListLock);
1372 // Get the ticket number
1373 ticket = number = rand() % giFreeTickets;
1375 // Find the next thread
1376 for(thread = gActiveThreads.Head; thread; prev = thread, thread = thread->Next )
1378 if( caiTICKET_COUNTS[ thread->Priority ] > number) break;
1379 number -= caiTICKET_COUNTS[ thread->Priority ];
1382 // If we didn't find a thread, something went wrong
1386 for(thread=gActiveThreads;thread;thread=thread->Next) {
1387 if(thread->CurCPU >= 0) continue;
1388 number += caiTICKET_COUNTS[ thread->Priority ];
1390 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
1391 giFreeTickets, number);
1396 prev->Next = thread->Next;
1398 gActiveThreads.Head = thread->Next;
1400 gActiveThreads.Tail = prev;
1402 giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
1403 # if DEBUG_TRACE_TICKETS
1404 LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n",
1405 CPU, thread, thread->TID, thread->ThreadName,
1406 giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]);
1411 // Priority based round robin scheduler
1413 #elif SCHEDULER_TYPE == SCHED_RR_PRI
1417 for( i = 0; i < MIN_PRIORITY + 1; i ++ )
1419 if( !gaActiveThreads[i].Head )
1422 thread = gaActiveThreads[i].Head;
1425 gaActiveThreads[i].Head = thread->Next;
1427 gaActiveThreads[i].Tail = NULL;
1428 thread->Next = NULL;
1434 SHORTREL(&glThreadListLock);
1437 if( thread->Status != THREAD_STAT_ACTIVE ) {
1438 LogF("Oops, Thread %i (%s) is not active\n", thread->TID, thread->ThreadName);
1441 #elif SCHEDULER_TYPE == SCHED_RR_SIM
1443 // Get the next thread off the list
1444 thread = gActiveThreads.Head;
1445 gActiveThreads.Head = thread->Next;
1447 gaActiveThreads.Tail = NULL;
1448 thread->Next = NULL;
1452 SHORTREL(&glThreadListLock);
1457 # error "Unimplemented scheduling algorithm"
1460 // Make the new thread non-schedulable
1461 thread->CurCPU = CPU;
1462 thread->Remaining = thread->Quantum;
1464 SHORTREL( &glThreadListLock );
1470 EXPORT(Threads_GetUID);
1471 EXPORT(Threads_GetGID);