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 if( dead_thread->Status != THREAD_STAT_ZOMBIE ) {
497 Log_Error("Thread", "Thread %p(%i %s) is not ZOMBIE, instead %s",
498 dead_thread, dead_thread->TID, dead_thread->ThreadName,
499 (dead_thread->Status < ciTHREAD_STAT_COUNT ? casTHREAD_STAT[dead_thread->Status] : "?")
501 ASSERTC(dead_thread->Status, ==, THREAD_STAT_ZOMBIE);
503 dead_thread->Status = THREAD_STAT_DEAD;
504 // - Set return status
506 *Status = dead_thread->RetStatus;
508 Threads_Delete( dead_thread );
512 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Woken with no child");
517 // Any peer/child thread
519 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
525 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
532 // TODO: Register on thread to be poked when it dies
534 // NOTE: Race condition - Other child dies, desired child dies, first death is 'lost'
535 while( (ret = Threads_WaitTID(-1, Status)) != TID )
547 * \brief Gets a thread given its TID
548 * \param TID Thread ID
549 * \return Thread pointer
551 tThread *Threads_GetThread(Uint TID)
555 // Search global list
556 for( thread = gAllThreads; thread; thread = thread->GlobalNext )
558 if(thread->TID == TID)
562 Log_Notice("Threads", "Unable to find TID %i on main list\n", TID);
568 * \brief Deletes an entry from a list
569 * \param List Pointer to the list head
570 * \param Thread Thread to find
573 tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread)
575 tThread *ret, *prev = NULL;
577 for(ret = List->Head;
578 ret && ret != Thread;
579 prev = ret, ret = ret->Next
582 // Is the thread on the list
584 //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
589 List->Head = Thread->Next;
590 //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
593 prev->Next = Thread->Next;
594 //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
596 if( Thread->Next == NULL )
602 void Threads_int_AddToList(tThreadList *List, tThread *Thread)
605 List->Tail->Next = Thread;
613 * \brief Exit the current process (or another?)
614 * \param TID Thread ID to kill
615 * \param Status Exit status
617 void Threads_Exit(int TID, int Status)
620 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
622 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
624 // Halt forever, just in case
629 * \fn void Threads_Kill(tThread *Thread, int Status)
630 * \brief Kill a thread
631 * \param Thread Thread to kill
632 * \param Status Status code to return to the parent
634 void Threads_Kill(tThread *Thread, int Status)
636 int isCurThread = Thread == Proc_GetCurThread();
638 // TODO: Disown all children?
641 // TODO: I should keep a .Children list
642 for(tThread* child = gAllThreads; child; child = child->GlobalNext)
644 if(child->Parent == Thread)
645 child->Parent = &gThreadZero;
650 ///\note Double lock is needed due to overlap of lock areas
652 // Lock thread (stop us recieving messages)
653 SHORTLOCK( &Thread->IsLocked );
655 // Clear Message Queue
656 while( Thread->Messages )
658 tMsg *msg = Thread->Messages->Next;
659 free( Thread->Messages );
660 Thread->Messages = msg;
664 SHORTLOCK( &glThreadListLock );
666 switch(Thread->Status)
668 case THREAD_STAT_PREINIT: // Only on main list
671 // Currently active thread
672 case THREAD_STAT_ACTIVE:
673 if( Thread != Proc_GetCurThread() )
675 #if SCHEDULER_TYPE == SCHED_RR_PRI
676 tThreadList *list = &gaActiveThreads[Thread->Priority];
678 tThreadList *list = &gActiveThreads;
680 if( Threads_int_DelFromQueue( list, Thread ) )
685 Log_Warning("Threads",
686 "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list",
687 Thread, Thread->TID, Thread->ThreadName
690 #if SCHEDULER_TYPE == SCHED_LOTTERY
691 giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ];
694 // Ensure that we are not rescheduled
695 Thread->Remaining = 0; // Clear Remaining Quantum
696 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
698 // Update bookkeeping
699 giNumActiveThreads --;
701 // Kill it while it sleeps!
702 case THREAD_STAT_SLEEPING:
703 if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) )
705 Log_Warning("Threads",
706 "Threads_Kill - Thread "PRIthread_fmt" marked as sleeping, but not on list",
707 PRIthread_args(Thread)
712 // Brains!... You cannot kill something that is already dead
713 case THREAD_STAT_ZOMBIE:
714 Log_Warning("Threads", "Threads_Kill - Thread %p(%i,%s) is undead, you cannot kill it",
715 Thread, Thread->TID, Thread->ThreadName);
716 SHORTREL( &glThreadListLock );
717 SHORTREL( &Thread->IsLocked );
721 Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)",
727 Thread->RetStatus = Status;
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 "PRIthread_fmt" went *hurk* (%i) (isCurThread=%B)", PRIthread_args(Thread), Status, isCurThread);
739 //Log("Thread status = %i %s", Thread->Status, casTHREAD_STAT[Thread->Status]);
741 SHORTREL( &Thread->IsLocked );
752 * \brief Yield remainder of the current thread's timeslice
754 void Threads_Yield(void)
756 // Log("Threads_Yield: by %p", __builtin_return_address(0));
761 * \breif Wait for the thread status to not be a specified value
763 void Threads_int_WaitForStatusEnd(enum eThreadStatus Status)
765 tThread *us = Proc_GetCurThread();
766 LOG("us = %p(%i %s), status=%i", us, us->TID, us->ThreadName, Status);
767 ASSERT(Status != THREAD_STAT_ACTIVE);
768 ASSERT(Status != THREAD_STAT_DEAD);
769 while( us->Status == Status )
772 if( us->Status == Status )
773 Debug("Thread %p(%i %s) rescheduled while in %s state for %p",
774 us, us->TID, us->ThreadName,
775 casTHREAD_STAT[Status],
776 __builtin_return_address(0));
780 int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock)
782 SHORTLOCK( &glThreadListLock );
783 tThread *us = Threads_RemActive();
785 // - Mark as sleeping
787 us->WaitPointer = Ptr;
788 us->RetStatus = Num; // Use RetStatus as a temp variable
793 (*ListTail)->Next = us;
800 else if( ListHead ) {
801 us->Next = *ListHead;
808 //if( Proc_ThreadSync(us) )
810 SHORTREL( &glThreadListLock );
813 Threads_int_WaitForStatusEnd(Status);
814 us->WaitPointer = NULL;
815 return us->RetStatus;
819 * \fn void Threads_Sleep(void)
820 * \brief Take the current process off the run queue
822 void Threads_Sleep(void)
824 tThread *cur = Proc_GetCurThread();
827 SHORTLOCK( &glThreadListLock );
829 // Don't sleep if there is a message waiting
830 if( cur->Messages ) {
831 SHORTREL( &glThreadListLock );
835 // Remove us from running queue
837 // Mark thread as sleeping
838 cur->Status = THREAD_STAT_SLEEPING;
840 // Add to Sleeping List (at the top)
841 Threads_int_AddToList( &gSleepingThreads, cur );
843 #if DEBUG_TRACE_STATE
844 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
848 SHORTREL( &glThreadListLock );
849 Threads_int_WaitForStatusEnd(THREAD_STAT_SLEEPING);
854 * \brief Wakes a sleeping/waiting thread up
855 * \param Thread Thread to wake
856 * \return Boolean Failure (Returns ERRNO)
857 * \warning This should ONLY be called with task switches disabled
859 int Threads_Wake(tThread *Thread)
864 switch(Thread->Status)
866 case THREAD_STAT_ACTIVE:
867 Log("Threads_Wake - Waking awake thread (%i)", Thread->TID);
870 case THREAD_STAT_SLEEPING:
871 // Remove from sleeping queue
872 SHORTLOCK( &glThreadListLock );
873 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
874 SHORTREL( &glThreadListLock );
876 Threads_AddActive( Thread );
878 #if DEBUG_TRACE_STATE
879 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
883 case THREAD_STAT_SEMAPHORESLEEP: {
885 tThread *th, *prev=NULL;
887 sem = Thread->WaitPointer;
889 SHORTLOCK( &sem->Protector );
891 // Remove from sleeping queue
892 for( th = sem->Waiting; th; prev = th, th = th->Next )
893 if( th == Thread ) break;
897 prev->Next = Thread->Next;
899 sem->Waiting = Thread->Next;
900 if(sem->LastWaiting == Thread)
901 sem->LastWaiting = prev;
906 for( th = sem->Signaling; th; prev = th, th = th->Next )
907 if( th == Thread ) break;
909 Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)",
910 Thread, Thread->TID, Thread->ThreadName,
911 sem, sem->ModName, sem->Name);
916 prev->Next = Thread->Next;
918 sem->Signaling = Thread->Next;
919 if(sem->LastSignaling == Thread)
920 sem->LastSignaling = prev;
923 Thread->RetStatus = 0; // It didn't get anything
924 Threads_AddActive( Thread );
926 #if DEBUG_TRACE_STATE
927 Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName);
929 SHORTREL( &sem->Protector );
932 case THREAD_STAT_WAITING:
933 Warning("Threads_Wake - Waiting threads are not currently supported");
936 case THREAD_STAT_DEAD:
937 Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID);
941 Log_Warning("Threads", "Threads_Wake - Unknown process status (%i)", Thread->Status);
947 * \brief Wake a thread given the TID
948 * \param TID Thread ID to wake
949 * \return Boolean Faulure (errno)
951 int Threads_WakeTID(tTID TID)
953 tThread *thread = Threads_GetThread(TID);
957 ret = Threads_Wake( thread );
958 //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
962 void Threads_ToggleTrace(int TID)
964 tThread *thread = Threads_GetThread(TID);
966 thread->bInstrTrace = !thread->bInstrTrace;
970 * \brief Adds a thread to the active queue
972 void Threads_AddActive(tThread *Thread)
974 #if DEBUG_TRACE_ACTIVEQUEUE
975 Debug("Threads_AddActive("PRIthread_fmt")", PRIthread_args(Thread));
977 SHORTLOCK( &glThreadListLock );
979 if( Thread->Status == THREAD_STAT_ACTIVE )
981 tThread *cur = Proc_GetCurThread();
982 Log_KernelPanic("Threads",
983 "ret=%p CPU%i "PRIthread_fmt" is adding "PRIthread_fmt" when it is active",
984 __builtin_return_address(0),
985 GetCPUNum(), PRIthread_args(cur), PRIthread_args(Thread));
986 SHORTREL( &glThreadListLock );
991 Thread->Status = THREAD_STAT_ACTIVE;
992 // Add to active list
993 // - Thread can be the current thread if we're interrupted just before
994 // Proc_Reschedule in a sleep state.
995 if( Thread != Proc_GetCurThread() )
997 #if SCHEDULER_TYPE == SCHED_RR_PRI
998 tThreadList *list = &gaActiveThreads[Thread->Priority];
1000 tThreadList *list = &gActiveThreads;
1002 #if DEBUG_TRACE_ACTIVEQUEUE
1003 Debug(" - Head="PRIthread_fmt",Tail="PRIthread_fmt"",
1004 PRIthread_args(list->Head),
1005 PRIthread_args(list->Tail)
1008 Threads_int_AddToList( list, Thread );
1011 // Update bookkeeping
1012 giNumActiveThreads ++;
1014 #if SCHEDULER_TYPE == SCHED_LOTTERY
1017 // Only change the ticket count if the thread is un-scheduled
1018 if(Thread->CurCPU != -1)
1021 delta = caiTICKET_COUNTS[ Thread->Priority ];
1023 giFreeTickets += delta;
1024 # if DEBUG_TRACE_TICKETS
1025 Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]",
1026 GetCPUNum(), Thread, Thread->TID, Thread->ThreadName,
1027 giFreeTickets, delta
1033 SHORTREL( &glThreadListLock );
1037 * \brief Removes the current thread from the active queue
1038 * \warning This should ONLY be called with the lock held
1039 * \return Current thread pointer
1041 tThread *Threads_RemActive(void)
1043 tThread *us = Proc_GetCurThread();
1044 #if DEBUG_TRACE_ACTIVEQUEUE
1045 Debug("Threads_RemActive(%p(%i %s))", us, us->TID, us->ThreadName);
1047 giNumActiveThreads --;
1052 * \fn void Threads_SetFaultHandler(Uint Handler)
1053 * \brief Sets the signal handler for a signal
1055 void Threads_SetFaultHandler(Uint Handler)
1057 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
1058 Proc_GetCurThread()->FaultHandler = Handler;
1062 * \fn void Threads_Fault(int Num)
1063 * \brief Calls a fault handler
1065 void Threads_Fault(int Num)
1067 tThread *thread = Proc_GetCurThread();
1069 if(!thread) return ;
1071 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
1073 switch(thread->FaultHandler)
1076 Threads_Kill(thread, -1);
1079 case 1: // Dump Core?
1080 Threads_Kill(thread, -1);
1085 // Double Fault? Oh, F**k
1086 if(thread->CurFaultNum != 0) {
1087 Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID);
1088 Threads_Kill(thread, -1); // For now, just kill
1092 thread->CurFaultNum = Num;
1094 Proc_CallFaultHandler(thread);
1098 * \fn void Threads_SegFault(tVAddr Addr)
1099 * \brief Called when a Segment Fault occurs
1101 void Threads_SegFault(tVAddr Addr)
1103 tThread *cur = Proc_GetCurThread();
1104 cur->bInstrTrace = 0;
1105 Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr);
1106 MM_DumpTables(0, USER_MAX);
1108 //Threads_Exit( 0, -1 );
1112 void Threads_PostSignalTo(tThread *Thread, int SignalNum)
1115 Log_Debug("Threads", "Signalling %i(%s) with %i", Thread->TID, Thread->ThreadName, SignalNum);
1116 Thread->PendingSignal = SignalNum;
1117 Threads_PostEvent(Thread, THREAD_EVENT_SIGNAL);
1119 void Threads_PostSignal(int SignalNum)
1121 Threads_PostSignalTo( Proc_GetCurThread(), SignalNum );
1124 void Threads_SignalGroup(tPGID PGID, int Signal)
1126 for( tProcess *proc = gAllProcesses; proc; proc = proc->Next )
1128 if(proc->PGID == PGID)
1130 Threads_PostSignalTo(proc->FirstThread, Signal);
1137 int Threads_GetPendingSignal(void)
1139 tThread *cur = Proc_GetCurThread();
1141 // Atomic AND with 0 fetches and clears in one operation
1142 int ret = __sync_fetch_and_and( &cur->PendingSignal, 0 );
1145 Log_Debug("Threads", "Thread %i(%s) has signal %i pending",
1146 cur->TID, cur->ThreadName, ret);
1152 * \brief Update the current thread's signal handler
1154 void Threads_SetSignalHandler(int SignalNum, void *Handler)
1156 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1158 if( !MM_IsUser(Handler) )
1160 Proc_GetCurThread()->Process->SignalHandlers[SignalNum] = Handler;
1164 * \brief Gets the registered (or default, if none set) handler for a signal.
1165 * \return Handler function pointer, OR NULL if no signal to be ignored
1167 void *Threads_GetSignalHandler(int SignalNum)
1170 void *User_Signal_Core = User_Signal_Kill;
1172 if( SignalNum <= 0 || SignalNum >= NSIGNALS )
1174 void *ret = Proc_GetCurThread()->Process->SignalHandlers[SignalNum];
1175 if( !ret || (SignalNum == SIGKILL || SignalNum == SIGSTOP) )
1182 ret = User_Signal_Kill;
1188 ret = User_Signal_Core;
1191 ret = User_Signal_Kill;
1194 ret = User_Signal_Core;
1199 ret = User_Signal_Kill;
1206 Log_Debug("Threads", "Handler %p for signal %i", ret, SignalNum);
1210 // --- Process Structure Access Functions ---
1211 tPGID Threads_GetPGID(void)
1213 return Proc_GetCurThread()->Process->PGID;
1215 tPID Threads_GetPID(void)
1217 return Proc_GetCurThread()->Process->PID;
1219 tTID Threads_GetTID(void)
1221 return Proc_GetCurThread()->TID;
1223 tUID Threads_GetUID(void)
1225 return Proc_GetCurThread()->Process->UID;
1227 tGID Threads_GetGID(void)
1229 return Proc_GetCurThread()->Process->GID;
1232 int Threads_SetUID(tUID ID)
1234 tThread *t = Proc_GetCurThread();
1235 if( t->Process->UID != 0 ) {
1239 Log_Debug("Threads", "PID %i's UID set to %i", t->Process->PID, ID);
1240 t->Process->UID = ID;
1244 int Threads_SetGID(tGID ID)
1246 tThread *t = Proc_GetCurThread();
1247 if( t->Process->UID != 0 ) {
1251 Log_Debug("Threads", "PID %i's GID set to %i", t->Process->PID, ID);
1252 t->Process->GID = ID;
1256 // --- Per-thread storage ---
1257 int *Threads_GetErrno(void)
1259 return &Proc_GetCurThread()->_errno;
1262 // --- Configuration ---
1263 int *Threads_GetMaxFD(tProcess *Process)
1265 if(!Process) Process = Proc_GetCurThread()->Process;
1266 return &Process->MaxFD;
1268 char **Threads_GetChroot(tProcess *Process)
1270 if(!Process) Process = Proc_GetCurThread()->Process;
1271 return &Process->RootDir;
1273 char **Threads_GetCWD(tProcess *Process)
1275 if(!Process) Process = Proc_GetCurThread()->Process;
1276 return &Process->CurrentWorkingDir;
1280 void Threads_int_DumpThread(tThread *thread)
1283 Log(" %p NULL", thread);
1286 if( !CheckMem(thread, sizeof(tThread)) ) {
1287 Log(" %p INVAL", thread);
1290 tPID pid = (thread->Process ? thread->Process->PID : -1);
1291 const char *statstr = (thread->Status < sizeof(casTHREAD_STAT)/sizeof(casTHREAD_STAT[0])
1292 ? casTHREAD_STAT[thread->Status] : "");
1293 Log(" %p %i (%i) - %s (CPU %i) - %i (%s)",
1294 thread, thread->TID, pid, thread->ThreadName, thread->CurCPU,
1295 thread->Status, statstr
1297 switch(thread->Status)
1299 case THREAD_STAT_MUTEXSLEEP:
1300 Log(" Mutex Pointer: %p", thread->WaitPointer);
1302 case THREAD_STAT_RWLOCKSLEEP:
1303 Log(" Lock Pointer: %p", thread->WaitPointer);
1304 Log(" Lock Name: %s",
1305 ((tRWLock*)thread->WaitPointer)->Name);
1307 case THREAD_STAT_SEMAPHORESLEEP:
1308 Log(" Semaphore Pointer: %p", thread->WaitPointer);
1309 Log(" Semaphore Name: %s:%s",
1310 ((tSemaphore*)thread->WaitPointer)->ModName,
1311 ((tSemaphore*)thread->WaitPointer)->Name
1314 case THREAD_STAT_EVENTSLEEP:
1315 Log(" Event Mask: %x", thread->RetStatus);
1317 case THREAD_STAT_ZOMBIE:
1318 Log(" Return Status: %i", thread->RetStatus);
1322 Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
1323 Log(" KStack %p", thread->KernelStack);
1324 if( thread->bInstrTrace )
1325 Log(" Tracing Enabled");
1326 Proc_DumpThreadCPUState(thread);
1330 * \fn void Threads_Dump(void)
1332 void Threads_DumpActive(void)
1334 Log("Active Threads: (%i reported)", giNumActiveThreads);
1336 #if SCHEDULER_TYPE == SCHED_RR_PRI
1337 for( int i = 0; i < MIN_PRIORITY+1; i++ )
1339 tThreadList *list = &gaActiveThreads[i];
1341 tThreadList *list = &gActiveThreads;
1343 for(tThread *thread = list->Head; thread; thread = thread->Next)
1345 Threads_int_DumpThread(thread);
1346 if(thread->Status != THREAD_STAT_ACTIVE)
1347 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)",
1348 thread->Status, THREAD_STAT_ACTIVE);
1351 #if SCHEDULER_TYPE == SCHED_RR_PRI
1357 * \fn void Threads_Dump(void)
1358 * \brief Dumps a list of currently running threads
1360 void Threads_Dump(void)
1362 Log("--- Thread Dump ---");
1363 Threads_DumpActive();
1365 Log("All Threads:");
1366 for(tThread *thread = gAllThreads; thread; thread = thread->GlobalNext)
1368 Threads_int_DumpThread(thread);
1372 tThread *Threads_int_GetRunnable(void)
1374 #if SCHEDULER_TYPE == SCHED_LOTTERY
1375 // -----------------------------------
1376 // Lottery Scheduler
1377 // -----------------------------------
1378 #if DEBUG_VALIDATE_TICKET_COUNTS
1380 int total_tickets = 0;
1381 for(const tThread *thread = gActiveThreads.Head; thread; thread = thread->Next)
1383 if(thread->Status != THREAD_STAT_ACTIVE)
1384 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
1385 thread, thread->TID, thread->ThreadName, thread->Status);
1386 if(thread->Next == thread) {
1387 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
1388 thread, thread->TID, thread->ThreadName, thread->Status);
1390 total_tickets += caiTICKET_COUNTS[ thread->Priority ];
1392 if(total_tickets != giFreeTickets)
1394 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
1395 giFreeTickets, total_tickets, CPU);
1400 // No free tickets (all tasks delegated to cores)
1401 if( giFreeTickets == 0 )
1406 // Get the ticket number
1407 int ticket = rand() % giFreeTickets;
1408 int number = ticket;
1410 // Find the next thread
1411 tThread **pnp = &gActiveThreads.Head;
1413 for(thread = gActiveThreads.Head; thread; pnp = &thread->Next, thread = thread->Next )
1415 if( caiTICKET_COUNTS[ thread->Priority ] > number)
1417 number -= caiTICKET_COUNTS[ thread->Priority ];
1420 // If we didn't find a thread, something went wrong
1423 int total_tickets = 0;
1424 for(thread=gActiveThreads;thread;thread=thread->Next) {
1425 if(thread->CurCPU >= 0) continue;
1426 total_tickets += caiTICKET_COUNTS[ thread->Priority ];
1428 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
1429 giFreeTickets, total_tickets);
1433 *pnp = thread->Next;
1435 gActiveThreads.Tail = prev;
1437 giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
1438 # if DEBUG_TRACE_TICKETS
1439 LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n",
1440 CPU, thread, thread->TID, thread->ThreadName,
1441 giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]);
1445 #elif SCHEDULER_TYPE == SCHED_RR_PRI
1447 // -----------------------------------
1448 // Priority based round robin scheduler
1449 // -----------------------------------
1450 for( int i = 0; i < MIN_PRIORITY + 1; i ++ )
1452 if( gaActiveThreads[i].Head == NULL )
1455 tThread *thread = gaActiveThreads[i].Head;
1457 if( thread->Status != THREAD_STAT_ACTIVE ) {
1458 for( const tThread *t = gaActiveThreads[i].Head; t; t = t->Next )
1459 LogF("- %p(%i %s)\n", t, t->TID, t->ThreadName);
1460 Panic("Thread %p(%i %s) from pqueue %i is active!",
1461 thread, thread->TID, thread->ThreadName, i);
1465 gaActiveThreads[i].Head = thread->Next;
1467 gaActiveThreads[i].Tail = NULL;
1468 thread->Next = NULL;
1473 #elif SCHEDULER_TYPE == SCHED_RR_SIM
1475 // -----------------------------------
1476 // Single-list round-robin
1477 // -----------------------------------
1478 tThread *thread = gActiveThreads.Head;
1479 LOG("thread = %p", thread);
1482 gActiveThreads.Head = thread->Next;
1484 gaActiveThreads.Tail = NULL;
1485 thread->Next = NULL;
1489 # error "Unimplemented scheduling algorithm"
1495 * \brief Gets the next thread to run
1496 * \param CPU Current CPU
1497 * \param Last The thread the CPU was running
1499 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
1501 ASSERT( CPU_HAS_LOCK(&glThreadListLock) );
1503 // Don't change threads if the current CPU has switches disabled
1504 if( gaThreads_NoTaskSwitch[CPU] ) {
1509 // Make sure the current (well, old) thread is marked as de-scheduled
1510 if(Last) Last->CurCPU = -1;
1512 // No active threads, just take a nap
1513 if(giNumActiveThreads == 0) {
1515 #if DEBUG_TRACE_TICKETS
1516 Log("No active threads");
1521 // Allow the old thread to be scheduled again
1524 if( Last->Status == THREAD_STAT_ACTIVE ) {
1526 #if SCHEDULER_TYPE == SCHED_LOTTERY
1527 giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
1528 # if DEBUG_TRACE_TICKETS
1529 LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n",
1530 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets,
1531 caiTICKET_COUNTS[ Last->Priority ]);
1535 #if SCHEDULER_TYPE == SCHED_RR_PRI
1536 list = &gaActiveThreads[ Last->Priority ];
1538 list = &gActiveThreads;
1540 // Add to end of list
1541 Threads_int_AddToList( list, Last );
1542 #if DEBUG_TRACE_ACTIVEQUEUE > 1
1543 Debug("Threads_GetNextToRun: Append thread %p(%i %s)",
1544 Last, Last->TID, Last->ThreadName);
1547 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
1549 LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n",
1550 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
1555 // Call actual scheduler
1556 tThread *thread = Threads_int_GetRunnable();
1561 if( thread->Status != THREAD_STAT_ACTIVE )
1563 LogF("Thread %p(%i %s) scheduled while not active\n",
1564 thread, thread->TID, thread->ThreadName);
1567 // Make the new thread non-schedulable
1568 thread->CurCPU = CPU;
1569 thread->Remaining = thread->Quantum;
1571 #if DEBUG_TRACE_SCHEDULE
1572 Debug("Scheduled "PRIthread_fmt", next = %p",
1573 PRIthread_args(thread),
1579 // No thread possible, warning condition (idle thread should be runnable)
1580 Warning("No runnable thread for CPU%i", CPU);
1587 EXPORT(Threads_GetUID);
1588 EXPORT(Threads_GetGID);