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 //giFreeTickets = gThreadZero.NumTickets;
94 giNumActiveThreads = 1;
100 * \fn void Threads_SetName(char *NewName)
101 * \brief Sets the current thread's name
103 int Threads_SetName(char *NewName)
105 tThread *cur = Proc_GetCurThread();
106 char *oldname = cur->ThreadName;
108 cur->ThreadName = NULL;
110 if( IsHeap(oldname) ) free( oldname );
112 cur->ThreadName = strdup(NewName);
117 * \fn char *Threads_GetName(int ID)
118 * \brief Gets a thread's name
120 char *Threads_GetName(int ID)
123 return Proc_GetCurThread()->ThreadName;
125 // TODO: Find a thread and get its name
130 * \fn void Threads_SetTickets(tThread *Thread, int Num)
131 * \brief Sets the 'priority' of a task
133 void Threads_SetTickets(tThread *Thread, int Num)
136 Thread = Proc_GetCurThread();
138 if(Num > MAX_TICKETS) Num = MAX_TICKETS;
140 if( Thread != Proc_GetCurThread() ) {
141 SHORTLOCK( &glThreadListLock );
142 giFreeTickets -= Thread->NumTickets - Num;
143 Thread->NumTickets = Num;
144 SHORTREL( &glThreadListLock );
147 Thread->NumTickets = Num;
151 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
153 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
157 cur = Proc_GetCurThread();
159 new = malloc(sizeof(tThread));
164 memcpy(new, cur, sizeof(tThread));
168 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
169 new->Status = THREAD_STAT_ACTIVE;
173 new->TID = giNextTID++;
177 new->ThreadName = strdup(cur->ThreadName);
179 // Set Thread Group ID (PID)
181 new->TGID = new->TID;
183 new->TGID = cur->TGID;
185 // Messages are not inherited
186 new->Messages = NULL;
187 new->LastMessage = NULL;
190 new->Remaining = new->Quantum = cur->Quantum;
191 new->NumTickets = cur->NumTickets;
193 // Set Signal Handlers
194 new->CurFaultNum = 0;
195 new->FaultHandler = cur->FaultHandler;
197 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
199 switch(cCONFIG_TYPES[i])
202 new->Config[i] = cur->Config[i];
206 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
213 // Maintain a global list of threads
214 SHORTLOCK( &glThreadListLock );
215 new->GlobalPrev = NULL; // Protect against bugs
216 new->GlobalNext = gAllThreads;
218 SHORTREL( &glThreadListLock );
224 * \fn Uint *Threads_GetCfgPtr(int Id)
226 Uint *Threads_GetCfgPtr(int Id)
228 if(Id < 0 || Id >= NUM_CFG_ENTRIES) {
229 Warning("Threads_GetCfgPtr: Index %i is out of bounds", Id);
233 return &Proc_GetCurThread()->Config[Id];
237 * \fn tTID Threads_WaitTID(int TID, int *status)
238 * \brief Wait for a task to change state
239 * \return TID of child that changed state
241 int Threads_WaitTID(int TID, int *status)
245 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
249 // Any peer/child thread
251 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
257 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
263 tThread *t = Threads_GetThread(TID);
264 int initStatus = t->Status;
267 if(initStatus != THREAD_STAT_ZOMBIE) {
268 // TODO: Handle child also being suspended if wanted
269 while(t->Status != THREAD_STAT_ZOMBIE) {
271 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
272 Threads_GetTID(), t->TID, t->Status);
276 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
277 Threads_GetTID(), t->TID, t->Status);
281 case THREAD_STAT_ZOMBIE:
283 t->Status = THREAD_STAT_DEAD;
284 // TODO: Child return value?
285 if(status) *status = 0;
286 // add to delete queue
287 Threads_AddToDelete( t );
290 if(status) *status = -1;
300 * \fn tThread *Threads_GetThread(Uint TID)
301 * \brief Gets a thread given its TID
302 * \param TID Thread ID
304 tThread *Threads_GetThread(Uint TID)
308 // Search global list
309 for(thread = gAllThreads;
311 thread = thread->GlobalNext)
313 if(thread->TID == TID)
321 * \fn void Threads_AddToDelete(tThread *Thread)
322 * \brief Adds a thread to the delete queue
324 void Threads_AddToDelete(tThread *Thread)
326 // Add to delete queue
328 Thread->Next = gDeleteThreads;
329 gDeleteThreads = Thread;
332 gDeleteThreads = Thread;
337 * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
338 * \brief Gets the previous entry in a thead linked list
340 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
344 if(*List == Thread) {
345 return (tThread*)List;
348 ret->Next && ret->Next != Thread;
351 // Error if the thread is not on the list
352 if(!ret->Next || ret->Next != Thread) {
360 * \fn void Threads_Exit(int TID, int Status)
361 * \brief Exit the current process
363 void Threads_Exit(int TID, int Status)
366 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
368 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
369 // Halt forever, just in case
375 * \fn void Threads_Kill(tThread *Thread, int Status)
376 * \brief Kill a thread
377 * \param Thread Thread to kill
378 * \param Status Status code to return to the parent
380 void Threads_Kill(tThread *Thread, int Status)
389 for(child = gActiveThreads;
393 if(child->PTID == Thread->TID)
394 Threads_Kill(child, -1);
399 ///\note Double lock is needed due to overlap of lock areas
401 // Lock thread (stop us recieving messages)
402 SHORTLOCK( &Thread->IsLocked );
405 SHORTLOCK( &glThreadListLock );
407 // Get previous thread on list
408 prev = Threads_int_GetPrev( &gActiveThreads, Thread );
410 Warning("Proc_Exit - Current thread is not on the active queue");
411 SHORTREL( &Thread->IsLocked );
412 SHORTREL( &glThreadListLock );
416 // Clear Message Queue
417 while( Thread->Messages )
419 msg = Thread->Messages->Next;
420 free( Thread->Messages );
421 Thread->Messages = msg;
424 Thread->Remaining = 0; // Clear Remaining Quantum
425 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
426 prev->Next = Thread->Next; // Remove from active
428 giNumActiveThreads --;
429 if( Thread != Proc_GetCurThread() )
430 giFreeTickets -= Thread->NumTickets;
433 Thread->RetStatus = Status;
435 // Don't Zombie if we are being killed as part of a tree
438 Thread->Status = THREAD_STAT_DEAD;
439 Threads_AddToDelete( Thread );
441 Thread->Status = THREAD_STAT_ZOMBIE;
443 Threads_Wake( Thread->Parent );
446 Log("Thread %i went *hurk* (%i)", Thread->TID, Thread->Status);
449 SHORTREL( &Thread->IsLocked );
450 SHORTREL( &glThreadListLock );
452 if(Status != -1) HALT();
456 * \fn void Threads_Yield(void)
457 * \brief Yield remainder of timeslice
459 void Threads_Yield(void)
461 Proc_GetCurThread()->Remaining = 0;
466 * \fn void Threads_Sleep(void)
467 * \brief Take the current process off the run queue
469 void Threads_Sleep(void)
471 tThread *cur = Proc_GetCurThread();
473 //Log_Log("Threads", "%i going to sleep", cur->TID);
476 SHORTLOCK( &glThreadListLock );
478 // Don't sleep if there is a message waiting
479 if( cur->Messages ) {
480 SHORTREL( &glThreadListLock );
484 // Remove us from running queue
487 // Add to Sleeping List (at the top)
488 cur->Next = gSleepingThreads;
489 gSleepingThreads = cur;
491 // Mark thread as sleeping
492 cur->Status = THREAD_STAT_SLEEPING;
494 #if DEBUG_TRACE_STATE
495 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
499 SHORTREL( &glThreadListLock );
501 while(cur->Status != THREAD_STAT_ACTIVE) HALT();
506 * \fn int Threads_Wake( tThread *Thread )
507 * \brief Wakes a sleeping/waiting thread up
508 * \param Thread Thread to wake
509 * \return Boolean Failure (Returns ERRNO)
510 * \note Should be called with the scheduler lock held
512 int Threads_Wake(tThread *Thread)
519 switch(Thread->Status)
521 case THREAD_STAT_ACTIVE:
522 Log("Thread_Wake: Waking awake thread (%i)", Thread->TID);
525 case THREAD_STAT_SLEEPING: // TODO: Comment better
526 // Remove from sleeping queue
527 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
528 prev->Next = Thread->Next;
529 // Add to active queue
530 Thread->Next = gActiveThreads;
531 gActiveThreads = Thread;
532 // Update bookkeeping
533 giNumActiveThreads ++;
534 giFreeTickets += Thread->NumTickets;
535 #if DEBUG_TRACE_TICKETS
536 Log("Threads_Wake: new giFreeTickets = %i", giFreeTickets);
539 Thread->Status = THREAD_STAT_ACTIVE;
540 #if DEBUG_TRACE_STATE
541 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
545 case THREAD_STAT_WAITING:
546 Warning("Thread_Wake - Waiting threads are not currently supported");
549 case THREAD_STAT_DEAD:
550 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
554 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
560 * \brief Wake a thread given the TID
561 * \param TID Thread ID to wake
562 * \return Boolean Faulure (errno)
564 int Threads_WakeTID(tTID TID)
566 tThread *thread = Threads_GetThread(TID);
570 SHORTLOCK( &glThreadListLock );
571 ret = Threads_Wake( thread );
572 SHORTREL( &glThreadListLock );
577 * \fn void Threads_AddActive(tThread *Thread)
578 * \brief Adds a thread to the active queue
580 void Threads_AddActive(tThread *Thread)
582 SHORTLOCK( &glThreadListLock );
583 // Add to active list
584 Thread->Next = gActiveThreads;
585 gActiveThreads = Thread;
586 // Update bookkeeping
587 giNumActiveThreads ++;
588 giFreeTickets += Thread->NumTickets;
589 #if DEBUG_TRACE_TICKETS
590 Log("Threads_AddActive: new giFreeTickets = %i", giFreeTickets);
592 SHORTREL( &glThreadListLock );
596 * \brief Removes the current thread from the active queue
597 * \warning This should ONLY be called with task switches disabled
598 * \return Current thread pointer
600 tThread *Threads_RemActive(void)
602 tThread *ret = Proc_GetCurThread();
603 tThread *prev = Threads_int_GetPrev(&gActiveThreads, ret);
604 if(!prev) return NULL;
607 prev->Next = ret->Next;
608 giNumActiveThreads --;
613 * \fn void Threads_SetFaultHandler(Uint Handler)
614 * \brief Sets the signal handler for a signal
616 void Threads_SetFaultHandler(Uint Handler)
618 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
619 Proc_GetCurThread()->FaultHandler = Handler;
623 * \fn void Threads_Fault(int Num)
624 * \brief Calls a fault handler
626 void Threads_Fault(int Num)
628 tThread *thread = Proc_GetCurThread();
630 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
634 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
636 switch(thread->FaultHandler)
639 Threads_Kill(thread, -1);
642 case 1: // Dump Core?
643 Threads_Kill(thread, -1);
648 // Double Fault? Oh, F**k
649 if(thread->CurFaultNum != 0) {
650 Threads_Kill(thread, -1); // For now, just kill
654 thread->CurFaultNum = Num;
656 Proc_CallFaultHandler(thread);
659 // --- Process Structure Access Functions ---
660 tPID Threads_GetPID(void)
662 return Proc_GetCurThread()->TGID;
664 tTID Threads_GetTID(void)
666 return Proc_GetCurThread()->TID;
668 tUID Threads_GetUID(void)
670 return Proc_GetCurThread()->UID;
672 tGID Threads_GetGID(void)
674 return Proc_GetCurThread()->GID;
677 int Threads_SetUID(Uint *Errno, tUID ID)
679 tThread *t = Proc_GetCurThread();
684 Log_Debug("Threads", "TID %i's UID set to %i", t->TID, ID);
689 int Threads_SetGID(Uint *Errno, tGID ID)
691 tThread *t = Proc_GetCurThread();
696 Log_Debug("Threads", "TID %i's GID set to %i", t->TID, ID);
702 * \fn void Threads_Dump(void)
703 * \brief Dums a list of currently running threads
705 void Threads_Dump(void)
709 Log("Active Threads:");
710 for(thread=gActiveThreads;thread;thread=thread->Next)
712 Log(" %i (%i) - %s (CPU %i)",
713 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
714 Log(" State: %i", thread->Status);
715 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
716 Log(" KStack 0x%x", thread->KernelStack);
720 for(thread=gAllThreads;thread;thread=thread->GlobalNext)
722 Log(" %i (%i) - %s (CPU %i)",
723 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
724 Log(" State: %i", thread->Status);
725 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
726 Log(" KStack 0x%x", thread->KernelStack);
731 * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
732 * \brief Gets the next thread to run
733 * \param CPU Current CPU
734 * \param Last The thread the CPU was running
736 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
743 SHORTLOCK( &glThreadListLock );
745 // Clear Delete Queue
746 while(gDeleteThreads)
748 thread = gDeleteThreads->Next;
749 if( IS_LOCKED(&gDeleteThreads->IsLocked) ) { // Only free if structure is unused
751 gDeleteThreads->Status = THREAD_STAT_DEAD;
753 if( IsHeap(gDeleteThreads->ThreadName) )
754 free(gDeleteThreads->ThreadName);
755 // Remove from global list
756 if( gDeleteThreads == gAllThreads )
757 gAllThreads = gDeleteThreads->GlobalNext;
759 gDeleteThreads->GlobalPrev->GlobalNext = gDeleteThreads->GlobalNext;
760 free( gDeleteThreads );
762 gDeleteThreads = thread;
765 // No active threads, just take a nap
766 if(giNumActiveThreads == 0) {
767 SHORTREL( &glThreadListLock );
768 #if DEBUG_TRACE_TICKETS
769 Log("No active threads");
774 // Special case: 1 thread
775 if(giNumActiveThreads == 1) {
776 if( gActiveThreads->CurCPU == -1 )
777 gActiveThreads->CurCPU = CPU;
778 SHORTREL( &glThreadListLock );
779 if( gActiveThreads->CurCPU == CPU )
780 return gActiveThreads;
781 return NULL; // CPU has nothing to do
784 // Allow the old thread to be scheduled again
786 if( Last->Status == THREAD_STAT_ACTIVE ) {
787 giFreeTickets += Last->NumTickets;
788 #if DEBUG_TRACE_TICKETS
789 LogF(" CPU %i released %p (%i %s) into the pool (%i tickets in pool)\n",
790 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets);
793 #if DEBUG_TRACE_TICKETS
795 LogF(" %p (%s)->Status = %i\n", Last, Last->ThreadName, Last->Status);
802 for(thread=gActiveThreads;thread;thread=thread->Next) {
803 if(thread->CurCPU >= 0) continue;
804 number += thread->NumTickets;
806 if(number != giFreeTickets) {
807 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
808 giFreeTickets, number, CPU);
812 // No free tickets (all tasks delegated to cores)
813 if( giFreeTickets == 0 ) {
814 SHORTREL(&glThreadListLock);
818 // Get the ticket number
819 ticket = number = rand() % giFreeTickets;
821 // Find the next thread
822 for(thread=gActiveThreads;thread;thread=thread->Next)
824 if(thread->CurCPU >= 0) continue;
825 if(thread->NumTickets > number) break;
826 number -= thread->NumTickets;
832 for(thread=gActiveThreads;thread;thread=thread->Next) {
833 if(thread->CurCPU >= 0) continue;
834 number += thread->NumTickets;
836 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
837 giFreeTickets, number);
839 #if DEBUG_TRACE_TICKETS
840 LogF(" CPU%i giFreeTickets = %i\n", CPU, giFreeTickets);
843 // Make the new thread non-schedulable
844 giFreeTickets -= thread->NumTickets;
845 thread->CurCPU = CPU;
848 #if DEBUG_TRACE_TICKETS
849 LogF(" CPU%i giFreeTickets = %i, giving %p (%i %s CPU=%i)\n",
850 CPU, giFreeTickets, thread, thread->TID, thread->ThreadName, thread->CurCPU);
853 SHORTREL( &glThreadListLock );
859 * \fn void Threads_SegFault(tVAddr Addr)
860 * \brief Called when a Segment Fault occurs
862 void Threads_SegFault(tVAddr Addr)
864 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
866 //Threads_Exit( 0, -1 );
870 * \brief Acquire a heavy mutex
871 * \param Mutex Mutex to acquire
873 * This type of mutex checks if the mutex is avaliable, and acquires it
874 * if it is. Otherwise, the current thread is added to the mutex's wait
875 * queue and the thread suspends. When the holder of the mutex completes,
876 * the oldest thread (top thread) on the queue is given the lock and
879 void Mutex_Acquire(tMutex *Mutex)
881 tThread *us = Proc_GetCurThread();
884 SHORTLOCK( &Mutex->Protector );
886 //Log("Mutex_Acquire: (%p)", Mutex);
888 // Check if the lock is already held
890 SHORTLOCK( &glThreadListLock );
891 // - Remove from active list
893 // - Mark as sleeping
894 us->Status = THREAD_STAT_OFFSLEEP;
897 if(Mutex->LastWaiting) {
898 Mutex->LastWaiting->Next = us;
899 Mutex->LastWaiting = us;
903 Mutex->LastWaiting = us;
905 SHORTREL( &glThreadListLock );
906 SHORTREL( &Mutex->Protector );
907 while(us->Status == THREAD_STAT_OFFSLEEP) HALT();
908 // We're only woken when we get the lock
910 // Ooh, let's take it!
913 SHORTREL( &Mutex->Protector );
918 * \brief Release a held mutex
919 * \param Mutex Mutex to release
921 void Mutex_Release(tMutex *Mutex)
923 SHORTLOCK( &Mutex->Protector );
924 //Log("Mutex_Release: (%p)", Mutex);
925 if( Mutex->Waiting ) {
926 Mutex->Owner = Mutex->Waiting; // Set owner
927 Mutex->Waiting = Mutex->Waiting->Next; // Next!
929 Mutex->Owner->Status = THREAD_STAT_ACTIVE;
930 Threads_AddActive(Mutex->Owner);
931 Log("Mutex %p Woke %p", Mutex, Mutex->Owner);
936 SHORTREL( &Mutex->Protector );
940 * \brief Is this mutex locked?
941 * \param Mutex Mutex pointer
943 int Mutex_IsLocked(tMutex *Mutex)
945 return Mutex->Owner != NULL;
949 EXPORT(Threads_GetUID);