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);
55 // Only used for the core kernel
56 tThread gThreadZero = {
57 Status: THREAD_STAT_ACTIVE, // Status
58 ThreadName: "ThreadZero", // Name
59 Quantum: DEFAULT_QUANTUM, // Default Quantum
60 Remaining: DEFAULT_QUANTUM, // Current Quantum
61 NumTickets: DEFAULT_TICKETS // Number of tickets
65 tSpinlock glThreadListLock = 0; ///\note NEVER use a heap function while locked
66 // --- Current State ---
67 volatile int giNumActiveThreads = 0;
68 //volatile int giTotalTickets = 0;
69 volatile int giFreeTickets = 0;
70 volatile Uint giNextTID = 1;
71 // --- Thread Lists ---
72 tThread *gActiveThreads = NULL; // Currently Running Threads
73 tThread *gSleepingThreads = NULL; // Sleeping Threads
74 tThread *gDeleteThreads = NULL; // Threads to delete
79 * \fn void Threads_Init(void)
80 * \brief Initialse the thread list
82 void Threads_Init(void)
86 // Create Initial Task
87 gActiveThreads = &gThreadZero;
88 //giFreeTickets = gThreadZero.NumTickets;
89 giNumActiveThreads = 1;
95 * \fn void Threads_SetName(char *NewName)
96 * \brief Sets the current thread's name
98 int Threads_SetName(char *NewName)
100 tThread *cur = Proc_GetCurThread();
101 char *oldname = cur->ThreadName;
103 cur->ThreadName = NULL;
105 if( IsHeap(oldname) ) free( oldname );
107 cur->ThreadName = strdup(NewName);
112 * \fn char *Threads_GetName(int ID)
113 * \brief Gets a thread's name
115 char *Threads_GetName(int ID)
118 return Proc_GetCurThread()->ThreadName;
120 // TODO: Find a thread and get its name
125 * \fn void Threads_SetTickets(tThread *Thread, int Num)
126 * \brief Sets the 'priority' of a task
128 void Threads_SetTickets(tThread *Thread, int Num)
131 Thread = Proc_GetCurThread();
133 if(Num > MAX_TICKETS) Num = MAX_TICKETS;
135 if( Thread != Proc_GetCurThread() ) {
136 LOCK( &glThreadListLock );
137 giFreeTickets -= Thread->NumTickets - Num;
138 Thread->NumTickets = Num;
139 RELEASE( &glThreadListLock );
142 Thread->NumTickets = Num;
146 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
148 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
152 cur = Proc_GetCurThread();
154 new = malloc(sizeof(tThread));
159 memcpy(new, cur, sizeof(tThread));
164 new->Status = THREAD_STAT_ACTIVE;
168 new->TID = giNextTID++;
169 new->PTID = cur->TID;
172 new->ThreadName = strdup(cur->ThreadName);
174 // Set Thread Group ID (PID)
176 new->TGID = new->TID;
178 new->TGID = cur->TGID;
180 // Messages are not inherited
181 new->Messages = NULL;
182 new->LastMessage = NULL;
185 new->Remaining = new->Quantum = cur->Quantum;
186 new->NumTickets = cur->NumTickets;
188 // Set Signal Handlers
189 new->CurFaultNum = 0;
190 new->FaultHandler = cur->FaultHandler;
192 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
194 switch(cCONFIG_TYPES[i])
197 new->Config[i] = cur->Config[i];
201 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
212 * \fn Uint *Threads_GetCfgPtr(int Id)
214 Uint *Threads_GetCfgPtr(int Id)
216 if(Id < 0 || Id >= NUM_CFG_ENTRIES) {
217 Warning("Threads_GetCfgPtr: Index %i is out of bounds", Id);
221 return &Proc_GetCurThread()->Config[Id];
225 * \fn void Threads_WaitTID(int TID, int *status)
226 * \brief Wait for a task to change state
228 int Threads_WaitTID(int TID, int *status)
236 // Any peer/child thread
249 tThread *t = Threads_GetThread(TID);
250 int initStatus = t->Status;
253 if(initStatus != THREAD_STAT_ZOMBIE) {
254 while(t->Status == initStatus) {
262 case THREAD_STAT_ZOMBIE:
263 t->Status = THREAD_STAT_DEAD;
264 if(status) *status = 0;
265 Threads_AddToDelete( t );
268 if(status) *status = -1;
278 * \fn tThread *Threads_GetThread(Uint TID)
279 * \brief Gets a thread given its TID
280 * \param TID Thread ID
282 tThread *Threads_GetThread(Uint TID)
286 // Search Active List
287 for(thread = gActiveThreads;
289 thread = thread->Next)
291 if(thread->TID == TID)
295 // Search Sleeping List
296 for(thread = gSleepingThreads;
298 thread = thread->Next)
300 if(thread->TID == TID)
308 * \fn void Threads_AddToDelete(tThread *Thread)
309 * \brief Adds a thread to the delete queue
311 void Threads_AddToDelete(tThread *Thread)
313 // Add to delete queue
315 Thread->Next = gDeleteThreads;
316 gDeleteThreads = Thread;
319 gDeleteThreads = Thread;
324 * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
325 * \brief Gets the previous entry in a thead linked list
327 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
331 if(*List == Thread) {
332 return (tThread*)List;
335 ret->Next && ret->Next != Thread;
338 // Error if the thread is not on the list
339 if(!ret->Next || ret->Next != Thread) {
347 * \fn void Threads_Exit(int TID, int Status)
348 * \brief Exit the current process
350 void Threads_Exit(int TID, int Status)
353 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
355 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
356 // Halt forever, just in case
362 * \fn void Threads_Kill(tThread *Thread, int Status)
363 * \brief Kill a thread
364 * \param Thread Thread to kill
365 * \param Status Status code to return to the parent
367 void Threads_Kill(tThread *Thread, int Status)
376 for(child = gActiveThreads;
380 if(child->PTID == Thread->TID)
381 Threads_Kill(child, -1);
386 ///\note Double lock is needed due to overlap of lock areas
388 // Lock thread (stop us recieving messages)
389 LOCK( &Thread->IsLocked );
392 LOCK( &glThreadListLock );
394 // Get previous thread on list
395 prev = Threads_int_GetPrev( &gActiveThreads, Thread );
397 Warning("Proc_Exit - Current thread is not on the active queue");
401 // Clear Message Queue
402 while( Thread->Messages )
404 msg = Thread->Messages->Next;
405 free( Thread->Messages );
406 Thread->Messages = msg;
409 Thread->Remaining = 0; // Clear Remaining Quantum
410 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
411 prev->Next = Thread->Next; // Remove from active
413 giNumActiveThreads --;
414 if( Thread != Proc_GetCurThread() )
415 giFreeTickets -= Thread->NumTickets;
416 //Log("Threads_Kill: giFreeTickets = %i", giFreeTickets);
418 // Mark thread as a zombie
419 Thread->RetStatus = Status;
421 // Don't Zombie if we are being killed as part of a tree
424 Thread->Status = THREAD_STAT_DEAD;
425 Threads_AddToDelete( Thread );
427 Thread->Status = THREAD_STAT_ZOMBIE;
431 RELEASE( &Thread->IsLocked ); // Released first so that it IS released
432 RELEASE( &glThreadListLock );
434 //Log("Thread %i went *hurk*", Thread->TID);
436 if(Status != -1) HALT();
440 * \fn void Threads_Yield(void)
441 * \brief Yield remainder of timeslice
443 void Threads_Yield(void)
445 Proc_GetCurThread()->Remaining = 0;
450 * \fn void Threads_Sleep(void)
451 * \brief Take the current process off the run queue
453 void Threads_Sleep(void)
455 tThread *cur = Proc_GetCurThread();
458 //Log_Log("Threads", "%i going to sleep", cur->TID);
461 LOCK( &glThreadListLock );
463 // Get thread before current thread
464 thread = Threads_int_GetPrev( &gActiveThreads, cur );
466 Warning("Threads_Sleep - Current thread is not on the active queue");
468 RELEASE( &glThreadListLock );
472 // Don't sleep if there is a message waiting
473 if( cur->Messages ) {
474 RELEASE( &glThreadListLock );
478 // Unset remaining timeslices (force a task switch on timer fire)
481 // Remove from active list
482 thread->Next = cur->Next;
484 // Add to Sleeping List (at the top)
485 cur->Next = gSleepingThreads;
486 gSleepingThreads = cur;
488 // Reduce the active count & ticket count
489 giNumActiveThreads --;
491 // Mark thread as sleeping
492 cur->Status = THREAD_STAT_SLEEPING;
495 RELEASE( &glThreadListLock );
497 while(cur->Status != THREAD_STAT_ACTIVE) HALT();
502 * \fn int Threads_Wake( tThread *Thread )
503 * \brief Wakes a sleeping/waiting thread up
504 * \param Thread Thread to wake
505 * \return Boolean Failure (Returns ERRNO)
507 int Threads_Wake(tThread *Thread)
514 switch(Thread->Status)
516 case THREAD_STAT_ACTIVE:
517 Log("Thread_Wake: Waking awake thread (%i)", Thread->TID);
519 case THREAD_STAT_SLEEPING:
520 //Log_Log("Threads", "Waking %i (%p) from sleeping (CPU=%i)",
521 // Thread->TID, Thread, Thread->CurCPU);
522 LOCK( &glThreadListLock );
523 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
524 prev->Next = Thread->Next; // Remove from sleeping queue
525 Thread->Next = gActiveThreads; // Add to active queue
526 gActiveThreads = Thread;
527 giNumActiveThreads ++;
528 // Thread can't be the current, so no need to check
530 giFreeTickets += Thread->NumTickets;
531 #if DEBUG_TRACE_TICKETS
532 Log("Threads_Wake: giFreeTickets = %i", giFreeTickets);
534 Thread->Status = THREAD_STAT_ACTIVE;
535 RELEASE( &glThreadListLock );
537 case THREAD_STAT_WAITING:
538 Warning("Thread_Wake - Waiting threads are not currently supported");
540 case THREAD_STAT_DEAD:
541 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
544 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
550 * \brief Wake a thread given the TID
551 * \param TID Thread ID to wake
552 * \return Boolean Faulure (errno)
554 int Threads_WakeTID(tTID TID)
556 tThread *thread = Threads_GetThread(TID);
559 return Threads_Wake( thread );
563 * \fn void Threads_AddActive(tThread *Thread)
564 * \brief Adds a thread to the active queue
566 void Threads_AddActive(tThread *Thread)
568 LOCK( &glThreadListLock );
569 Thread->Next = gActiveThreads;
570 gActiveThreads = Thread;
571 giNumActiveThreads ++;
572 // Thread can't be the current, so no need to check
573 giFreeTickets += Thread->NumTickets;
574 #if DEBUG_TRACE_TICKETS
575 Log("Threads_AddActive: giFreeTickets = %i", giFreeTickets);
577 RELEASE( &glThreadListLock );
581 * \fn void Threads_SetFaultHandler(Uint Handler)
582 * \brief Sets the signal handler for a signal
584 void Threads_SetFaultHandler(Uint Handler)
586 Log_Log("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
587 Proc_GetCurThread()->FaultHandler = Handler;
591 * \fn void Threads_Fault(int Num)
592 * \brief Calls a fault handler
594 void Threads_Fault(int Num)
596 tThread *thread = Proc_GetCurThread();
598 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
602 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
604 switch(thread->FaultHandler)
607 Threads_Kill(thread, -1);
610 case 1: // Dump Core?
611 Threads_Kill(thread, -1);
616 // Double Fault? Oh, F**k
617 if(thread->CurFaultNum != 0) {
618 Threads_Kill(thread, -1); // For now, just kill
622 thread->CurFaultNum = Num;
624 Proc_CallFaultHandler(thread);
627 // --- Process Structure Access Functions ---
628 tPID Threads_GetPID(void)
630 return Proc_GetCurThread()->TGID;
632 tTID Threads_GetTID(void)
634 return Proc_GetCurThread()->TID;
636 tUID Threads_GetUID(void)
638 return Proc_GetCurThread()->UID;
640 tGID Threads_GetGID(void)
642 return Proc_GetCurThread()->GID;
645 int Threads_SetUID(Uint *Errno, tUID ID)
647 tThread *t = Proc_GetCurThread();
652 Log("Threads_SetUID - Setting User ID to %i", ID);
657 int Threads_SetGID(Uint *Errno, tGID ID)
659 tThread *t = Proc_GetCurThread();
664 Log("Threads_SetGID - Setting Group ID to %i", ID);
670 * \fn void Threads_Dump(void)
671 * \brief Dums a list of currently running threads
673 void Threads_Dump(void)
677 Log("Active Threads:");
678 for(thread=gActiveThreads;thread;thread=thread->Next)
680 Log(" %i (%i) - %s (CPU %i)",
681 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
682 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
683 Log(" KStack 0x%x", thread->KernelStack);
685 Log("Sleeping Threads:");
686 for(thread=gSleepingThreads;thread;thread=thread->Next)
689 thread->TID, thread->TGID, thread->ThreadName);
690 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
691 Log(" KStack 0x%x", thread->KernelStack);
696 * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
697 * \brief Gets the next thread to run
698 * \param CPU Current CPU
699 * \param Last The thread the CPU was running
701 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
707 // TODO: Enable the code to tell if the current CPU has the lock or
710 // Check if the thread list is locked by other code
711 // - If so, don't switch (give it a chance to complete)
712 if( IS_LOCKED(&glThreadListLock) )
715 // Clear Delete Queue
716 while(gDeleteThreads)
718 thread = gDeleteThreads->Next;
719 if(gDeleteThreads->IsLocked) { // Only free if structure is unused
720 gDeleteThreads->Status = THREAD_STAT_NULL;
721 free( gDeleteThreads );
723 gDeleteThreads = thread;
726 // No active threads, just take a nap
727 if(giNumActiveThreads == 0) {
728 #if DEBUG_TRACE_TICKETS
729 Log("No active threads");
735 // - HLT lock (Used because only another CPU can obtain the lock,
736 // but it has a potentially long lock period)
737 // - Well, this CPU can obtain the lock, but that is aliveviated by
739 TIGHTLOCK( &glThreadListLock );
741 // Special case: 1 thread
742 if(giNumActiveThreads == 1) {
743 if( gActiveThreads->CurCPU == -1 )
744 gActiveThreads->CurCPU = CPU;
745 RELEASE( &glThreadListLock );
746 if( gActiveThreads->CurCPU == CPU )
747 return gActiveThreads;
748 return NULL; // CPU has nothing to do
751 // Allow the old thread to be scheduled again
753 if( Last->Status == THREAD_STAT_ACTIVE ) {
754 giFreeTickets += Last->NumTickets;
755 #if DEBUG_TRACE_TICKETS
756 LogF(" CPU %i released %p (%s) into the pool (%i tickets in pool)\n",
757 CPU, Last, Last->ThreadName, Last->NumTickets);
760 #if DEBUG_TRACE_TICKETS
762 LogF(" %p (%s)->Status = %i\n", Last, Last->ThreadName, Last->Status);
769 for(thread=gActiveThreads;thread;thread=thread->Next) {
770 if(thread->CurCPU >= 0) continue;
771 number += thread->NumTickets;
773 if(number != giFreeTickets) {
774 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
775 giFreeTickets, number, CPU);
779 // No free tickets (all tasks delegated to cores)
780 if( giFreeTickets == 0 ) {
781 RELEASE(&glThreadListLock);
785 // Get the ticket number
786 ticket = number = rand() % giFreeTickets;
788 // Find the next thread
789 for(thread=gActiveThreads;thread;thread=thread->Next)
791 if(thread->CurCPU >= 0) continue;
792 if(thread->NumTickets > number) break;
793 number -= thread->NumTickets;
799 for(thread=gActiveThreads;thread;thread=thread->Next) {
800 if(thread->CurCPU >= 0) continue;
801 number += thread->NumTickets;
803 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
804 giFreeTickets, number);
806 #if DEBUG_TRACE_TICKETS
807 LogF(" CPU%i giFreeTickets = %i\n", CPU, giFreeTickets);
810 // Make the new thread non-schedulable
811 giFreeTickets -= thread->NumTickets;
812 thread->CurCPU = CPU;
815 #if DEBUG_TRACE_TICKETS
816 LogF(" CPU%i giFreeTickets = %i, giving %p (%s CPU=%i)\n",
817 CPU, giFreeTickets, thread, thread->ThreadName, thread->CurCPU);
820 RELEASE( &glThreadListLock );
826 * \fn void Threads_SegFault(tVAddr Addr)
827 * \brief Called when a Segment Fault occurs
829 void Threads_SegFault(tVAddr Addr)
831 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
833 //Threads_Exit( 0, -1 );
837 EXPORT(Threads_GetUID);