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; // Not needed, as ThreadZero is running
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)
388 // TODO: Kill all children
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( &glThreadListLock );
415 SHORTREL( &Thread->IsLocked );
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 * \warning This should ONLY be called with task switches disabled
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:
529 // Remove from sleeping queue
530 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
531 prev->Next = Thread->Next;
533 // Add to active queue
534 Thread->Next = gActiveThreads;
535 gActiveThreads = Thread;
537 // Update bookkeeping
538 giNumActiveThreads ++;
539 giFreeTickets += Thread->NumTickets;
540 #if DEBUG_TRACE_TICKETS
541 Log("Threads_Wake: new giFreeTickets = %i", giFreeTickets);
545 Thread->Status = THREAD_STAT_ACTIVE;
546 #if DEBUG_TRACE_STATE
547 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
551 case THREAD_STAT_WAITING:
552 Warning("Thread_Wake - Waiting threads are not currently supported");
555 case THREAD_STAT_DEAD:
556 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
560 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
566 * \brief Wake a thread given the TID
567 * \param TID Thread ID to wake
568 * \return Boolean Faulure (errno)
570 int Threads_WakeTID(tTID TID)
572 tThread *thread = Threads_GetThread(TID);
576 SHORTLOCK( &glThreadListLock );
577 ret = Threads_Wake( thread );
578 SHORTREL( &glThreadListLock );
579 //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
584 * \fn void Threads_AddActive(tThread *Thread)
585 * \brief Adds a thread to the active queue
587 void Threads_AddActive(tThread *Thread)
589 SHORTLOCK( &glThreadListLock );
590 // Add to active list
591 Thread->Next = gActiveThreads;
592 gActiveThreads = Thread;
593 // Update bookkeeping
594 giNumActiveThreads ++;
595 giFreeTickets += Thread->NumTickets;
596 #if DEBUG_TRACE_TICKETS
597 Log("Threads_AddActive: new giFreeTickets = %i", giFreeTickets);
599 SHORTREL( &glThreadListLock );
603 * \brief Removes the current thread from the active queue
604 * \warning This should ONLY be called with task switches disabled
605 * \return Current thread pointer
607 tThread *Threads_RemActive(void)
609 tThread *ret = Proc_GetCurThread();
610 tThread *prev = Threads_int_GetPrev(&gActiveThreads, ret);
611 if(!prev) return NULL;
614 prev->Next = ret->Next;
615 giNumActiveThreads --;
620 * \fn void Threads_SetFaultHandler(Uint Handler)
621 * \brief Sets the signal handler for a signal
623 void Threads_SetFaultHandler(Uint Handler)
625 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
626 Proc_GetCurThread()->FaultHandler = Handler;
630 * \fn void Threads_Fault(int Num)
631 * \brief Calls a fault handler
633 void Threads_Fault(int Num)
635 tThread *thread = Proc_GetCurThread();
637 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
641 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
643 switch(thread->FaultHandler)
646 Threads_Kill(thread, -1);
649 case 1: // Dump Core?
650 Threads_Kill(thread, -1);
655 // Double Fault? Oh, F**k
656 if(thread->CurFaultNum != 0) {
657 Threads_Kill(thread, -1); // For now, just kill
661 thread->CurFaultNum = Num;
663 Proc_CallFaultHandler(thread);
666 // --- Process Structure Access Functions ---
667 tPID Threads_GetPID(void)
669 return Proc_GetCurThread()->TGID;
671 tTID Threads_GetTID(void)
673 return Proc_GetCurThread()->TID;
675 tUID Threads_GetUID(void)
677 return Proc_GetCurThread()->UID;
679 tGID Threads_GetGID(void)
681 return Proc_GetCurThread()->GID;
684 int Threads_SetUID(Uint *Errno, tUID ID)
686 tThread *t = Proc_GetCurThread();
691 Log_Debug("Threads", "TID %i's UID set to %i", t->TID, ID);
696 int Threads_SetGID(Uint *Errno, tGID ID)
698 tThread *t = Proc_GetCurThread();
703 Log_Debug("Threads", "TID %i's GID set to %i", t->TID, ID);
709 * \fn void Threads_Dump(void)
710 * \brief Dums a list of currently running threads
712 void Threads_Dump(void)
716 Log("Active Threads:");
717 for(thread=gActiveThreads;thread;thread=thread->Next)
719 Log(" %i (%i) - %s (CPU %i)",
720 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
721 if(thread->Status != THREAD_STAT_ACTIVE)
722 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
723 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
724 Log(" KStack 0x%x", thread->KernelStack);
728 for(thread=gAllThreads;thread;thread=thread->GlobalNext)
730 Log(" %i (%i) - %s (CPU %i)",
731 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
732 Log(" State %i", thread->Status);
733 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
734 Log(" KStack 0x%x", thread->KernelStack);
739 * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
740 * \brief Gets the next thread to run
741 * \param CPU Current CPU
742 * \param Last The thread the CPU was running
744 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
751 SHORTLOCK( &glThreadListLock );
753 // Clear Delete Queue
754 // - I should probably put this in a worker thread to avoid calling free() in the scheduler
755 while(gDeleteThreads)
757 thread = gDeleteThreads->Next;
758 if( IS_LOCKED(&gDeleteThreads->IsLocked) ) { // Only free if structure is unused
760 gDeleteThreads->Status = THREAD_STAT_DEAD;
762 if( IsHeap(gDeleteThreads->ThreadName) )
763 free(gDeleteThreads->ThreadName);
764 // Remove from global list
765 if( gDeleteThreads == gAllThreads )
766 gAllThreads = gDeleteThreads->GlobalNext;
768 gDeleteThreads->GlobalPrev->GlobalNext = gDeleteThreads->GlobalNext;
769 free( gDeleteThreads );
771 gDeleteThreads = thread;
774 // No active threads, just take a nap
775 if(giNumActiveThreads == 0) {
776 SHORTREL( &glThreadListLock );
777 #if DEBUG_TRACE_TICKETS
778 Log("No active threads");
783 // Special case: 1 thread
784 if(giNumActiveThreads == 1) {
785 if( gActiveThreads->CurCPU == -1 )
786 gActiveThreads->CurCPU = CPU;
787 SHORTREL( &glThreadListLock );
788 if( gActiveThreads->CurCPU == CPU )
789 return gActiveThreads;
790 return NULL; // CPU has nothing to do
793 // Allow the old thread to be scheduled again
795 if( Last->Status == THREAD_STAT_ACTIVE ) {
796 giFreeTickets += Last->NumTickets;
797 #if DEBUG_TRACE_TICKETS
798 LogF(" CPU %i released %p (%i %s) into the pool (%i tickets in pool)\n",
799 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets);
802 #if DEBUG_TRACE_TICKETS
804 LogF(" %p (%s)->Status = %i (Released)\n", Last, Last->ThreadName, Last->Status);
811 for(thread = gActiveThreads; thread; thread = thread->Next) {
812 if(thread->CurCPU >= 0) continue;
813 number += thread->NumTickets;
815 if(number != giFreeTickets) {
816 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
817 giFreeTickets, number, CPU);
821 // No free tickets (all tasks delegated to cores)
822 if( giFreeTickets == 0 ) {
823 SHORTREL(&glThreadListLock);
827 // Get the ticket number
828 ticket = number = rand() % giFreeTickets;
830 // Find the next thread
831 for(thread=gActiveThreads;thread;thread=thread->Next)
833 if(thread->CurCPU >= 0) continue;
834 if(thread->NumTickets > number) break;
835 number -= thread->NumTickets;
841 for(thread=gActiveThreads;thread;thread=thread->Next) {
842 if(thread->CurCPU >= 0) continue;
843 number += thread->NumTickets;
845 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
846 giFreeTickets, number);
848 #if DEBUG_TRACE_TICKETS
849 LogF(" CPU%i giFreeTickets = %i\n", CPU, giFreeTickets);
852 // Make the new thread non-schedulable
853 giFreeTickets -= thread->NumTickets;
854 thread->CurCPU = CPU;
857 #if DEBUG_TRACE_TICKETS
858 LogF(" CPU%i giFreeTickets = %i, giving %p (%i %s CPU=%i)\n",
859 CPU, giFreeTickets, thread, thread->TID, thread->ThreadName, thread->CurCPU);
862 SHORTREL( &glThreadListLock );
868 * \fn void Threads_SegFault(tVAddr Addr)
869 * \brief Called when a Segment Fault occurs
871 void Threads_SegFault(tVAddr Addr)
873 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
875 //Threads_Exit( 0, -1 );
879 * \brief Acquire a heavy mutex
880 * \param Mutex Mutex to acquire
882 * This type of mutex checks if the mutex is avaliable, and acquires it
883 * if it is. Otherwise, the current thread is added to the mutex's wait
884 * queue and the thread suspends. When the holder of the mutex completes,
885 * the oldest thread (top thread) on the queue is given the lock and
888 void Mutex_Acquire(tMutex *Mutex)
890 tThread *us = Proc_GetCurThread();
893 SHORTLOCK( &Mutex->Protector );
895 //Log("Mutex_Acquire: (%p)", Mutex);
897 // Check if the lock is already held
899 SHORTLOCK( &glThreadListLock );
900 // - Remove from active list
902 // - Mark as sleeping
903 us->Status = THREAD_STAT_OFFSLEEP;
906 if(Mutex->LastWaiting) {
907 Mutex->LastWaiting->Next = us;
908 Mutex->LastWaiting = us;
912 Mutex->LastWaiting = us;
914 SHORTREL( &glThreadListLock );
915 SHORTREL( &Mutex->Protector );
916 while(us->Status == THREAD_STAT_OFFSLEEP) Threads_Yield();
917 // We're only woken when we get the lock
919 // Ooh, let's take it!
922 SHORTREL( &Mutex->Protector );
927 * \brief Release a held mutex
928 * \param Mutex Mutex to release
930 void Mutex_Release(tMutex *Mutex)
932 SHORTLOCK( &Mutex->Protector );
933 //Log("Mutex_Release: (%p)", Mutex);
934 if( Mutex->Waiting ) {
935 Mutex->Owner = Mutex->Waiting; // Set owner
936 Mutex->Waiting = Mutex->Waiting->Next; // Next!
938 Mutex->Owner->Status = THREAD_STAT_ACTIVE;
939 Threads_AddActive(Mutex->Owner);
940 //Log("Mutex %p Woke %p", Mutex, Mutex->Owner);
945 SHORTREL( &Mutex->Protector );
949 * \brief Is this mutex locked?
950 * \param Mutex Mutex pointer
952 int Mutex_IsLocked(tMutex *Mutex)
954 return Mutex->Owner != NULL;
958 EXPORT(Threads_GetUID);
959 EXPORT(Mutex_Acquire);
960 EXPORT(Mutex_Release);
961 EXPORT(Mutex_IsLocked);