4 * - Common Thread Control
11 #define DEFAULT_QUANTUM 10
12 #define DEFAULT_TICKETS 5
13 #define MAX_TICKETS 10
14 const enum eConfigTypes cCONFIG_TYPES[] = {
15 CFGT_HEAPSTR, // e.g. CFG_VFS_CWD
16 CFGT_INT, // e.g. CFG_VFS_MAXFILES
21 extern void ArchThreads_Init(void);
22 extern void Proc_Start(void);
23 extern tThread *Proc_GetCurThread(void);
24 extern int Proc_Clone(Uint *Err, Uint Flags);
25 extern void Proc_CallFaultHandler(tThread *Thread);
28 void Threads_Init(void);
29 int Threads_SetName(char *NewName);
30 char *Threads_GetName(int ID);
31 void Threads_SetTickets(int Num);
32 tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
33 int Threads_WaitTID(int TID, int *status);
34 tThread *Threads_GetThread(Uint TID);
35 void Threads_AddToDelete(tThread *Thread);
36 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread);
37 void Threads_Exit(int TID, int Status);
38 void Threads_Kill(tThread *Thread, int Status);
39 void Threads_Yield(void);
40 void Threads_Sleep(void);
41 void Threads_Wake(tThread *Thread);
42 void Threads_AddActive(tThread *Thread);
43 int Threads_GetPID(void);
44 int Threads_GetTID(void);
45 tUID Threads_GetUID(void);
46 int Threads_SetUID(Uint *Errno, tUID ID);
47 tGID Threads_GetGID(void);
48 int Threads_SetGID(Uint *Errno, tUID ID);
49 void Threads_Dump(void);
53 // Only used for the core kernel
54 tThread gThreadZero = {
55 Status: THREAD_STAT_ACTIVE, // Status
56 ThreadName: "ThreadZero", // Name
57 Quantum: DEFAULT_QUANTUM, Remaining: DEFAULT_QUANTUM, // Quantum, Remaining
58 NumTickets: DEFAULT_TICKETS
62 tSpinlock glThreadListLock = 0; ///\note NEVER use a heap function while locked
63 // --- Current State ---
64 volatile int giNumActiveThreads = 0;
65 //volatile int giTotalTickets = 0;
66 volatile int giFreeTickets = 0;
67 volatile Uint giNextTID = 1;
68 // --- Thread Lists ---
69 tThread *gActiveThreads = NULL; // Currently Running Threads
70 tThread *gSleepingThreads = NULL; // Sleeping Threads
71 tThread *gDeleteThreads = NULL; // Threads to delete
76 * \fn void Threads_Init(void)
77 * \brief Initialse the thread list
79 void Threads_Init(void)
83 // Create Initial Task
84 gActiveThreads = &gThreadZero;
85 //giFreeTickets = gThreadZero.NumTickets;
86 giNumActiveThreads = 1;
92 * \fn void Threads_SetName(char *NewName)
93 * \brief Sets the current thread's name
95 int Threads_SetName(char *NewName)
97 tThread *cur = Proc_GetCurThread();
98 char *oldname = cur->ThreadName;
100 cur->ThreadName = NULL;
102 if( IsHeap(oldname) ) free( oldname );
104 cur->ThreadName = strdup(NewName);
109 * \fn char *Threads_GetName(int ID)
110 * \brief Gets a thread's name
112 char *Threads_GetName(int ID)
115 return Proc_GetCurThread()->ThreadName;
117 // TODO: Find a thread and get its name
122 * \fn void Threads_SetTickets(int Num)
123 * \brief Sets the 'priority' of a task
125 void Threads_SetTickets(int Num)
127 tThread *cur = Proc_GetCurThread();
129 if(Num > MAX_TICKETS) Num = MAX_TICKETS;
131 cur->NumTickets = Num;
135 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
137 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
141 cur = Proc_GetCurThread();
143 new = malloc(sizeof(tThread));
148 memcpy(new, cur, sizeof(tThread));
153 new->Status = THREAD_STAT_ACTIVE;
157 new->TID = giNextTID++;
158 new->PTID = cur->TID;
161 Log("Threads_CloneTCB: cur (%p) ->ThreadName = %p", cur, cur->ThreadName);
162 new->ThreadName = strdup(cur->ThreadName);
164 // Set Thread Group ID (PID)
166 new->TGID = new->TID;
168 new->TGID = cur->TGID;
170 // Messages are not inherited
171 new->Messages = NULL;
172 new->LastMessage = NULL;
175 new->Remaining = new->Quantum = cur->Quantum;
176 new->NumTickets = cur->NumTickets;
178 // Set Signal Handlers
179 new->CurFaultNum = 0;
180 new->FaultHandler = cur->FaultHandler;
182 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
184 switch(cCONFIG_TYPES[i])
187 new->Config[i] = cur->Config[i];
191 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
202 * \fn Uint *Threads_GetCfgPtr(int Id)
204 Uint *Threads_GetCfgPtr(int Id)
206 if(Id < 0 || Id >= NUM_CFG_ENTRIES) {
207 Warning("Threads_GetCfgPtr: Index %i is out of bounds", Id);
211 return &Proc_GetCurThread()->Config[Id];
215 * \fn void Threads_WaitTID(int TID, int *status)
216 * \brief Wait for a task to change state
218 int Threads_WaitTID(int TID, int *status)
226 // Any peer/child thread
239 tThread *t = Threads_GetThread(TID);
240 int initStatus = t->Status;
243 if(initStatus != THREAD_STAT_ZOMBIE) {
244 while(t->Status == initStatus) {
252 case THREAD_STAT_ZOMBIE:
253 t->Status = THREAD_STAT_DEAD;
254 if(status) *status = 0;
255 Threads_AddToDelete( t );
258 if(status) *status = -1;
268 * \fn tThread *Threads_GetThread(Uint TID)
269 * \brief Gets a thread given its TID
270 * \param TID Thread ID
272 tThread *Threads_GetThread(Uint TID)
276 // Search Active List
277 for(thread = gActiveThreads;
279 thread = thread->Next)
281 if(thread->TID == TID)
285 // Search Sleeping List
286 for(thread = gSleepingThreads;
288 thread = thread->Next)
290 if(thread->TID == TID)
298 * \fn void Threads_AddToDelete(tThread *Thread)
299 * \brief Adds a thread to the delete queue
301 void Threads_AddToDelete(tThread *Thread)
303 // Add to delete queue
305 Thread->Next = gDeleteThreads;
306 gDeleteThreads = Thread;
309 gDeleteThreads = Thread;
314 * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
315 * \brief Gets the previous entry in a thead linked list
317 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
321 if(*List == Thread) {
322 return (tThread*)List;
325 ret->Next && ret->Next != Thread;
328 // Error if the thread is not on the list
329 if(!ret->Next || ret->Next != Thread) {
337 * \fn void Threads_Exit(int TID, int Status)
338 * \brief Exit the current process
340 void Threads_Exit(int TID, int Status)
343 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
345 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
346 // Halt forever, just in case
352 * \fn void Threads_Kill(tThread *Thread, int Status)
353 * \brief Kill a thread
354 * \param Thread Thread to kill
355 * \param Status Status code to return to the parent
357 void Threads_Kill(tThread *Thread, int Status)
366 for(child = gActiveThreads;
370 if(child->PTID == Thread->TID)
371 Threads_Kill(child, -1);
376 ///\note Double lock is needed due to overlap of lock areas
378 // Lock thread (stop us recieving messages)
379 LOCK( &Thread->IsLocked );
382 LOCK( &glThreadListLock );
384 // Get previous thread on list
385 prev = Threads_int_GetPrev( &gActiveThreads, Thread );
387 Warning("Proc_Exit - Current thread is not on the active queue");
391 // Clear Message Queue
392 while( Thread->Messages )
394 msg = Thread->Messages->Next;
395 free( Thread->Messages );
396 Thread->Messages = msg;
399 Thread->Remaining = 0; // Clear Remaining Quantum
400 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
401 prev->Next = Thread->Next; // Remove from active
403 giNumActiveThreads --;
404 if( Thread != Proc_GetCurThread() )
405 giFreeTickets -= Thread->NumTickets;
407 // Mark thread as a zombie
408 Thread->RetStatus = Status;
410 // Don't Zombie if we are being killed as part of a tree
413 Thread->Status = THREAD_STAT_DEAD;
414 Threads_AddToDelete( Thread );
416 Thread->Status = THREAD_STAT_ZOMBIE;
420 RELEASE( &Thread->IsLocked ); // Released first so that it IS released
421 RELEASE( &glThreadListLock );
423 //Log("Thread %i went *hurk*", Thread->TID);
425 if(Status != -1) HALT();
429 * \fn void Threads_Yield(void)
430 * \brief Yield remainder of timeslice
432 void Threads_Yield(void)
434 Proc_GetCurThread()->Remaining = 0;
439 * \fn void Threads_Sleep(void)
440 * \brief Take the current process off the run queue
442 void Threads_Sleep(void)
444 tThread *cur = Proc_GetCurThread();
447 //Log_Log("Threads", "%i going to sleep", cur->TID);
450 LOCK( &glThreadListLock );
452 // Get thread before current thread
453 thread = Threads_int_GetPrev( &gActiveThreads, cur );
455 Warning("Threads_Sleep - Current thread is not on the active queue");
457 RELEASE( &glThreadListLock );
461 // Don't sleep if there is a message waiting
462 if( cur->Messages ) {
463 RELEASE( &glThreadListLock );
467 // Unset remaining timeslices (force a task switch on timer fire)
470 // Remove from active list
471 thread->Next = cur->Next;
473 // Add to Sleeping List (at the top)
474 cur->Next = gSleepingThreads;
475 gSleepingThreads = cur;
477 // Reduce the active count & ticket count
478 giNumActiveThreads --;
479 //giTotalTickets -= cur->NumTickets;
481 // Mark thread as sleeping
482 cur->Status = THREAD_STAT_SLEEPING;
485 RELEASE( &glThreadListLock );
487 while(cur->Status != THREAD_STAT_ACTIVE) HALT();
492 * \fn void Threads_Wake( tThread *Thread )
493 * \brief Wakes a sleeping/waiting thread up
495 void Threads_Wake(tThread *Thread)
498 switch(Thread->Status)
500 case THREAD_STAT_ACTIVE: break;
501 case THREAD_STAT_SLEEPING:
502 //Log_Log("Threads", "Waking %i (%p) from sleeping", Thread->TID, Thread);
503 LOCK( &glThreadListLock );
504 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
505 prev->Next = Thread->Next; // Remove from sleeping queue
506 Thread->Next = gActiveThreads; // Add to active queue
507 gActiveThreads = Thread;
508 giNumActiveThreads ++;
509 // Thread can't be the current, so no need to check
510 giFreeTickets += Thread->NumTickets;
511 Thread->Status = THREAD_STAT_ACTIVE;
512 RELEASE( &glThreadListLock );
514 case THREAD_STAT_WAITING:
515 Warning("Thread_Wake - Waiting threads are not currently supported");
517 case THREAD_STAT_DEAD:
518 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
521 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
527 * \brief Wake a thread given the TID
529 void Threads_WakeTID(tTID Thread)
531 Threads_Wake( Threads_GetThread(Thread) );
535 * \fn void Threads_AddActive(tThread *Thread)
536 * \brief Adds a thread to the active queue
538 void Threads_AddActive(tThread *Thread)
540 LOCK( &glThreadListLock );
541 Thread->Next = gActiveThreads;
542 gActiveThreads = Thread;
543 giNumActiveThreads ++;
544 // Thread can't be the current, so no need to check
545 giFreeTickets += Thread->NumTickets;
546 //Log("Threads_AddActive: giNumActiveThreads = %i, giTotalTickets = %i",
547 // giNumActiveThreads, giTotalTickets);
548 RELEASE( &glThreadListLock );
552 * \fn void Threads_SetFaultHandler(Uint Handler)
553 * \brief Sets the signal handler for a signal
555 void Threads_SetFaultHandler(Uint Handler)
557 Log_Log("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
558 Proc_GetCurThread()->FaultHandler = Handler;
562 * \fn void Threads_Fault(int Num)
563 * \brief Calls a fault handler
565 void Threads_Fault(int Num)
567 tThread *thread = Proc_GetCurThread();
569 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
573 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
575 switch(thread->FaultHandler)
578 Threads_Kill(thread, -1);
581 case 1: // Dump Core?
582 Threads_Kill(thread, -1);
587 // Double Fault? Oh, F**k
588 if(thread->CurFaultNum != 0) {
589 Threads_Kill(thread, -1); // For now, just kill
593 thread->CurFaultNum = Num;
595 Proc_CallFaultHandler(thread);
598 // --- Process Structure Access Functions ---
599 tPID Threads_GetPID(void)
601 return Proc_GetCurThread()->TGID;
603 tTID Threads_GetTID(void)
605 return Proc_GetCurThread()->TID;
607 tUID Threads_GetUID(void)
609 return Proc_GetCurThread()->UID;
611 tGID Threads_GetGID(void)
613 return Proc_GetCurThread()->GID;
616 int Threads_SetUID(Uint *Errno, tUID ID)
618 tThread *t = Proc_GetCurThread();
623 Log("Threads_SetUID - Setting User ID to %i", ID);
628 int Threads_SetGID(Uint *Errno, tGID ID)
630 tThread *t = Proc_GetCurThread();
635 Log("Threads_SetGID - Setting Group ID to %i", ID);
641 * \fn void Threads_Dump(void)
642 * \brief Dums a list of currently running threads
644 void Threads_Dump(void)
647 tThread *cur = Proc_GetCurThread();
649 Log("Active Threads:");
650 for(thread=gActiveThreads;thread;thread=thread->Next)
652 Log(" %i (%i) - %s (CPU %i)",
653 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
654 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
655 Log(" KStack 0x%x", thread->KernelStack);
657 Log("Sleeping Threads:");
658 for(thread=gSleepingThreads;thread;thread=thread->Next)
660 Log("%c%i (%i) - %s",
661 (thread==cur?'*':' '),
662 thread->TID, thread->TGID, thread->ThreadName);
663 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
664 Log(" KStack 0x%x", thread->KernelStack);
669 * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
670 * \brief Gets the next thread to run
671 * \param CPU Current CPU
672 * \param Last The thread the CPU was running
674 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
680 // Note: Enable the code to tell if the switch code has the lock, or
681 // if it's the other code.
683 // Check if the thread list is locked by other code
684 // - If so, don't switch (give it a chance to complete)
685 if( IS_LOCKED(&glThreadListLock) )
688 // No active threads, just take a nap
689 if(giNumActiveThreads == 0) {
694 // - HLT lock (Used because only another CPU can obtain the lock,
695 // but it has a potentially long lock period)
696 // - Well, this CPU can obtain the lock, but that is aliveviated by
698 TIGHTLOCK( &glThreadListLock );
700 // Special case: 1 thread
701 if(giNumActiveThreads == 1) {
702 if( gActiveThreads->CurCPU == -1 )
703 gActiveThreads->CurCPU = CPU;
704 RELEASE( &glThreadListLock );
705 if( gActiveThreads->CurCPU == CPU )
706 return gActiveThreads;
707 return NULL; // CPU has nothing to do
710 // Allow the old thread to be scheduled again
712 if( Last->Status == THREAD_STAT_ACTIVE )
713 giFreeTickets += Last->NumTickets;
717 // No free tickets (all tasks delegated to cores)
718 if( giFreeTickets == 0 ) {
719 RELEASE(&glThreadListLock);
723 // Get the ticket number
724 ticket = number = rand() % giFreeTickets;
726 // Find the next thread
727 for(thread=gActiveThreads;thread;thread=thread->Next)
729 if(thread->CurCPU >= 0) continue;
730 if(thread->NumTickets > number) break;
731 number -= thread->NumTickets;
737 for(thread=gActiveThreads;thread;thread=thread->Next) {
738 if(thread->CurCPU >= 0) continue;
739 number += thread->NumTickets;
741 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
742 giFreeTickets, number);
745 // Make the new thread non-schedulable
746 giFreeTickets -= thread->NumTickets;
747 thread->CurCPU = CPU;
749 RELEASE( &glThreadListLock );
755 * \fn void Threads_SegFault(tVAddr Addr)
756 * \brief Called when a Segment Fault occurs
758 void Threads_SegFault(tVAddr Addr)
760 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
762 //Threads_Exit( 0, -1 );
766 EXPORT(Threads_GetUID);