3 * - By John Hodge (thePowersGang)
5 * - Common Thread Control
9 #include <threads_int.h>
12 #include <semaphore.h>
14 #include <vfs_threads.h> // VFS Handle maintainence
18 #define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
19 #define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
23 #define SCHED_LOTTERY 1 // Lottery scheduler
24 #define SCHED_RR_SIM 2 // Single Queue Round Robin
25 #define SCHED_RR_PRI 3 // Multi Queue Round Robin
27 #define SCHEDULER_TYPE SCHED_RR_PRI
30 #define DEFAULT_QUANTUM 5
31 #define DEFAULT_PRIORITY 5
32 #define MIN_PRIORITY 10
35 extern void User_Signal_Kill(int SigNum);
45 void Threads_Init(void);
47 void Threads_Delete(tThread *Thread);
48 int Threads_SetName(const char *NewName);
50 char *Threads_GetName(tTID ID);
52 void Threads_SetPriority(tThread *Thread, int Pri);
53 tThread *Threads_CloneTCB(Uint Flags);
54 int Threads_WaitTID(int TID, int *status);
55 tThread *Threads_GetThread(Uint TID);
57 tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread);
58 void Threads_int_AddToList(tThreadList *List, tThread *Thread);
60 void Threads_Exit(int TID, int Status);
61 void Threads_Kill(tThread *Thread, int Status);
62 void Threads_Yield(void);
63 int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock);
64 void Threads_Sleep(void);
65 int Threads_Wake(tThread *Thread);
66 void Threads_AddActive(tThread *Thread);
67 tThread *Threads_RemActive(void);
69 void Threads_ToggleTrace(int TID);
70 void Threads_Fault(int Num);
71 void Threads_SegFault(tVAddr Addr);
72 void Threads_PostSignalTo(tThread *Thread, int SignalNum);
74 void Threads_PostSignal(int SignalNum);
75 void Threads_SignalGroup(tPGID PGID, int SignalNum);
77 int Threads_GetPendingSignal(void);
78 void Threads_SetSignalHandler(int SignalNum, void *Handler);
79 void *Threads_GetSignalHandler(int SignalNum);
81 int Threads_GetPID(void);
82 int Threads_GetTID(void);
83 tUID Threads_GetUID(void);
84 tGID Threads_GetGID(void);
85 int Threads_SetUID(Uint *Errno, tUID ID);
86 int Threads_SetGID(Uint *Errno, tUID ID);
88 void Threads_int_DumpThread(tThread *thread);
89 void Threads_Dump(void);
90 void Threads_DumpActive(void);
94 tProcess gProcessZero = {
96 // Only used for the core kernel
97 tThread gThreadZero = {
98 .Status = THREAD_STAT_ACTIVE, // Status
99 .ThreadName = (char*)"ThreadZero", // Name
100 .Quantum = DEFAULT_QUANTUM, // Default Quantum
101 .Remaining = DEFAULT_QUANTUM, // Current Quantum
102 .Priority = DEFAULT_PRIORITY // Number of tickets
105 tProcess *gAllProcesses = &gProcessZero;
107 tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
108 // --- Current State ---
109 volatile int giNumActiveThreads = 0; // Number of threads on the active queue
110 volatile Uint giNextTID = 1; // Next TID to allocate
111 // --- Thread Lists ---
112 tThread *gAllThreads = NULL; // All allocated threads
113 tThreadList gSleepingThreads; // Sleeping Threads
114 int giNumCPUs = 1; // Number of CPUs
115 BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF)
116 // --- Scheduler Types ---
117 #if SCHEDULER_TYPE == SCHED_LOTTERY
118 const int caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0};
119 volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads
120 tThreadList gActiveThreads; // Currently Running Threads
121 #elif SCHEDULER_TYPE == SCHED_RR_SIM
122 tThreadList gActiveThreads; // Currently Running Threads
123 #elif SCHEDULER_TYPE == SCHED_RR_PRI
124 tThreadList gaActiveThreads[MIN_PRIORITY+1]; // Active threads for each priority level
126 # error "Unkown scheduler type"
131 * \fn void Threads_Init(void)
132 * \brief Initialse the thread list
134 void Threads_Init(void)
138 Log_Debug("Threads", "Offsets of tThread");
139 Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority));
140 Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack));
142 // Create Initial Task
143 gAllThreads = &gThreadZero;
144 giNumActiveThreads = 1;
145 gThreadZero.Process = &gProcessZero;
150 void Threads_Delete(tThread *Thread)
153 Thread->Status = THREAD_STAT_BURIED;
155 // Clear out process state
156 Proc_ClearThread(Thread);
158 Thread->Process->nThreads --;
160 if( Thread->Process->FirstThread == Thread )
162 Thread->Process->FirstThread = Thread->ProcessNext;
166 tThread *prev = Thread->Process->FirstThread;
167 while(prev && prev->ProcessNext != Thread)
168 prev = prev->ProcessNext;
170 Log_Error("Threads", "Thread %p(%i %s) is not on the process's list",
171 Thread, Thread->TID, Thread->ThreadName
174 prev->ProcessNext = Thread->ProcessNext;
177 // If the final thread is being terminated, clean up the process
178 if( Thread->Process->nThreads == 0 )
180 tProcess *proc = Thread->Process;
182 // Remove from global process list
185 proc->Prev->Next = proc->Next;
187 gAllProcesses = proc->Next;
189 proc->Next->Prev = proc->Prev;
192 VFS_CloseAllUserHandles();
193 // Architecture cleanup
194 Proc_ClearProcess( proc );
195 // VFS Configuration strings
196 if( proc->CurrentWorkingDir)
197 free( proc->CurrentWorkingDir );
199 free( proc->RootDir );
200 // Process descriptor
205 if( IsHeap(Thread->ThreadName) )
206 free(Thread->ThreadName);
208 // Remove from global list
209 // TODO: Lock this too
210 if( Thread == gAllThreads )
211 gAllThreads = Thread->GlobalNext;
213 Thread->GlobalPrev->GlobalNext = Thread->GlobalNext;
219 * \fn void Threads_SetName(const char *NewName)
220 * \brief Sets the current thread's name
221 * \param NewName New name for the thread
222 * \return Boolean Failure
224 int Threads_SetName(const char *NewName)
226 tThread *cur = Proc_GetCurThread();
227 char *oldname = cur->ThreadName;
229 // NOTE: There is a possibility of non-thread safety here
230 // A thread could read the current name pointer before it is zeroed
232 cur->ThreadName = NULL;
234 if( IsHeap(oldname) ) free( oldname );
235 cur->ThreadName = strdup(NewName);
237 Log_Debug("Threads", "Thread renamed to '%s'", NewName);
243 * \fn char *Threads_GetName(int ID)
244 * \brief Gets a thread's name
245 * \param ID Thread ID (-1 indicates current thread)
246 * \return Pointer to name
247 * \retval NULL Failure
249 char *Threads_GetName(tTID ID)
252 return Proc_GetCurThread()->ThreadName;
254 return Threads_GetThread(ID)->ThreadName;
258 * \fn void Threads_SetPriority(tThread *Thread, int Pri)
259 * \brief Sets the priority of a task
260 * \param Thread Thread to update ticket count (NULL means current thread)
261 * \param Pri New priority
263 void Threads_SetPriority(tThread *Thread, int Pri)
265 // Get current thread
266 if(Thread == NULL) Thread = Proc_GetCurThread();
268 // - If < 0, set to lowest priority
269 // - Minumum priority is actualy a high number, 0 is highest
270 if(Pri < 0) Pri = MIN_PRIORITY;
271 if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY;
273 // Do we actually have to do anything?
274 if( Pri == Thread->Priority ) return;
276 #if SCHEDULER_TYPE == SCHED_RR_PRI
277 if( Thread != Proc_GetCurThread() )
279 SHORTLOCK( &glThreadListLock );
280 // Remove from old priority
281 Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread );
283 Threads_int_AddToList( &gaActiveThreads[Pri], Thread );
284 Thread->Priority = Pri;
285 SHORTREL( &glThreadListLock );
288 Thread->Priority = Pri;
290 // If this isn't the current thread, we need to lock
291 if( Thread != Proc_GetCurThread() )
293 SHORTLOCK( &glThreadListLock );
295 #if SCHEDULER_TYPE == SCHED_LOTTERY
296 giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri];
297 # if DEBUG_TRACE_TICKETS
298 Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]",
300 caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]);
303 Thread->Priority = Pri;
304 SHORTREL( &glThreadListLock );
307 Thread->Priority = Pri;
310 #if DEBUG_TRACE_STATE
311 Log("Threads_SetPriority: %p(%i %s) pri set %i",
312 Thread, Thread->TID, Thread->ThreadName,
318 * \brief Clone the TCB of the current thread
319 * \param Flags Flags for something... (What is this for?)
321 tThread *Threads_CloneTCB(Uint Flags)
324 cur = Proc_GetCurThread();
326 // Allocate and duplicate
327 new = malloc(sizeof(tThread));
328 if(new == NULL) { errno = -ENOMEM; return NULL; }
329 memcpy(new, cur, sizeof(tThread));
333 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
334 new->Status = THREAD_STAT_PREINIT;
338 new->TID = giNextTID++;
340 new->bInstrTrace = 0;
343 new->ThreadName = strdup(cur->ThreadName);
345 // Set Thread Group ID (PID)
346 if(Flags & CLONE_VM) {
347 tProcess *newproc, *oldproc;
348 oldproc = cur->Process;
349 new->Process = malloc( sizeof(struct sProcess) );
350 newproc = new->Process;
351 newproc->PID = new->TID;
352 if( Flags & CLONE_PGID )
353 newproc->PGID = oldproc->PGID;
355 newproc->PGID = newproc->PID;
356 newproc->UID = oldproc->UID;
357 newproc->GID = oldproc->GID;
358 newproc->MaxFD = oldproc->MaxFD;
359 newproc->CurrentWorkingDir = oldproc->CurrentWorkingDir ? strdup( oldproc->CurrentWorkingDir ) : NULL;
360 newproc->RootDir = oldproc->RootDir ? strdup( oldproc->RootDir ) : NULL;
361 newproc->nThreads = 1;
362 // Reference all handles in the VFS
363 VFS_ReferenceUserHandles();
365 // Add to global list
366 newproc->Prev = NULL;
368 newproc->Next = gAllProcesses;
369 gAllProcesses = newproc;
371 newproc->FirstThread = new;
372 new->ProcessNext = NULL;
375 new->Process->nThreads ++;
376 new->Process = cur->Process;
378 new->ProcessNext = new->Process->FirstThread;
379 new->Process->FirstThread = new;
382 // Messages are not inherited
383 new->Messages = NULL;
384 new->LastMessage = NULL;
387 new->Remaining = new->Quantum = cur->Quantum;
388 new->Priority = cur->Priority;
391 // Set Signal Handlers
392 new->CurFaultNum = 0;
393 new->FaultHandler = cur->FaultHandler;
395 // Maintain a global list of threads
396 SHORTLOCK( &glThreadListLock );
397 new->GlobalPrev = NULL; // Protect against bugs
398 new->GlobalNext = gAllThreads;
399 gAllThreads->GlobalPrev = new;
401 SHORTREL( &glThreadListLock );
407 * \brief Clone the TCB of the kernel thread
409 tThread *Threads_CloneThreadZero(void)
413 // Allocate and duplicate
414 new = malloc(sizeof(tThread));
418 memcpy(new, &gThreadZero, sizeof(tThread));
420 new->Process->nThreads ++;
424 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
425 new->Status = THREAD_STAT_PREINIT;
429 new->TID = giNextTID++;
433 new->ThreadName = NULL;
435 // Messages are not inherited
436 new->Messages = NULL;
437 new->LastMessage = NULL;
440 new->Remaining = new->Quantum = DEFAULT_QUANTUM;
441 new->Priority = DEFAULT_PRIORITY;
442 new->bInstrTrace = 0;
444 // Set Signal Handlers
445 new->CurFaultNum = 0;
446 new->FaultHandler = 0;
448 // Maintain a global list of threads
449 SHORTLOCK( &glThreadListLock );
450 new->GlobalPrev = NULL; // Protect against bugs
451 new->GlobalNext = gAllThreads;
452 gAllThreads->GlobalPrev = new;
454 SHORTREL( &glThreadListLock );
460 * \brief Wait for a task to change state
461 * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
462 * \param Status Thread return status
463 * \return TID of child that changed state
465 tTID Threads_WaitTID(int TID, int *Status)
470 Uint32 ev = Threads_WaitEvents(THREAD_EVENT_DEADCHILD);
472 if( ev & THREAD_EVENT_DEADCHILD )
474 // A child died, get the TID
475 tThread *us = Proc_GetCurThread();
476 ASSERT(us->LastDeadChild);
477 ret = us->LastDeadChild->TID;
478 // - Mark as dead (as opposed to undead)
479 ASSERT(us->LastDeadChild->Status == THREAD_STAT_ZOMBIE);
480 us->LastDeadChild->Status = THREAD_STAT_DEAD;
481 // - Set return status
483 *Status = us->LastDeadChild->RetStatus;
484 us->LastDeadChild = NULL;
485 Mutex_Release(&us->DeadChildLock);
489 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
494 // Any peer/child thread
496 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
502 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
509 // TODO: Register on thread to be poked when it dies
511 // NOTE: Race condition - Other child dies, desired child dies, first death is 'lost'
512 while( (ret = Threads_WaitTID(-1, Status)) != TID )
524 * \brief Gets a thread given its TID
525 * \param TID Thread ID
526 * \return Thread pointer
528 tThread *Threads_GetThread(Uint TID)
532 // Search global list
533 for( thread = gAllThreads; thread; thread = thread->GlobalNext )
535 if(thread->TID == TID)
539 Log_Notice("Threads", "Unable to find TID %i on main list\n", TID);
545 * \brief Deletes an entry from a list
546 * \param List Pointer to the list head
547 * \param Thread Thread to find
550 tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread)
552 tThread *ret, *prev = NULL;
554 for(ret = List->Head;
555 ret && ret != Thread;
556 prev = ret, ret = ret->Next
559 // Is the thread on the list
561 //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
566 List->Head = Thread->Next;
567 //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
570 prev->Next = Thread->Next;
571 //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
573 if( Thread->Next == NULL )
579 void Threads_int_AddToList(tThreadList *List, tThread *Thread)
582 List->Tail->Next = Thread;
590 * \brief Exit the current process (or another?)
591 * \param TID Thread ID to kill
592 * \param Status Exit status
594 void Threads_Exit(int TID, int Status)
597 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
599 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
601 // Halt forever, just in case
606 * \fn void Threads_Kill(tThread *Thread, int Status)
607 * \brief Kill a thread
608 * \param Thread Thread to kill
609 * \param Status Status code to return to the parent
611 void Threads_Kill(tThread *Thread, int Status)
614 int isCurThread = Thread == Proc_GetCurThread();
616 // TODO: Disown all children?
620 // TODO: I should keep a .Children list
621 for(child = gAllThreads;
623 child = child->GlobalNext)
625 if(child->Parent == Thread)
626 child->Parent = &gThreadZero;
631 ///\note Double lock is needed due to overlap of lock areas
633 // Lock thread (stop us recieving messages)
634 SHORTLOCK( &Thread->IsLocked );
636 // Clear Message Queue
637 while( Thread->Messages )
639 msg = Thread->Messages->Next;
640 free( Thread->Messages );
641 Thread->Messages = msg;
645 SHORTLOCK( &glThreadListLock );
647 switch(Thread->Status)
649 case THREAD_STAT_PREINIT: // Only on main list
652 // Currently active thread
653 case THREAD_STAT_ACTIVE:
654 if( Thread != Proc_GetCurThread() )
656 #if SCHEDULER_TYPE == SCHED_RR_PRI
657 tThreadList *list = &gaActiveThreads[Thread->Priority];
659 tThreadList *list = &gActiveThreads;
661 if( Threads_int_DelFromQueue( list, Thread ) )
666 Log_Warning("Threads",
667 "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list",
668 Thread, Thread->TID, Thread->ThreadName
671 #if SCHEDULER_TYPE == SCHED_LOTTERY
672 giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ];
675 // Ensure that we are not rescheduled
676 Thread->Remaining = 0; // Clear Remaining Quantum
677 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
679 // Update bookkeeping
680 giNumActiveThreads --;
682 // Kill it while it sleeps!
683 case THREAD_STAT_SLEEPING:
684 if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) )
686 Log_Warning("Threads",
687 "Threads_Kill - Thread %p(%i,%s) marked as sleeping, but not on list",
688 Thread, Thread->TID, Thread->ThreadName
693 // Brains!... You cannot kill something that is already dead
694 case THREAD_STAT_ZOMBIE:
695 Log_Warning("Threads", "Threads_Kill - Thread %p(%i,%s) is undead, you cannot kill it",
696 Thread, Thread->TID, Thread->ThreadName);
697 SHORTREL( &glThreadListLock );
698 SHORTREL( &Thread->IsLocked );
702 Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)",
708 Thread->RetStatus = Status;
710 SHORTREL( &Thread->IsLocked );
712 Thread->Status = THREAD_STAT_ZOMBIE;
713 SHORTREL( &glThreadListLock );
714 // TODO: It's possible that we could be timer-preempted here, should disable that... somehow
715 Mutex_Acquire( &Thread->Parent->DeadChildLock ); // released by parent
716 Thread->Parent->LastDeadChild = Thread;
717 Threads_PostEvent( Thread->Parent, THREAD_EVENT_DEADCHILD );
719 Log("Thread %i went *hurk* (%i)", Thread->TID, Status);
730 * \brief Yield remainder of the current thread's timeslice
732 void Threads_Yield(void)
734 // Log("Threads_Yield: by %p", __builtin_return_address(0));
739 * \breif Wait for the thread status to not be a specified value
741 void Threads_int_WaitForStatusEnd(enum eThreadStatus Status)
743 tThread *us = Proc_GetCurThread();
744 ASSERT(Status != THREAD_STAT_ACTIVE);
745 ASSERT(Status != THREAD_STAT_DEAD);
746 while( us->Status == Status )
749 if( us->Status == Status )
750 Debug("Thread %p(%i %s) rescheduled while in %s state for %p",
751 us, us->TID, us->ThreadName,
752 casTHREAD_STAT[Status],
753 __builtin_return_address(0));
757 int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock)
759 SHORTLOCK( &glThreadListLock );
760 tThread *us = Threads_RemActive();
762 // - Mark as sleeping
764 us->WaitPointer = Ptr;
765 us->RetStatus = Num; // Use RetStatus as a temp variable
770 (*ListTail)->Next = us;
781 //if( Proc_ThreadSync(us) )
783 SHORTREL( &glThreadListLock );
786 Threads_int_WaitForStatusEnd(Status);
787 us->WaitPointer = NULL;
788 return us->RetStatus;
792 * \fn void Threads_Sleep(void)
793 * \brief Take the current process off the run queue
795 void Threads_Sleep(void)
797 tThread *cur = Proc_GetCurThread();
800 SHORTLOCK( &glThreadListLock );
802 // Don't sleep if there is a message waiting
803 if( cur->Messages ) {
804 SHORTREL( &glThreadListLock );
808 // Remove us from running queue
810 // Mark thread as sleeping
811 cur->Status = THREAD_STAT_SLEEPING;
813 // Add to Sleeping List (at the top)
814 Threads_int_AddToList( &gSleepingThreads, cur );
816 #if DEBUG_TRACE_STATE
817 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
821 SHORTREL( &glThreadListLock );
822 Threads_int_WaitForStatusEnd(THREAD_STAT_SLEEPING);
827 * \brief Wakes a sleeping/waiting thread up
828 * \param Thread Thread to wake
829 * \return Boolean Failure (Returns ERRNO)
830 * \warning This should ONLY be called with task switches disabled
832 int Threads_Wake(tThread *Thread)
837 switch(Thread->Status)
839 case THREAD_STAT_ACTIVE:
840 Log("Threads_Wake - Waking awake thread (%i)", Thread->TID);
843 case THREAD_STAT_SLEEPING:
844 // Remove from sleeping queue
845 SHORTLOCK( &glThreadListLock );
846 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
847 SHORTREL( &glThreadListLock );
849 Threads_AddActive( Thread );
851 #if DEBUG_TRACE_STATE
852 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
856 case THREAD_STAT_SEMAPHORESLEEP: {
858 tThread *th, *prev=NULL;
860 sem = Thread->WaitPointer;
862 SHORTLOCK( &sem->Protector );
864 // Remove from sleeping queue
865 for( th = sem->Waiting; th; prev = th, th = th->Next )
866 if( th == Thread ) break;
870 prev->Next = Thread->Next;
872 sem->Waiting = Thread->Next;
873 if(sem->LastWaiting == Thread)
874 sem->LastWaiting = prev;
879 for( th = sem->Signaling; th; prev = th, th = th->Next )
880 if( th == Thread ) break;
882 Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)",
883 Thread, Thread->TID, Thread->ThreadName,
884 sem, sem->ModName, sem->Name);
889 prev->Next = Thread->Next;
891 sem->Signaling = Thread->Next;
892 if(sem->LastSignaling == Thread)
893 sem->LastSignaling = prev;
896 Thread->RetStatus = 0; // It didn't get anything
897 Threads_AddActive( Thread );
899 #if DEBUG_TRACE_STATE
900 Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName);
902 SHORTREL( &sem->Protector );
905 case THREAD_STAT_WAITING:
906 Warning("Threads_Wake - Waiting threads are not currently supported");
909 case THREAD_STAT_DEAD:
910 Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID);
914 Log_Warning("Threads", "Threads_Wake - Unknown process status (%i)", Thread->Status);
920 * \brief Wake a thread given the TID
921 * \param TID Thread ID to wake
922 * \return Boolean Faulure (errno)
924 int Threads_WakeTID(tTID TID)
926 tThread *thread = Threads_GetThread(TID);
930 ret = Threads_Wake( thread );
931 //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
935 void Threads_ToggleTrace(int TID)
937 tThread *thread = Threads_GetThread(TID);
939 thread->bInstrTrace = !thread->bInstrTrace;
943 * \brief Adds a thread to the active queue
945 void Threads_AddActive(tThread *Thread)
947 SHORTLOCK( &glThreadListLock );
949 if( Thread->Status == THREAD_STAT_ACTIVE ) {
950 tThread *cur = Proc_GetCurThread();
951 Log_Warning("Threads", "WTF, %p CPU%i %p (%i %s) is adding %p (%i %s) when it is active",
952 __builtin_return_address(0),
953 GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName);
954 SHORTREL( &glThreadListLock );
959 Thread->Status = THREAD_STAT_ACTIVE;
960 // Thread->CurCPU = -1;
961 // Add to active list
963 #if SCHEDULER_TYPE == SCHED_RR_PRI
964 tThreadList *list = &gaActiveThreads[Thread->Priority];
966 tThreadList *list = &gActiveThreads;
968 Threads_int_AddToList( list, Thread );
971 // Update bookkeeping
972 giNumActiveThreads ++;
974 #if SCHEDULER_TYPE == SCHED_LOTTERY
977 // Only change the ticket count if the thread is un-scheduled
978 if(Thread->CurCPU != -1)
981 delta = caiTICKET_COUNTS[ Thread->Priority ];
983 giFreeTickets += delta;
984 # if DEBUG_TRACE_TICKETS
985 Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]",
986 GetCPUNum(), Thread, Thread->TID, Thread->ThreadName,
993 SHORTREL( &glThreadListLock );
997 * \brief Removes the current thread from the active queue
998 * \warning This should ONLY be called with the lock held
999 * \return Current thread pointer
1001 tThread *Threads_RemActive(void)
1003 giNumActiveThreads --;
1004 return Proc_GetCurThread();
1008 * \fn void Threads_SetFaultHandler(Uint Handler)
1009 * \brief Sets the signal handler for a signal
1011 void Threads_SetFaultHandler(Uint Handler)
1013 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
1014 Proc_GetCurThread()->FaultHandler = Handler;
1018 * \fn void Threads_Fault(int Num)
1019 * \brief Calls a fault handler
1021 void Threads_Fault(int Num)
1023 tThread *thread = Proc_GetCurThread();
1025 if(!thread) return ;
1027 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
1029 switch(thread->FaultHandler)
1032 Threads_Kill(thread, -1);
1035 case 1: // Dump Core?
1036 Threads_Kill(thread, -1);
1041 // Double Fault? Oh, F**k
1042 if(thread->CurFaultNum != 0) {
1043 Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID);
1044 Threads_Kill(thread, -1); // For now, just kill
1048 thread->CurFaultNum = Num;
1050 Proc_CallFaultHandler(thread);
1054 * \fn void Threads_SegFault(tVAddr Addr)
1055 * \brief Called when a Segment Fault occurs
1057 void Threads_SegFault(tVAddr Addr)
1059 tThread *cur = Proc_GetCurThread();
1060 cur->bInstrTrace = 0;
1061 Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr);
1062 MM_DumpTables(0, USER_MAX);
1064 //Threads_Exit( 0, -1 );
1068 void Threads_PostSignalTo(tThread *Thread, int SignalNum)
1071 Log_Debug("Threads", "Signalling %i(%s) with %i", Thread->TID, Thread->ThreadName, SignalNum);
1072 Thread->PendingSignal = SignalNum;
1073 Threads_PostEvent(Thread, THREAD_EVENT_SIGNAL);
1075 void Threads_PostSignal(int SignalNum)
1077 Threads_PostSignalTo( Proc_GetCurThread(), SignalNum );
1080 void Threads_SignalGroup(tPGID PGID, int Signal)
1082 for( tProcess *proc = gAllProcesses; proc; proc = proc->Next )
1084 if(proc->PGID == PGID)
1086 Threads_PostSignalTo(proc->FirstThread, Signal);
1093 int Threads_GetPendingSignal(void)
1095 tThread *cur = Proc_GetCurThread();
1097 // Atomic AND with 0 fetches and clears in one operation
1098 int ret = __sync_fetch_and_and( &cur->PendingSignal, 0 );
1101 Log_Debug("Threads", "Thread %i(%s) has signal %i pending",
1102 cur->TID, cur->ThreadName, ret);
1108 * \brief Update the current thread's signal handler
1110 void Threads_SetSignalHandler(int SignalNum, void *Handler)
1112 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1114 if( !MM_IsUser(Handler) )
1116 Proc_GetCurThread()->Process->SignalHandlers[SignalNum] = Handler;
1120 * \brief Gets the registered (or default, if none set) handler for a signal.
1121 * \return Handler function pointer, OR NULL if no signal to be ignored
1123 void *Threads_GetSignalHandler(int SignalNum)
1126 void *User_Signal_Core = User_Signal_Kill;
1128 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1130 void *ret = Proc_GetCurThread()->Process->SignalHandlers[SignalNum];
1131 if( !ret || (SignalNum == SIGKILL || SignalNum == SIGSTOP) )
1138 ret = User_Signal_Kill;
1144 ret = User_Signal_Core;
1147 ret = User_Signal_Kill;
1150 ret = User_Signal_Core;
1155 ret = User_Signal_Kill;
1162 Log_Debug("Threads", "Handler %p for signal %i", ret, SignalNum);
1166 // --- Process Structure Access Functions ---
1167 tPGID Threads_GetPGID(void)
1169 return Proc_GetCurThread()->Process->PGID;
1171 tPID Threads_GetPID(void)
1173 return Proc_GetCurThread()->Process->PID;
1175 tTID Threads_GetTID(void)
1177 return Proc_GetCurThread()->TID;
1179 tUID Threads_GetUID(void)
1181 return Proc_GetCurThread()->Process->UID;
1183 tGID Threads_GetGID(void)
1185 return Proc_GetCurThread()->Process->GID;
1188 int Threads_SetUID(tUID ID)
1190 tThread *t = Proc_GetCurThread();
1191 if( t->Process->UID != 0 ) {
1195 Log_Debug("Threads", "PID %i's UID set to %i", t->Process->PID, ID);
1196 t->Process->UID = ID;
1200 int Threads_SetGID(tGID ID)
1202 tThread *t = Proc_GetCurThread();
1203 if( t->Process->UID != 0 ) {
1207 Log_Debug("Threads", "PID %i's GID set to %i", t->Process->PID, ID);
1208 t->Process->GID = ID;
1212 // --- Per-thread storage ---
1213 int *Threads_GetErrno(void)
1215 return &Proc_GetCurThread()->_errno;
1218 // --- Configuration ---
1219 int *Threads_GetMaxFD(void)
1221 return &Proc_GetCurThread()->Process->MaxFD;
1223 char **Threads_GetChroot(void)
1225 return &Proc_GetCurThread()->Process->RootDir;
1227 char **Threads_GetCWD(void)
1229 return &Proc_GetCurThread()->Process->CurrentWorkingDir;
1233 void Threads_int_DumpThread(tThread *thread)
1236 Log(" %p NULL", thread);
1239 if( !CheckMem(thread, sizeof(tThread)) ) {
1240 Log(" %p INVAL", thread);
1243 tPID pid = (thread->Process ? thread->Process->PID : -1);
1244 const char *statstr = (thread->Status < sizeof(casTHREAD_STAT)/sizeof(casTHREAD_STAT[0])
1245 ? casTHREAD_STAT[thread->Status] : "");
1246 Log(" %p %i (%i) - %s (CPU %i) - %i (%s)",
1247 thread, thread->TID, pid, thread->ThreadName, thread->CurCPU,
1248 thread->Status, statstr
1250 switch(thread->Status)
1252 case THREAD_STAT_MUTEXSLEEP:
1253 Log(" Mutex Pointer: %p", thread->WaitPointer);
1255 case THREAD_STAT_RWLOCKSLEEP:
1256 Log(" Lock Pointer: %p", thread->WaitPointer);
1257 Log(" Lock Name: %s",
1258 ((tRWLock*)thread->WaitPointer)->Name);
1260 case THREAD_STAT_SEMAPHORESLEEP:
1261 Log(" Semaphore Pointer: %p", thread->WaitPointer);
1262 Log(" Semaphore Name: %s:%s",
1263 ((tSemaphore*)thread->WaitPointer)->ModName,
1264 ((tSemaphore*)thread->WaitPointer)->Name
1267 case THREAD_STAT_EVENTSLEEP:
1268 Log(" Event Mask: %x", thread->RetStatus);
1270 case THREAD_STAT_ZOMBIE:
1271 Log(" Return Status: %i", thread->RetStatus);
1275 Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
1276 Log(" KStack %p", thread->KernelStack);
1277 if( thread->bInstrTrace )
1278 Log(" Tracing Enabled");
1279 Proc_DumpThreadCPUState(thread);
1283 * \fn void Threads_Dump(void)
1285 void Threads_DumpActive(void)
1289 #if SCHEDULER_TYPE == SCHED_RR_PRI
1293 Log("Active Threads: (%i reported)", giNumActiveThreads);
1295 #if SCHEDULER_TYPE == SCHED_RR_PRI
1296 for( i = 0; i < MIN_PRIORITY+1; i++ )
1298 list = &gaActiveThreads[i];
1300 list = &gActiveThreads;
1302 for(thread=list->Head;thread;thread=thread->Next)
1304 Threads_int_DumpThread(thread);
1305 if(thread->Status != THREAD_STAT_ACTIVE)
1306 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)",
1307 thread->Status, THREAD_STAT_ACTIVE);
1310 #if SCHEDULER_TYPE == SCHED_RR_PRI
1316 * \fn void Threads_Dump(void)
1317 * \brief Dumps a list of currently running threads
1319 void Threads_Dump(void)
1321 Log("--- Thread Dump ---");
1322 Threads_DumpActive();
1324 Log("All Threads:");
1325 for(tThread *thread = gAllThreads; thread; thread = thread->GlobalNext)
1327 Threads_int_DumpThread(thread);
1332 * \brief Gets the next thread to run
1333 * \param CPU Current CPU
1334 * \param Last The thread the CPU was running
1336 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
1340 // If this CPU has the lock, we must let it complete
1341 if( CPU_HAS_LOCK( &glThreadListLock ) )
1344 // Don't change threads if the current CPU has switches disabled
1345 if( gaThreads_NoTaskSwitch[CPU] )
1349 SHORTLOCK( &glThreadListLock );
1351 // Make sure the current (well, old) thread is marked as de-scheduled
1352 if(Last) Last->CurCPU = -1;
1354 // No active threads, just take a nap
1355 if(giNumActiveThreads == 0) {
1356 SHORTREL( &glThreadListLock );
1357 #if DEBUG_TRACE_TICKETS
1358 Log("No active threads");
1364 #if SCHEDULER_TYPE != SCHED_RR_PRI
1365 // Special case: 1 thread
1366 if(giNumActiveThreads == 1) {
1367 if( gActiveThreads.Head->CurCPU == -1 )
1368 gActiveThreads.Head->CurCPU = CPU;
1370 SHORTREL( &glThreadListLock );
1372 if( gActiveThreads.Head->CurCPU == CPU )
1373 return gActiveThreads.Head;
1375 return NULL; // CPU has nothing to do
1380 // Allow the old thread to be scheduled again
1382 if( Last->Status == THREAD_STAT_ACTIVE ) {
1384 #if SCHEDULER_TYPE == SCHED_LOTTERY
1385 giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
1386 # if DEBUG_TRACE_TICKETS
1387 LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n",
1388 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets,
1389 caiTICKET_COUNTS[ Last->Priority ]);
1393 #if SCHEDULER_TYPE == SCHED_RR_PRI
1394 list = &gaActiveThreads[ Last->Priority ];
1396 list = &gActiveThreads;
1398 // Add to end of list
1399 Threads_int_AddToList( list, Last );
1401 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
1403 LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n",
1404 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
1410 // Lottery Scheduler
1412 #if SCHEDULER_TYPE == SCHED_LOTTERY
1417 for(thread = gActiveThreads.Head; thread; thread = thread->Next)
1419 if(thread->Status != THREAD_STAT_ACTIVE)
1420 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
1421 thread, thread->TID, thread->ThreadName, thread->Status);
1422 if(thread->Next == thread) {
1423 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
1424 thread, thread->TID, thread->ThreadName, thread->Status);
1426 number += caiTICKET_COUNTS[ thread->Priority ];
1428 if(number != giFreeTickets) {
1429 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
1430 giFreeTickets, number, CPU);
1434 // No free tickets (all tasks delegated to cores)
1435 if( giFreeTickets == 0 ) {
1436 SHORTREL(&glThreadListLock);
1440 // Get the ticket number
1441 ticket = number = rand() % giFreeTickets;
1443 // Find the next thread
1444 for(thread = gActiveThreads.Head; thread; prev = thread, thread = thread->Next )
1446 if( caiTICKET_COUNTS[ thread->Priority ] > number) break;
1447 number -= caiTICKET_COUNTS[ thread->Priority ];
1450 // If we didn't find a thread, something went wrong
1454 for(thread=gActiveThreads;thread;thread=thread->Next) {
1455 if(thread->CurCPU >= 0) continue;
1456 number += caiTICKET_COUNTS[ thread->Priority ];
1458 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
1459 giFreeTickets, number);
1464 prev->Next = thread->Next;
1466 gActiveThreads.Head = thread->Next;
1468 gActiveThreads.Tail = prev;
1470 giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
1471 # if DEBUG_TRACE_TICKETS
1472 LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n",
1473 CPU, thread, thread->TID, thread->ThreadName,
1474 giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]);
1479 // Priority based round robin scheduler
1481 #elif SCHEDULER_TYPE == SCHED_RR_PRI
1485 for( i = 0; i < MIN_PRIORITY + 1; i ++ )
1487 if( !gaActiveThreads[i].Head )
1490 thread = gaActiveThreads[i].Head;
1493 gaActiveThreads[i].Head = thread->Next;
1495 gaActiveThreads[i].Tail = NULL;
1496 thread->Next = NULL;
1502 SHORTREL(&glThreadListLock);
1505 if( thread->Status != THREAD_STAT_ACTIVE ) {
1506 LogF("Oops, Thread %i (%s) is not active\n", thread->TID, thread->ThreadName);
1509 #elif SCHEDULER_TYPE == SCHED_RR_SIM
1511 // Get the next thread off the list
1512 thread = gActiveThreads.Head;
1513 gActiveThreads.Head = thread->Next;
1515 gaActiveThreads.Tail = NULL;
1516 thread->Next = NULL;
1520 SHORTREL(&glThreadListLock);
1525 # error "Unimplemented scheduling algorithm"
1528 // Make the new thread non-schedulable
1529 thread->CurCPU = CPU;
1530 thread->Remaining = thread->Quantum;
1532 SHORTREL( &glThreadListLock );
1538 EXPORT(Threads_GetUID);
1539 EXPORT(Threads_GetGID);