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 NULL, 0, // Next, Lock
56 THREAD_STAT_ACTIVE, // Status
60 0, // Parent Thread ID
67 0, 0, // Current Fault, Fault Handler
69 NULL, NULL, // Messages, Last Message
70 DEFAULT_QUANTUM, DEFAULT_QUANTUM, // Quantum, Remaining
72 {0} // Default config to zero
76 tSpinlock glThreadListLock = 0; ///\note NEVER use a heap function while locked
77 // --- Current State ---
78 volatile int giNumActiveThreads = 0;
79 //volatile int giTotalTickets = 0;
80 volatile int giFreeTickets = 0;
81 volatile Uint giNextTID = 1;
82 // --- Thread Lists ---
83 tThread *gActiveThreads = NULL; // Currently Running Threads
84 tThread *gSleepingThreads = NULL; // Sleeping Threads
85 tThread *gDeleteThreads = NULL; // Threads to delete
90 * \fn void Threads_Init(void)
91 * \brief Initialse the thread list
93 void Threads_Init(void)
97 // Create Initial Task
98 gActiveThreads = &gThreadZero;
99 //giFreeTickets = gThreadZero.NumTickets;
100 giNumActiveThreads = 1;
104 if(Proc_Clone(0, 0) == 0)
106 tThread *cur = Proc_GetCurThread();
107 cur->ThreadName = "Idle Thread";
108 Threads_SetTickets(0); // Never called randomly
109 cur->Quantum = 1; // 1 slice quantum
110 for(;;) HALT(); // Just yeilds
118 * \fn void Threads_SetName(char *NewName)
119 * \brief Sets the current thread's name
121 int Threads_SetName(char *NewName)
123 tThread *cur = Proc_GetCurThread();
124 if( IsHeap(cur->ThreadName) )
125 free( cur->ThreadName );
126 // TODO: Possible concurrency issue
127 cur->ThreadName = malloc(strlen(NewName)+1);
128 strcpy(cur->ThreadName, NewName);
133 * \fn char *Threads_GetName(int ID)
134 * \brief Gets a thread's name
136 char *Threads_GetName(int ID)
139 return Proc_GetCurThread()->ThreadName;
141 // TODO: Find a thread and get its name
146 * \fn void Threads_SetTickets(int Num)
147 * \brief Sets the 'priority' of a task
149 void Threads_SetTickets(int Num)
151 tThread *cur = Proc_GetCurThread();
153 if(Num > MAX_TICKETS) Num = MAX_TICKETS;
155 cur->NumTickets = Num;
159 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
161 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
165 cur = Proc_GetCurThread();
167 new = malloc(sizeof(tThread));
172 memcpy(new, cur, sizeof(tThread));
177 new->Status = THREAD_STAT_ACTIVE;
181 new->TID = giNextTID++;
182 new->PTID = cur->TID;
185 new->ThreadName = strdup(cur->ThreadName);
187 // Set Thread Group ID (PID)
189 new->TGID = new->TID;
191 new->TGID = cur->TGID;
193 // Messages are not inherited
194 new->Messages = NULL;
195 new->LastMessage = NULL;
198 new->Remaining = new->Quantum = cur->Quantum;
199 new->NumTickets = cur->NumTickets;
201 // Set Signal Handlers
202 new->CurFaultNum = 0;
203 new->FaultHandler = cur->FaultHandler;
205 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
207 switch(cCONFIG_TYPES[i])
210 new->Config[i] = cur->Config[i];
214 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
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 void Threads_WaitTID(int TID, int *status)
239 * \brief Wait for a task to change state
241 int Threads_WaitTID(int TID, int *status)
249 // Any peer/child thread
262 tThread *t = Threads_GetThread(TID);
263 int initStatus = t->Status;
266 if(initStatus != THREAD_STAT_ZOMBIE) {
267 while(t->Status == initStatus) {
275 case THREAD_STAT_ZOMBIE:
276 t->Status = THREAD_STAT_DEAD;
277 if(status) *status = 0;
278 Threads_AddToDelete( t );
281 if(status) *status = -1;
291 * \fn tThread *Threads_GetThread(Uint TID)
292 * \brief Gets a thread given its TID
293 * \param TID Thread ID
295 tThread *Threads_GetThread(Uint TID)
299 // Search Active List
300 for(thread = gActiveThreads;
302 thread = thread->Next)
304 if(thread->TID == TID)
308 // Search Sleeping List
309 for(thread = gSleepingThreads;
311 thread = thread->Next)
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 LOCK( &Thread->IsLocked );
405 LOCK( &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");
414 // Clear Message Queue
415 while( Thread->Messages )
417 msg = Thread->Messages->Next;
418 free( Thread->Messages );
419 Thread->Messages = msg;
422 Thread->Remaining = 0; // Clear Remaining Quantum
423 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
424 prev->Next = Thread->Next; // Remove from active
426 giNumActiveThreads --;
427 if( Thread != Proc_GetCurThread() )
428 giFreeTickets -= Thread->NumTickets;
430 // Mark thread as a zombie
431 Thread->RetStatus = Status;
433 // Don't Zombie if we are being killed as part of a tree
436 Thread->Status = THREAD_STAT_DEAD;
437 Threads_AddToDelete( Thread );
439 Thread->Status = THREAD_STAT_ZOMBIE;
443 RELEASE( &Thread->IsLocked ); // Released first so that it IS released
444 RELEASE( &glThreadListLock );
446 //Log("Thread %i went *hurk*", Thread->TID);
448 if(Status != -1) HALT();
452 * \fn void Threads_Yield(void)
453 * \brief Yield remainder of timeslice
455 void Threads_Yield(void)
457 Proc_GetCurThread()->Remaining = 0;
462 * \fn void Threads_Sleep(void)
463 * \brief Take the current process off the run queue
465 void Threads_Sleep(void)
467 tThread *cur = Proc_GetCurThread();
470 //Log_Log("Threads", "%i going to sleep", cur->TID);
473 LOCK( &glThreadListLock );
475 // Get thread before current thread
476 thread = Threads_int_GetPrev( &gActiveThreads, cur );
478 Warning("Threads_Sleep - Current thread is not on the active queue");
480 RELEASE( &glThreadListLock );
484 // Don't sleep if there is a message waiting
485 if( cur->Messages ) {
486 RELEASE( &glThreadListLock );
490 // Unset remaining timeslices (force a task switch on timer fire)
493 // Remove from active list
494 thread->Next = cur->Next;
496 // Add to Sleeping List (at the top)
497 cur->Next = gSleepingThreads;
498 gSleepingThreads = cur;
500 // Reduce the active count & ticket count
501 giNumActiveThreads --;
502 //giTotalTickets -= cur->NumTickets;
504 // Mark thread as sleeping
505 cur->Status = THREAD_STAT_SLEEPING;
508 RELEASE( &glThreadListLock );
510 while(cur->Status != THREAD_STAT_ACTIVE) HALT();
515 * \fn void Threads_Wake( tThread *Thread )
516 * \brief Wakes a sleeping/waiting thread up
518 void Threads_Wake(tThread *Thread)
521 switch(Thread->Status)
523 case THREAD_STAT_ACTIVE: break;
524 case THREAD_STAT_SLEEPING:
525 //Log_Log("Threads", "Waking %i (%p) from sleeping", Thread->TID, Thread);
526 LOCK( &glThreadListLock );
527 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
528 prev->Next = Thread->Next; // Remove from sleeping queue
529 Thread->Next = gActiveThreads; // Add to active queue
530 gActiveThreads = Thread;
531 giNumActiveThreads ++;
532 // Thread can't be the current, so no need to check
533 giFreeTickets += Thread->NumTickets;
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
552 void Threads_WakeTID(tTID Thread)
554 Threads_Wake( Threads_GetThread(Thread) );
558 * \fn void Threads_AddActive(tThread *Thread)
559 * \brief Adds a thread to the active queue
561 void Threads_AddActive(tThread *Thread)
563 LOCK( &glThreadListLock );
564 Thread->Next = gActiveThreads;
565 gActiveThreads = Thread;
566 giNumActiveThreads ++;
567 // Thread can't be the current, so no need to check
568 giFreeTickets += Thread->NumTickets;
569 //Log("Threads_AddActive: giNumActiveThreads = %i, giTotalTickets = %i",
570 // giNumActiveThreads, giTotalTickets);
571 RELEASE( &glThreadListLock );
575 * \fn void Threads_SetFaultHandler(Uint Handler)
576 * \brief Sets the signal handler for a signal
578 void Threads_SetFaultHandler(Uint Handler)
580 Log_Log("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
581 Proc_GetCurThread()->FaultHandler = Handler;
585 * \fn void Threads_Fault(int Num)
586 * \brief Calls a fault handler
588 void Threads_Fault(int Num)
590 tThread *thread = Proc_GetCurThread();
592 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
596 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
598 switch(thread->FaultHandler)
601 Threads_Kill(thread, -1);
604 case 1: // Dump Core?
605 Threads_Kill(thread, -1);
610 // Double Fault? Oh, F**k
611 if(thread->CurFaultNum != 0) {
612 Threads_Kill(thread, -1); // For now, just kill
616 thread->CurFaultNum = Num;
618 Proc_CallFaultHandler(thread);
621 // --- Process Structure Access Functions ---
622 tPID Threads_GetPID(void)
624 return Proc_GetCurThread()->TGID;
626 tTID Threads_GetTID(void)
628 return Proc_GetCurThread()->TID;
630 tUID Threads_GetUID(void)
632 return Proc_GetCurThread()->UID;
634 tGID Threads_GetGID(void)
636 return Proc_GetCurThread()->GID;
639 int Threads_SetUID(Uint *Errno, tUID ID)
641 tThread *t = Proc_GetCurThread();
646 Log("Threads_SetUID - Setting User ID to %i", ID);
651 int Threads_SetGID(Uint *Errno, tGID ID)
653 tThread *t = Proc_GetCurThread();
658 Log("Threads_SetGID - Setting Group ID to %i", ID);
664 * \fn void Threads_Dump(void)
665 * \brief Dums a list of currently running threads
667 void Threads_Dump(void)
670 tThread *cur = Proc_GetCurThread();
672 Log("Active Threads:");
673 for(thread=gActiveThreads;thread;thread=thread->Next)
675 Log(" %i (%i) - %s (CPU %i)",
676 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
677 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
678 Log(" KStack 0x%x", thread->KernelStack);
680 Log("Sleeping Threads:");
681 for(thread=gSleepingThreads;thread;thread=thread->Next)
683 Log("%c%i (%i) - %s",
684 (thread==cur?'*':' '),
685 thread->TID, thread->TGID, thread->ThreadName);
686 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
687 Log(" KStack 0x%x", thread->KernelStack);
692 * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
693 * \brief Gets the next thread to run
694 * \param CPU Current CPU
695 * \param Last The thread the CPU was running
697 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
703 // Note: Enable the code to tell if the switch code has the lock, or
704 // if it's the other code.
706 // Check if the thread list is locked by other code
707 // - If so, don't switch (give it a chance to complete)
708 if( IS_LOCKED(&glThreadListLock) )
711 // No active threads, just take a nap
712 if(giNumActiveThreads == 0) {
717 // - HLT lock (Used because only another CPU can obtain the lock,
718 // but it has a potentially long lock period)
719 // - Well, this CPU can obtain the lock, but that is aliveviated by
721 TIGHTLOCK( &glThreadListLock );
723 // Special case: 1 thread
724 if(giNumActiveThreads == 1) {
725 if( gActiveThreads->CurCPU == -1 )
726 gActiveThreads->CurCPU = CPU;
727 RELEASE( &glThreadListLock );
728 if( gActiveThreads->CurCPU == CPU )
729 return gActiveThreads;
730 return NULL; // CPU has nothing to do
733 // Allow the old thread to be scheduled again
735 if( Last->Status == THREAD_STAT_ACTIVE )
736 giFreeTickets += Last->NumTickets;
740 // Get the ticket number
741 ticket = number = rand() % giFreeTickets;
743 // Find the next thread
744 for(thread=gActiveThreads;thread;thread=thread->Next)
746 if(thread->CurCPU >= 0) continue;
747 if(thread->NumTickets > number) break;
748 number -= thread->NumTickets;
754 for(thread=gActiveThreads;thread;thread=thread->Next) {
755 if(thread->CurCPU >= 0) continue;
756 number += thread->NumTickets;
758 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
759 giFreeTickets, number);
762 // Make the new thread non-schedulable
763 giFreeTickets -= thread->NumTickets;
764 thread->CurCPU = CPU;
766 RELEASE( &glThreadListLock );
772 * \fn void Threads_SegFault(tVAddr Addr)
773 * \brief Called when a Segment Fault occurs
775 void Threads_SegFault(tVAddr Addr)
777 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
779 //Threads_Exit( 0, -1 );
783 EXPORT(Threads_GetUID);