4 * - Common Thread Control
10 #define DEBUG_TRACE_TICKETS 0
13 #define DEFAULT_QUANTUM 10
14 #define DEFAULT_TICKETS 5
15 #define MAX_TICKETS 10
16 const enum eConfigTypes cCONFIG_TYPES[] = {
17 CFGT_HEAPSTR, // e.g. CFG_VFS_CWD
18 CFGT_INT, // e.g. CFG_VFS_MAXFILES
23 extern void ArchThreads_Init(void);
24 extern void Proc_Start(void);
25 extern tThread *Proc_GetCurThread(void);
26 extern int Proc_Clone(Uint *Err, Uint Flags);
27 extern void Proc_CallFaultHandler(tThread *Thread);
30 void Threads_Init(void);
31 int Threads_SetName(char *NewName);
32 char *Threads_GetName(int ID);
33 void Threads_SetTickets(tThread *Thread, int Num);
34 tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
35 int Threads_WaitTID(int TID, int *status);
36 tThread *Threads_GetThread(Uint TID);
37 void Threads_AddToDelete(tThread *Thread);
38 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread);
39 void Threads_Exit(int TID, int Status);
40 void Threads_Kill(tThread *Thread, int Status);
41 void Threads_Yield(void);
42 void Threads_Sleep(void);
43 int Threads_Wake(tThread *Thread);
44 void Threads_AddActive(tThread *Thread);
45 int Threads_GetPID(void);
46 int Threads_GetTID(void);
47 tUID Threads_GetUID(void);
48 int Threads_SetUID(Uint *Errno, tUID ID);
49 tGID Threads_GetGID(void);
50 int Threads_SetGID(Uint *Errno, tUID ID);
51 void Threads_Dump(void);
52 void Mutex_Acquire(tMutex *Mutex);
53 void Mutex_Release(tMutex *Mutex);
54 int Mutex_IsLocked(tMutex *Mutex);
58 // Only used for the core kernel
59 tThread gThreadZero = {
60 Status: THREAD_STAT_ACTIVE, // Status
61 ThreadName: "ThreadZero", // Name
62 Quantum: DEFAULT_QUANTUM, // Default Quantum
63 Remaining: DEFAULT_QUANTUM, // Current Quantum
64 NumTickets: DEFAULT_TICKETS // Number of tickets
68 tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
69 // --- Current State ---
70 volatile int giNumActiveThreads = 0;
71 //volatile int giTotalTickets = 0;
72 volatile int giFreeTickets = 0;
73 volatile Uint giNextTID = 1;
74 // --- Thread Lists ---
75 tThread *gActiveThreads = NULL; // Currently Running Threads
76 tThread *gSleepingThreads = NULL; // Sleeping Threads
77 tThread *gDeleteThreads = NULL; // Threads to delete
82 * \fn void Threads_Init(void)
83 * \brief Initialse the thread list
85 void Threads_Init(void)
89 // Create Initial Task
90 gActiveThreads = &gThreadZero;
91 //giFreeTickets = gThreadZero.NumTickets;
92 giNumActiveThreads = 1;
98 * \fn void Threads_SetName(char *NewName)
99 * \brief Sets the current thread's name
101 int Threads_SetName(char *NewName)
103 tThread *cur = Proc_GetCurThread();
104 char *oldname = cur->ThreadName;
106 cur->ThreadName = NULL;
108 if( IsHeap(oldname) ) free( oldname );
110 cur->ThreadName = strdup(NewName);
115 * \fn char *Threads_GetName(int ID)
116 * \brief Gets a thread's name
118 char *Threads_GetName(int ID)
121 return Proc_GetCurThread()->ThreadName;
123 // TODO: Find a thread and get its name
128 * \fn void Threads_SetTickets(tThread *Thread, int Num)
129 * \brief Sets the 'priority' of a task
131 void Threads_SetTickets(tThread *Thread, int Num)
134 Thread = Proc_GetCurThread();
136 if(Num > MAX_TICKETS) Num = MAX_TICKETS;
138 if( Thread != Proc_GetCurThread() ) {
139 SHORTLOCK( &glThreadListLock );
140 giFreeTickets -= Thread->NumTickets - Num;
141 Thread->NumTickets = Num;
142 SHORTREL( &glThreadListLock );
145 Thread->NumTickets = Num;
149 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
151 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
155 cur = Proc_GetCurThread();
157 new = malloc(sizeof(tThread));
162 memcpy(new, cur, sizeof(tThread));
166 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
167 new->Status = THREAD_STAT_ACTIVE;
171 new->TID = giNextTID++;
172 new->PTID = cur->TID;
175 new->ThreadName = strdup(cur->ThreadName);
177 // Set Thread Group ID (PID)
179 new->TGID = new->TID;
181 new->TGID = cur->TGID;
183 // Messages are not inherited
184 new->Messages = NULL;
185 new->LastMessage = NULL;
188 new->Remaining = new->Quantum = cur->Quantum;
189 new->NumTickets = cur->NumTickets;
191 // Set Signal Handlers
192 new->CurFaultNum = 0;
193 new->FaultHandler = cur->FaultHandler;
195 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
197 switch(cCONFIG_TYPES[i])
200 new->Config[i] = cur->Config[i];
204 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
215 * \fn Uint *Threads_GetCfgPtr(int Id)
217 Uint *Threads_GetCfgPtr(int Id)
219 if(Id < 0 || Id >= NUM_CFG_ENTRIES) {
220 Warning("Threads_GetCfgPtr: Index %i is out of bounds", Id);
224 return &Proc_GetCurThread()->Config[Id];
228 * \fn void Threads_WaitTID(int TID, int *status)
229 * \brief Wait for a task to change state
231 int Threads_WaitTID(int TID, int *status)
239 // Any peer/child thread
252 tThread *t = Threads_GetThread(TID);
253 int initStatus = t->Status;
256 if(initStatus != THREAD_STAT_ZOMBIE) {
257 while(t->Status == initStatus) {
265 case THREAD_STAT_ZOMBIE:
266 t->Status = THREAD_STAT_DEAD;
267 if(status) *status = 0;
268 Threads_AddToDelete( t );
271 if(status) *status = -1;
281 * \fn tThread *Threads_GetThread(Uint TID)
282 * \brief Gets a thread given its TID
283 * \param TID Thread ID
285 tThread *Threads_GetThread(Uint TID)
289 // Search Active List
290 for(thread = gActiveThreads;
292 thread = thread->Next)
294 if(thread->TID == TID)
298 // Search Sleeping List
299 for(thread = gSleepingThreads;
301 thread = thread->Next)
303 if(thread->TID == TID)
311 * \fn void Threads_AddToDelete(tThread *Thread)
312 * \brief Adds a thread to the delete queue
314 void Threads_AddToDelete(tThread *Thread)
316 // Add to delete queue
318 Thread->Next = gDeleteThreads;
319 gDeleteThreads = Thread;
322 gDeleteThreads = Thread;
327 * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
328 * \brief Gets the previous entry in a thead linked list
330 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
334 if(*List == Thread) {
335 return (tThread*)List;
338 ret->Next && ret->Next != Thread;
341 // Error if the thread is not on the list
342 if(!ret->Next || ret->Next != Thread) {
350 * \fn void Threads_Exit(int TID, int Status)
351 * \brief Exit the current process
353 void Threads_Exit(int TID, int Status)
356 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
358 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
359 // Halt forever, just in case
365 * \fn void Threads_Kill(tThread *Thread, int Status)
366 * \brief Kill a thread
367 * \param Thread Thread to kill
368 * \param Status Status code to return to the parent
370 void Threads_Kill(tThread *Thread, int Status)
379 for(child = gActiveThreads;
383 if(child->PTID == Thread->TID)
384 Threads_Kill(child, -1);
389 ///\note Double lock is needed due to overlap of lock areas
391 // Lock thread (stop us recieving messages)
392 SHORTLOCK( &Thread->IsLocked );
395 SHORTLOCK( &glThreadListLock );
397 // Get previous thread on list
398 prev = Threads_int_GetPrev( &gActiveThreads, Thread );
400 Warning("Proc_Exit - Current thread is not on the active queue");
401 Thread->IsLocked.Lock = 0; // We can't use SHORTREL as that starts IRQs again
402 SHORTREL( &glThreadListLock );
406 // Clear Message Queue
407 while( Thread->Messages )
409 msg = Thread->Messages->Next;
410 free( Thread->Messages );
411 Thread->Messages = msg;
414 Thread->Remaining = 0; // Clear Remaining Quantum
415 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
416 prev->Next = Thread->Next; // Remove from active
418 giNumActiveThreads --;
419 if( Thread != Proc_GetCurThread() )
420 giFreeTickets -= Thread->NumTickets;
421 //Log("Threads_Kill: giFreeTickets = %i", giFreeTickets);
423 // Mark thread as a zombie
424 Thread->RetStatus = Status;
426 // Don't Zombie if we are being killed as part of a tree
429 Thread->Status = THREAD_STAT_DEAD;
430 Threads_AddToDelete( Thread );
432 Thread->Status = THREAD_STAT_ZOMBIE;
436 Thread->IsLocked.Lock = 0; // Released first so that it IS released
437 SHORTREL( &glThreadListLock );
439 //Log("Thread %i went *hurk*", Thread->TID);
441 if(Status != -1) HALT();
445 * \fn void Threads_Yield(void)
446 * \brief Yield remainder of timeslice
448 void Threads_Yield(void)
450 Proc_GetCurThread()->Remaining = 0;
455 * \fn void Threads_Sleep(void)
456 * \brief Take the current process off the run queue
458 void Threads_Sleep(void)
460 tThread *cur = Proc_GetCurThread();
463 //Log_Log("Threads", "%i going to sleep", cur->TID);
466 SHORTLOCK( &glThreadListLock );
468 // Get thread before current thread
469 thread = Threads_int_GetPrev( &gActiveThreads, cur );
471 Warning("Threads_Sleep - Current thread is not on the active queue");
473 SHORTREL( &glThreadListLock );
477 // Don't sleep if there is a message waiting
478 if( cur->Messages ) {
479 SHORTREL( &glThreadListLock );
483 // Unset remaining timeslices (force a task switch on timer fire)
486 // Remove from active list
487 thread->Next = cur->Next;
489 // Add to Sleeping List (at the top)
490 cur->Next = gSleepingThreads;
491 gSleepingThreads = cur;
493 // Reduce the active count & ticket count
494 giNumActiveThreads --;
495 // - No need to alter giFreeTickets (we're being executed)
497 // Mark thread as sleeping
498 cur->Status = THREAD_STAT_SLEEPING;
501 SHORTREL( &glThreadListLock );
503 while(cur->Status != THREAD_STAT_ACTIVE) HALT();
508 * \fn int Threads_Wake( tThread *Thread )
509 * \brief Wakes a sleeping/waiting thread up
510 * \param Thread Thread to wake
511 * \return Boolean Failure (Returns ERRNO)
513 int Threads_Wake(tThread *Thread)
520 switch(Thread->Status)
522 case THREAD_STAT_ACTIVE:
523 Log("Thread_Wake: Waking awake thread (%i)", Thread->TID);
525 case THREAD_STAT_SLEEPING: // TODO: Comment better
526 //Log_Log("Threads", "Waking %i (%p) from sleeping (CPU=%i)",
527 // Thread->TID, Thread, Thread->CurCPU);
528 SHORTLOCK( &glThreadListLock );
529 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
530 prev->Next = Thread->Next; // Remove from sleeping queue
531 Thread->Next = gActiveThreads; // Add to active queue
532 gActiveThreads = Thread;
533 giNumActiveThreads ++;
534 // Thread can't be the current, so no need to check
536 giFreeTickets += Thread->NumTickets;
537 #if DEBUG_TRACE_TICKETS
538 Log("Threads_Wake: giFreeTickets = %i", giFreeTickets);
540 Thread->Status = THREAD_STAT_ACTIVE;
541 SHORTREL( &glThreadListLock );
543 case THREAD_STAT_WAITING:
544 Warning("Thread_Wake - Waiting threads are not currently supported");
546 case THREAD_STAT_DEAD:
547 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
550 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
556 * \brief Wake a thread given the TID
557 * \param TID Thread ID to wake
558 * \return Boolean Faulure (errno)
560 int Threads_WakeTID(tTID TID)
562 tThread *thread = Threads_GetThread(TID);
565 return Threads_Wake( thread );
569 * \fn void Threads_AddActive(tThread *Thread)
570 * \brief Adds a thread to the active queue
572 void Threads_AddActive(tThread *Thread)
574 SHORTLOCK( &glThreadListLock );
575 Thread->Next = gActiveThreads;
576 gActiveThreads = Thread;
577 giNumActiveThreads ++;
578 // Thread can't be the current, so no need to check
579 giFreeTickets += Thread->NumTickets;
580 #if DEBUG_TRACE_TICKETS
581 Log("Threads_AddActive: giFreeTickets = %i", giFreeTickets);
583 SHORTREL( &glThreadListLock );
587 * \fn void Threads_SetFaultHandler(Uint Handler)
588 * \brief Sets the signal handler for a signal
590 void Threads_SetFaultHandler(Uint Handler)
592 Log_Log("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
593 Proc_GetCurThread()->FaultHandler = Handler;
597 * \fn void Threads_Fault(int Num)
598 * \brief Calls a fault handler
600 void Threads_Fault(int Num)
602 tThread *thread = Proc_GetCurThread();
604 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
608 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
610 switch(thread->FaultHandler)
613 Threads_Kill(thread, -1);
616 case 1: // Dump Core?
617 Threads_Kill(thread, -1);
622 // Double Fault? Oh, F**k
623 if(thread->CurFaultNum != 0) {
624 Threads_Kill(thread, -1); // For now, just kill
628 thread->CurFaultNum = Num;
630 Proc_CallFaultHandler(thread);
633 // --- Process Structure Access Functions ---
634 tPID Threads_GetPID(void)
636 return Proc_GetCurThread()->TGID;
638 tTID Threads_GetTID(void)
640 return Proc_GetCurThread()->TID;
642 tUID Threads_GetUID(void)
644 return Proc_GetCurThread()->UID;
646 tGID Threads_GetGID(void)
648 return Proc_GetCurThread()->GID;
651 int Threads_SetUID(Uint *Errno, tUID ID)
653 tThread *t = Proc_GetCurThread();
658 Log("Threads_SetUID - Setting User ID to %i", ID);
663 int Threads_SetGID(Uint *Errno, tGID ID)
665 tThread *t = Proc_GetCurThread();
670 Log("Threads_SetGID - Setting Group ID to %i", ID);
676 * \fn void Threads_Dump(void)
677 * \brief Dums a list of currently running threads
679 void Threads_Dump(void)
683 Log("Active Threads:");
684 for(thread=gActiveThreads;thread;thread=thread->Next)
686 Log(" %i (%i) - %s (CPU %i)",
687 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
688 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
689 Log(" KStack 0x%x", thread->KernelStack);
691 Log("Sleeping Threads:");
692 for(thread=gSleepingThreads;thread;thread=thread->Next)
695 thread->TID, thread->TGID, thread->ThreadName);
696 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
697 Log(" KStack 0x%x", thread->KernelStack);
702 * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
703 * \brief Gets the next thread to run
704 * \param CPU Current CPU
705 * \param Last The thread the CPU was running
707 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
713 // TODO: Enable the code to tell if the current CPU has the lock or
716 // Check if the thread list is locked by other code
717 // - If so, don't switch (give it a chance to complete)
718 if( IS_LOCKED(&glThreadListLock) )
721 // Clear Delete Queue
722 while(gDeleteThreads)
724 thread = gDeleteThreads->Next;
725 if( IS_LOCKED(&gDeleteThreads->IsLocked) ) { // Only free if structure is unused
726 gDeleteThreads->Status = THREAD_STAT_NULL;
727 free( gDeleteThreads );
729 gDeleteThreads = thread;
732 // No active threads, just take a nap
733 if(giNumActiveThreads == 0) {
734 #if DEBUG_TRACE_TICKETS
735 Log("No active threads");
741 // - HLT lock (Used because only another CPU can obtain the lock,
742 // but it has a potentially long lock period)
743 // - Well, this CPU can obtain the lock, but that is aliveviated by
745 SHORTLOCK( &glThreadListLock );
747 // Special case: 1 thread
748 if(giNumActiveThreads == 1) {
749 if( gActiveThreads->CurCPU == -1 )
750 gActiveThreads->CurCPU = CPU;
751 SHORTREL( &glThreadListLock );
752 if( gActiveThreads->CurCPU == CPU )
753 return gActiveThreads;
754 return NULL; // CPU has nothing to do
757 // Allow the old thread to be scheduled again
759 if( Last->Status == THREAD_STAT_ACTIVE ) {
760 giFreeTickets += Last->NumTickets;
761 #if DEBUG_TRACE_TICKETS
762 LogF(" CPU %i released %p (%s) into the pool (%i tickets in pool)\n",
763 CPU, Last, Last->ThreadName, Last->NumTickets);
766 #if DEBUG_TRACE_TICKETS
768 LogF(" %p (%s)->Status = %i\n", Last, Last->ThreadName, Last->Status);
775 for(thread=gActiveThreads;thread;thread=thread->Next) {
776 if(thread->CurCPU >= 0) continue;
777 number += thread->NumTickets;
779 if(number != giFreeTickets) {
780 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
781 giFreeTickets, number, CPU);
785 // No free tickets (all tasks delegated to cores)
786 if( giFreeTickets == 0 ) {
787 SHORTREL(&glThreadListLock);
791 // Get the ticket number
792 ticket = number = rand() % giFreeTickets;
794 // Find the next thread
795 for(thread=gActiveThreads;thread;thread=thread->Next)
797 if(thread->CurCPU >= 0) continue;
798 if(thread->NumTickets > number) break;
799 number -= thread->NumTickets;
805 for(thread=gActiveThreads;thread;thread=thread->Next) {
806 if(thread->CurCPU >= 0) continue;
807 number += thread->NumTickets;
809 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
810 giFreeTickets, number);
812 #if DEBUG_TRACE_TICKETS
813 LogF(" CPU%i giFreeTickets = %i\n", CPU, giFreeTickets);
816 // Make the new thread non-schedulable
817 giFreeTickets -= thread->NumTickets;
818 thread->CurCPU = CPU;
821 #if DEBUG_TRACE_TICKETS
822 LogF(" CPU%i giFreeTickets = %i, giving %p (%s CPU=%i)\n",
823 CPU, giFreeTickets, thread, thread->ThreadName, thread->CurCPU);
826 SHORTREL( &glThreadListLock );
832 * \fn void Threads_SegFault(tVAddr Addr)
833 * \brief Called when a Segment Fault occurs
835 void Threads_SegFault(tVAddr Addr)
837 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
839 //Threads_Exit( 0, -1 );
845 void Mutex_Acquire(tMutex *Mutex)
847 tThread *us = Proc_GetCurThread();
851 SHORTLOCK( &Mutex->Protector );
853 //Log("Mutex_Acquire: (%p)", Mutex);
855 // Check if the lock is already held
857 SHORTLOCK( &glThreadListLock );
858 // - Remove from active list
860 prev = Threads_int_GetPrev(&gActiveThreads, us);
861 prev->Next = us->Next;
862 giNumActiveThreads --;
863 us->Status = THREAD_STAT_SLEEPING;
866 if(Mutex->LastWaiting) {
867 Mutex->LastWaiting->Next = us;
868 Mutex->LastWaiting = us;
872 Mutex->LastWaiting = us;
874 SHORTREL( &glThreadListLock );
875 SHORTREL( &Mutex->Protector );
876 while(us->Status == THREAD_STAT_SLEEPING) HALT();
877 // We're only woken when we get the lock
879 // Ooh, let's take it!
882 SHORTREL( &Mutex->Protector );
887 * \brief Release a held spinlock
889 void Mutex_Release(tMutex *Mutex)
891 SHORTLOCK( &Mutex->Protector );
892 //Log("Mutex_Release: (%p)", Mutex);
893 if( Mutex->Waiting ) {
894 Mutex->Owner = Mutex->Waiting; // Set owner
895 Mutex->Waiting = Mutex->Waiting->Next; // Next!
897 Mutex->Owner->Status = THREAD_STAT_ACTIVE;
898 Threads_AddActive(Mutex->Owner);
899 Log("Mutex %p Woke %p", Mutex, Mutex->Owner);
904 SHORTREL( &Mutex->Protector );
907 int Mutex_IsLocked(tMutex *Mutex)
909 return Mutex->Owner != NULL;
913 EXPORT(Threads_GetUID);