4 * - Common Thread Control
10 #define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
11 #define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
14 #define DEFAULT_QUANTUM 10
15 #define DEFAULT_TICKETS 5
16 #define MAX_TICKETS 10
17 const enum eConfigTypes cCONFIG_TYPES[] = {
18 CFGT_HEAPSTR, // e.g. CFG_VFS_CWD
19 CFGT_INT, // e.g. CFG_VFS_MAXFILES
24 extern void ArchThreads_Init(void);
25 extern void Proc_Start(void);
26 extern tThread *Proc_GetCurThread(void);
27 extern int Proc_Clone(Uint *Err, Uint Flags);
28 extern void Proc_CallFaultHandler(tThread *Thread);
31 void Threads_Init(void);
32 int Threads_SetName(char *NewName);
33 char *Threads_GetName(int ID);
34 void Threads_SetTickets(tThread *Thread, int Num);
35 tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
36 int Threads_WaitTID(int TID, int *status);
37 tThread *Threads_GetThread(Uint TID);
38 void Threads_AddToDelete(tThread *Thread);
39 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread);
40 void Threads_Exit(int TID, int Status);
41 void Threads_Kill(tThread *Thread, int Status);
42 void Threads_Yield(void);
43 void Threads_Sleep(void);
44 int Threads_Wake(tThread *Thread);
45 void Threads_AddActive(tThread *Thread);
46 tThread *Threads_RemActive(void);
47 int Threads_GetPID(void);
48 int Threads_GetTID(void);
49 tUID Threads_GetUID(void);
50 int Threads_SetUID(Uint *Errno, tUID ID);
51 tGID Threads_GetGID(void);
52 int Threads_SetGID(Uint *Errno, tUID ID);
53 void Threads_Dump(void);
54 void Mutex_Acquire(tMutex *Mutex);
55 void Mutex_Release(tMutex *Mutex);
56 int Mutex_IsLocked(tMutex *Mutex);
60 // Only used for the core kernel
61 tThread gThreadZero = {
62 Status: THREAD_STAT_ACTIVE, // Status
63 ThreadName: "ThreadZero", // Name
64 Quantum: DEFAULT_QUANTUM, // Default Quantum
65 Remaining: DEFAULT_QUANTUM, // Current Quantum
66 NumTickets: DEFAULT_TICKETS // Number of tickets
70 tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
71 // --- Current State ---
72 volatile int giNumActiveThreads = 0;
73 volatile int giFreeTickets = 0;
74 volatile Uint giNextTID = 1;
75 // --- Thread Lists ---
76 tThread *gAllThreads = NULL; // All allocated threads
77 tThread *gActiveThreads = NULL; // Currently Running Threads
78 tThread *gSleepingThreads = NULL; // Sleeping Threads
79 tThread *gDeleteThreads = NULL; // Threads to delete
84 * \fn void Threads_Init(void)
85 * \brief Initialse the thread list
87 void Threads_Init(void)
91 // Create Initial Task
92 gActiveThreads = &gThreadZero;
93 gAllThreads = &gThreadZero;
94 //giFreeTickets = gThreadZero.NumTickets;
95 giNumActiveThreads = 1;
101 * \fn void Threads_SetName(char *NewName)
102 * \brief Sets the current thread's name
104 int Threads_SetName(char *NewName)
106 tThread *cur = Proc_GetCurThread();
107 char *oldname = cur->ThreadName;
109 cur->ThreadName = NULL;
111 if( IsHeap(oldname) ) free( oldname );
113 cur->ThreadName = strdup(NewName);
118 * \fn char *Threads_GetName(int ID)
119 * \brief Gets a thread's name
121 char *Threads_GetName(int ID)
124 return Proc_GetCurThread()->ThreadName;
126 // TODO: Find a thread and get its name
131 * \fn void Threads_SetTickets(tThread *Thread, int Num)
132 * \brief Sets the 'priority' of a task
134 void Threads_SetTickets(tThread *Thread, int Num)
137 Thread = Proc_GetCurThread();
139 if(Num > MAX_TICKETS) Num = MAX_TICKETS;
141 if( Thread != Proc_GetCurThread() ) {
142 SHORTLOCK( &glThreadListLock );
143 giFreeTickets -= Thread->NumTickets - Num;
144 Thread->NumTickets = Num;
145 SHORTREL( &glThreadListLock );
148 Thread->NumTickets = Num;
152 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
154 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
158 cur = Proc_GetCurThread();
160 new = malloc(sizeof(tThread));
165 memcpy(new, cur, sizeof(tThread));
169 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
170 new->Status = THREAD_STAT_ACTIVE;
174 new->TID = giNextTID++;
178 new->ThreadName = strdup(cur->ThreadName);
180 // Set Thread Group ID (PID)
182 new->TGID = new->TID;
184 new->TGID = cur->TGID;
186 // Messages are not inherited
187 new->Messages = NULL;
188 new->LastMessage = NULL;
191 new->Remaining = new->Quantum = cur->Quantum;
192 new->NumTickets = cur->NumTickets;
194 // Set Signal Handlers
195 new->CurFaultNum = 0;
196 new->FaultHandler = cur->FaultHandler;
198 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
200 switch(cCONFIG_TYPES[i])
203 new->Config[i] = cur->Config[i];
207 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
214 // Maintain a global list of threads
215 SHORTLOCK( &glThreadListLock );
216 new->GlobalPrev = NULL; // Protect against bugs
217 new->GlobalNext = gAllThreads;
219 SHORTREL( &glThreadListLock );
225 * \fn Uint *Threads_GetCfgPtr(int Id)
227 Uint *Threads_GetCfgPtr(int Id)
229 if(Id < 0 || Id >= NUM_CFG_ENTRIES) {
230 Warning("Threads_GetCfgPtr: Index %i is out of bounds", Id);
234 return &Proc_GetCurThread()->Config[Id];
238 * \fn tTID Threads_WaitTID(int TID, int *status)
239 * \brief Wait for a task to change state
240 * \return TID of child that changed state
242 int Threads_WaitTID(int TID, int *status)
246 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
250 // Any peer/child thread
252 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
258 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
264 tThread *t = Threads_GetThread(TID);
265 int initStatus = t->Status;
268 if(initStatus != THREAD_STAT_ZOMBIE) {
269 // TODO: Handle child also being suspended if wanted
270 while(t->Status != THREAD_STAT_ZOMBIE) {
272 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
273 Threads_GetTID(), t->TID, t->Status);
277 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
278 Threads_GetTID(), t->TID, t->Status);
282 case THREAD_STAT_ZOMBIE:
284 t->Status = THREAD_STAT_DEAD;
285 // TODO: Child return value?
286 if(status) *status = 0;
287 // add to delete queue
288 Threads_AddToDelete( t );
291 if(status) *status = -1;
301 * \fn tThread *Threads_GetThread(Uint TID)
302 * \brief Gets a thread given its TID
303 * \param TID Thread ID
305 tThread *Threads_GetThread(Uint TID)
309 // Search global list
310 for(thread = gAllThreads;
312 thread = thread->GlobalNext)
314 if(thread->TID == TID)
318 Log("Unable to find TID %i on main list\n", TID);
324 * \fn void Threads_AddToDelete(tThread *Thread)
325 * \brief Adds a thread to the delete queue
327 void Threads_AddToDelete(tThread *Thread)
329 // Add to delete queue
331 Thread->Next = gDeleteThreads;
332 gDeleteThreads = Thread;
335 gDeleteThreads = Thread;
340 * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
341 * \brief Gets the previous entry in a thead linked list
343 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
347 if(*List == Thread) {
348 return (tThread*)List;
351 ret->Next && ret->Next != Thread;
354 // Error if the thread is not on the list
355 if(!ret->Next || ret->Next != Thread) {
363 * \fn void Threads_Exit(int TID, int Status)
364 * \brief Exit the current process
366 void Threads_Exit(int TID, int Status)
369 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
371 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
372 // Halt forever, just in case
378 * \fn void Threads_Kill(tThread *Thread, int Status)
379 * \brief Kill a thread
380 * \param Thread Thread to kill
381 * \param Status Status code to return to the parent
383 void Threads_Kill(tThread *Thread, int Status)
392 for(child = gActiveThreads;
396 if(child->PTID == Thread->TID)
397 Threads_Kill(child, -1);
402 ///\note Double lock is needed due to overlap of lock areas
404 // Lock thread (stop us recieving messages)
405 SHORTLOCK( &Thread->IsLocked );
408 SHORTLOCK( &glThreadListLock );
410 // Get previous thread on list
411 prev = Threads_int_GetPrev( &gActiveThreads, Thread );
413 Warning("Proc_Exit - Current thread is not on the active queue");
414 SHORTREL( &Thread->IsLocked );
415 SHORTREL( &glThreadListLock );
419 // Clear Message Queue
420 while( Thread->Messages )
422 msg = Thread->Messages->Next;
423 free( Thread->Messages );
424 Thread->Messages = msg;
427 Thread->Remaining = 0; // Clear Remaining Quantum
428 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
429 prev->Next = Thread->Next; // Remove from active
431 giNumActiveThreads --;
432 if( Thread != Proc_GetCurThread() )
433 giFreeTickets -= Thread->NumTickets;
436 Thread->RetStatus = Status;
438 // Don't Zombie if we are being killed as part of a tree
441 Thread->Status = THREAD_STAT_DEAD;
442 Threads_AddToDelete( Thread );
444 Thread->Status = THREAD_STAT_ZOMBIE;
446 Threads_Wake( Thread->Parent );
449 Log("Thread %i went *hurk* (%i)", Thread->TID, Thread->Status);
452 SHORTREL( &Thread->IsLocked );
453 SHORTREL( &glThreadListLock );
455 if(Status != -1) HALT();
459 * \fn void Threads_Yield(void)
460 * \brief Yield remainder of timeslice
462 void Threads_Yield(void)
464 Proc_GetCurThread()->Remaining = 0;
469 * \fn void Threads_Sleep(void)
470 * \brief Take the current process off the run queue
472 void Threads_Sleep(void)
474 tThread *cur = Proc_GetCurThread();
476 //Log_Log("Threads", "%i going to sleep", cur->TID);
479 SHORTLOCK( &glThreadListLock );
481 // Don't sleep if there is a message waiting
482 if( cur->Messages ) {
483 SHORTREL( &glThreadListLock );
487 // Remove us from running queue
490 // Add to Sleeping List (at the top)
491 cur->Next = gSleepingThreads;
492 gSleepingThreads = cur;
494 // Mark thread as sleeping
495 cur->Status = THREAD_STAT_SLEEPING;
497 #if DEBUG_TRACE_STATE
498 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
502 SHORTREL( &glThreadListLock );
504 while(cur->Status != THREAD_STAT_ACTIVE) HALT();
509 * \fn int Threads_Wake( tThread *Thread )
510 * \brief Wakes a sleeping/waiting thread up
511 * \param Thread Thread to wake
512 * \return Boolean Failure (Returns ERRNO)
513 * \note Should be called with the scheduler lock held
515 int Threads_Wake(tThread *Thread)
522 switch(Thread->Status)
524 case THREAD_STAT_ACTIVE:
525 Log("Thread_Wake: Waking awake thread (%i)", Thread->TID);
528 case THREAD_STAT_SLEEPING: // TODO: Comment better
529 // Remove from sleeping queue
530 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
531 prev->Next = Thread->Next;
532 // Add to active queue
533 Thread->Next = gActiveThreads;
534 gActiveThreads = Thread;
535 // Update bookkeeping
536 giNumActiveThreads ++;
537 giFreeTickets += Thread->NumTickets;
538 #if DEBUG_TRACE_TICKETS
539 Log("Threads_Wake: new giFreeTickets = %i", giFreeTickets);
542 Thread->Status = THREAD_STAT_ACTIVE;
543 #if DEBUG_TRACE_STATE
544 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
548 case THREAD_STAT_WAITING:
549 Warning("Thread_Wake - Waiting threads are not currently supported");
552 case THREAD_STAT_DEAD:
553 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
557 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
563 * \brief Wake a thread given the TID
564 * \param TID Thread ID to wake
565 * \return Boolean Faulure (errno)
567 int Threads_WakeTID(tTID TID)
569 tThread *thread = Threads_GetThread(TID);
573 SHORTLOCK( &glThreadListLock );
574 ret = Threads_Wake( thread );
575 SHORTREL( &glThreadListLock );
580 * \fn void Threads_AddActive(tThread *Thread)
581 * \brief Adds a thread to the active queue
583 void Threads_AddActive(tThread *Thread)
585 SHORTLOCK( &glThreadListLock );
586 // Add to active list
587 Thread->Next = gActiveThreads;
588 gActiveThreads = Thread;
589 // Update bookkeeping
590 giNumActiveThreads ++;
591 giFreeTickets += Thread->NumTickets;
592 #if DEBUG_TRACE_TICKETS
593 Log("Threads_AddActive: new giFreeTickets = %i", giFreeTickets);
595 SHORTREL( &glThreadListLock );
599 * \brief Removes the current thread from the active queue
600 * \warning This should ONLY be called with task switches disabled
601 * \return Current thread pointer
603 tThread *Threads_RemActive(void)
605 tThread *ret = Proc_GetCurThread();
606 tThread *prev = Threads_int_GetPrev(&gActiveThreads, ret);
607 if(!prev) return NULL;
610 prev->Next = ret->Next;
611 giNumActiveThreads --;
616 * \fn void Threads_SetFaultHandler(Uint Handler)
617 * \brief Sets the signal handler for a signal
619 void Threads_SetFaultHandler(Uint Handler)
621 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
622 Proc_GetCurThread()->FaultHandler = Handler;
626 * \fn void Threads_Fault(int Num)
627 * \brief Calls a fault handler
629 void Threads_Fault(int Num)
631 tThread *thread = Proc_GetCurThread();
633 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
637 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
639 switch(thread->FaultHandler)
642 Threads_Kill(thread, -1);
645 case 1: // Dump Core?
646 Threads_Kill(thread, -1);
651 // Double Fault? Oh, F**k
652 if(thread->CurFaultNum != 0) {
653 Threads_Kill(thread, -1); // For now, just kill
657 thread->CurFaultNum = Num;
659 Proc_CallFaultHandler(thread);
662 // --- Process Structure Access Functions ---
663 tPID Threads_GetPID(void)
665 return Proc_GetCurThread()->TGID;
667 tTID Threads_GetTID(void)
669 return Proc_GetCurThread()->TID;
671 tUID Threads_GetUID(void)
673 return Proc_GetCurThread()->UID;
675 tGID Threads_GetGID(void)
677 return Proc_GetCurThread()->GID;
680 int Threads_SetUID(Uint *Errno, tUID ID)
682 tThread *t = Proc_GetCurThread();
687 Log_Debug("Threads", "TID %i's UID set to %i", t->TID, ID);
692 int Threads_SetGID(Uint *Errno, tGID ID)
694 tThread *t = Proc_GetCurThread();
699 Log_Debug("Threads", "TID %i's GID set to %i", t->TID, ID);
705 * \fn void Threads_Dump(void)
706 * \brief Dums a list of currently running threads
708 void Threads_Dump(void)
712 Log("Active Threads:");
713 for(thread=gActiveThreads;thread;thread=thread->Next)
715 Log(" %i (%i) - %s (CPU %i)",
716 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
717 Log(" State: %i", thread->Status);
718 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
719 Log(" KStack 0x%x", thread->KernelStack);
723 for(thread=gAllThreads;thread;thread=thread->GlobalNext)
725 Log(" %i (%i) - %s (CPU %i)",
726 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
727 Log(" State: %i", thread->Status);
728 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
729 Log(" KStack 0x%x", thread->KernelStack);
734 * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
735 * \brief Gets the next thread to run
736 * \param CPU Current CPU
737 * \param Last The thread the CPU was running
739 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
746 SHORTLOCK( &glThreadListLock );
748 // Clear Delete Queue
749 while(gDeleteThreads)
751 thread = gDeleteThreads->Next;
752 if( IS_LOCKED(&gDeleteThreads->IsLocked) ) { // Only free if structure is unused
754 gDeleteThreads->Status = THREAD_STAT_DEAD;
756 if( IsHeap(gDeleteThreads->ThreadName) )
757 free(gDeleteThreads->ThreadName);
758 // Remove from global list
759 if( gDeleteThreads == gAllThreads )
760 gAllThreads = gDeleteThreads->GlobalNext;
762 gDeleteThreads->GlobalPrev->GlobalNext = gDeleteThreads->GlobalNext;
763 free( gDeleteThreads );
765 gDeleteThreads = thread;
768 // No active threads, just take a nap
769 if(giNumActiveThreads == 0) {
770 SHORTREL( &glThreadListLock );
771 #if DEBUG_TRACE_TICKETS
772 Log("No active threads");
777 // Special case: 1 thread
778 if(giNumActiveThreads == 1) {
779 if( gActiveThreads->CurCPU == -1 )
780 gActiveThreads->CurCPU = CPU;
781 SHORTREL( &glThreadListLock );
782 if( gActiveThreads->CurCPU == CPU )
783 return gActiveThreads;
784 return NULL; // CPU has nothing to do
787 // Allow the old thread to be scheduled again
789 if( Last->Status == THREAD_STAT_ACTIVE ) {
790 giFreeTickets += Last->NumTickets;
791 #if DEBUG_TRACE_TICKETS
792 LogF(" CPU %i released %p (%i %s) into the pool (%i tickets in pool)\n",
793 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets);
796 #if DEBUG_TRACE_TICKETS
798 LogF(" %p (%s)->Status = %i\n", Last, Last->ThreadName, Last->Status);
805 for(thread=gActiveThreads;thread;thread=thread->Next) {
806 if(thread->CurCPU >= 0) continue;
807 number += thread->NumTickets;
809 if(number != giFreeTickets) {
810 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
811 giFreeTickets, number, CPU);
815 // No free tickets (all tasks delegated to cores)
816 if( giFreeTickets == 0 ) {
817 SHORTREL(&glThreadListLock);
821 // Get the ticket number
822 ticket = number = rand() % giFreeTickets;
824 // Find the next thread
825 for(thread=gActiveThreads;thread;thread=thread->Next)
827 if(thread->CurCPU >= 0) continue;
828 if(thread->NumTickets > number) break;
829 number -= thread->NumTickets;
835 for(thread=gActiveThreads;thread;thread=thread->Next) {
836 if(thread->CurCPU >= 0) continue;
837 number += thread->NumTickets;
839 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
840 giFreeTickets, number);
842 #if DEBUG_TRACE_TICKETS
843 LogF(" CPU%i giFreeTickets = %i\n", CPU, giFreeTickets);
846 // Make the new thread non-schedulable
847 giFreeTickets -= thread->NumTickets;
848 thread->CurCPU = CPU;
851 #if DEBUG_TRACE_TICKETS
852 LogF(" CPU%i giFreeTickets = %i, giving %p (%i %s CPU=%i)\n",
853 CPU, giFreeTickets, thread, thread->TID, thread->ThreadName, thread->CurCPU);
856 SHORTREL( &glThreadListLock );
862 * \fn void Threads_SegFault(tVAddr Addr)
863 * \brief Called when a Segment Fault occurs
865 void Threads_SegFault(tVAddr Addr)
867 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
869 //Threads_Exit( 0, -1 );
873 * \brief Acquire a heavy mutex
874 * \param Mutex Mutex to acquire
876 * This type of mutex checks if the mutex is avaliable, and acquires it
877 * if it is. Otherwise, the current thread is added to the mutex's wait
878 * queue and the thread suspends. When the holder of the mutex completes,
879 * the oldest thread (top thread) on the queue is given the lock and
882 void Mutex_Acquire(tMutex *Mutex)
884 tThread *us = Proc_GetCurThread();
887 SHORTLOCK( &Mutex->Protector );
889 //Log("Mutex_Acquire: (%p)", Mutex);
891 // Check if the lock is already held
893 SHORTLOCK( &glThreadListLock );
894 // - Remove from active list
896 // - Mark as sleeping
897 us->Status = THREAD_STAT_OFFSLEEP;
900 if(Mutex->LastWaiting) {
901 Mutex->LastWaiting->Next = us;
902 Mutex->LastWaiting = us;
906 Mutex->LastWaiting = us;
908 SHORTREL( &glThreadListLock );
909 SHORTREL( &Mutex->Protector );
910 while(us->Status == THREAD_STAT_OFFSLEEP) HALT();
911 // We're only woken when we get the lock
913 // Ooh, let's take it!
916 SHORTREL( &Mutex->Protector );
921 * \brief Release a held mutex
922 * \param Mutex Mutex to release
924 void Mutex_Release(tMutex *Mutex)
926 SHORTLOCK( &Mutex->Protector );
927 //Log("Mutex_Release: (%p)", Mutex);
928 if( Mutex->Waiting ) {
929 Mutex->Owner = Mutex->Waiting; // Set owner
930 Mutex->Waiting = Mutex->Waiting->Next; // Next!
932 Mutex->Owner->Status = THREAD_STAT_ACTIVE;
933 Threads_AddActive(Mutex->Owner);
934 //Log("Mutex %p Woke %p", Mutex, Mutex->Owner);
939 SHORTREL( &Mutex->Protector );
943 * \brief Is this mutex locked?
944 * \param Mutex Mutex pointer
946 int Mutex_IsLocked(tMutex *Mutex)
948 return Mutex->Owner != NULL;
952 EXPORT(Threads_GetUID);
953 EXPORT(Mutex_Acquire);
954 EXPORT(Mutex_Release);
955 EXPORT(Mutex_IsLocked);