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, // CFG_VFS_CWD
16 CFGT_INT, // 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 tThread gThreadZero = {
54 NULL, 0, // Next, Lock
55 THREAD_STAT_ACTIVE, // Status
59 0, // Parent Thread ID
66 0, 0, // Current Fault, Fault Handler
68 NULL, NULL, // Messages, Last Message
69 DEFAULT_QUANTUM, DEFAULT_QUANTUM, // Quantum, Remaining
71 {0} // Default config to zero
75 tSpinlock glThreadListLock = 0; ///\note NEVER use a heap function while locked
76 // --- Current State ---
77 volatile int giNumActiveThreads = 0;
78 volatile int giTotalTickets = 0;
79 volatile Uint giNextTID = 1;
80 // --- Thread Lists ---
81 tThread *gActiveThreads = NULL; // Currently Running Threads
82 tThread *gSleepingThreads = NULL; // Sleeping Threads
83 tThread *gDeleteThreads = NULL; // Threads to delete
88 * \fn void Threads_Init(void)
89 * \brief Initialse the thread list
91 void Threads_Init(void)
95 // Create Initial Task
96 gActiveThreads = &gThreadZero;
97 giTotalTickets = gThreadZero.NumTickets;
98 giNumActiveThreads = 1;
102 if(Proc_Clone(0, 0) == 0)
104 tThread *cur = Proc_GetCurThread();
105 cur->ThreadName = "Idle Thread";
106 Threads_SetTickets(0); // Never called randomly
107 cur->Quantum = 1; // 1 slice quantum
110 HALT(); // Just yeilds
119 * \fn void Threads_SetName(char *NewName)
120 * \brief Sets the current thread's name
122 int Threads_SetName(char *NewName)
124 tThread *cur = Proc_GetCurThread();
125 if( IsHeap(cur->ThreadName) )
126 free( cur->ThreadName );
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;
145 * \fn void Threads_SetTickets(int Num)
146 * \brief Sets the 'priority' of a task
148 void Threads_SetTickets(int Num)
150 tThread *cur = Proc_GetCurThread();
152 if(Num > MAX_TICKETS) Num = MAX_TICKETS;
154 LOCK( &glThreadListLock );
155 giTotalTickets -= cur->NumTickets;
156 cur->NumTickets = Num;
157 giTotalTickets += Num;
158 //LOG("giTotalTickets = %i", giTotalTickets);
159 RELEASE( &glThreadListLock );
163 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
165 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
169 cur = Proc_GetCurThread();
171 new = malloc(sizeof(tThread));
179 new->Status = THREAD_STAT_ACTIVE;
183 new->TID = giNextTID++;
184 new->PTID = cur->TID;
187 new->ThreadName = malloc(strlen(cur->ThreadName)+1);
188 strcpy(new->ThreadName, cur->ThreadName);
190 // Set Thread Group ID (PID)
192 new->TGID = new->TID;
194 new->TGID = cur->TGID;
196 // Messages are not inherited
197 new->Messages = NULL;
198 new->LastMessage = NULL;
201 new->Remaining = new->Quantum = cur->Quantum;
202 new->NumTickets = cur->NumTickets;
204 // Set Signal Handlers
205 new->CurFaultNum = 0;
206 new->FaultHandler = cur->FaultHandler;
208 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
210 switch(cCONFIG_TYPES[i])
213 new->Config[i] = cur->Config[i];
217 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
228 * \fn Uint *Threads_GetCfgPtr(int Id)
230 Uint *Threads_GetCfgPtr(int Id)
232 if(Id < 0 || Id >= NUM_CFG_ENTRIES) {
233 Warning("Threads_GetCfgPtr: Index %i is out of bounds", Id);
237 return &Proc_GetCurThread()->Config[Id];
241 * \fn void Threads_WaitTID(int TID, int *status)
242 * \brief Wait for a task to change state
244 int Threads_WaitTID(int TID, int *status)
252 // Any peer/child thread
265 tThread *t = Threads_GetThread(TID);
266 int initStatus = t->Status;
269 if(initStatus != THREAD_STAT_ZOMBIE) {
270 while(t->Status == initStatus) {
278 case THREAD_STAT_ZOMBIE:
279 t->Status = THREAD_STAT_DEAD;
280 if(status) *status = 0;
281 Threads_AddToDelete( t );
284 if(status) *status = -1;
294 * \fn tThread *Threads_GetThread(Uint TID)
295 * \brief Gets a thread given its TID
297 tThread *Threads_GetThread(Uint TID)
301 // Search Active List
302 for(thread = gActiveThreads;
304 thread = thread->Next)
306 if(thread->TID == TID)
310 // Search Sleeping List
311 for(thread = gSleepingThreads;
313 thread = thread->Next)
315 if(thread->TID == TID)
323 * \fn void Threads_AddToDelete(tThread *Thread)
324 * \brief Adds a thread to the delete queue
326 void Threads_AddToDelete(tThread *Thread)
328 // Add to delete queue
330 Thread->Next = gDeleteThreads;
331 gDeleteThreads = Thread;
334 gDeleteThreads = Thread;
339 * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
340 * \brief Gets the previous entry in a thead linked list
342 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
346 if(*List == Thread) {
347 return (tThread*)List;
350 ret->Next && ret->Next != Thread;
353 // Error if the thread is not on the list
354 if(!ret->Next || ret->Next != Thread) {
362 * \fn void Threads_Exit(int TID, int Status)
363 * \brief Exit the current process
365 void Threads_Exit(int TID, int Status)
368 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
370 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
371 for(;;) HALT(); // 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 locks
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 giTotalTickets -= Thread->NumTickets;
429 // Mark thread as a zombie
430 Thread->RetStatus = Status;
432 // Don't Zombie if we are being killed as part of a tree
435 Thread->Status = THREAD_STAT_DEAD;
436 Threads_AddToDelete( Thread );
438 Thread->Status = THREAD_STAT_ZOMBIE;
442 RELEASE( &Thread->IsLocked ); // Released first so that it IS released
443 RELEASE( &glThreadListLock );
445 //Log("Thread %i went *hurk*", Thread->TID);
447 if(Status != -1) HALT();
451 * \fn void Threads_Yield(void)
452 * \brief Yield remainder of timeslice
454 void Threads_Yield(void)
456 Proc_GetCurThread()->Remaining = 0;
461 * \fn void Threads_Sleep(void)
462 * \brief Take the current process off the run queue
464 void Threads_Sleep(void)
466 tThread *cur = Proc_GetCurThread();
469 //Log_Log("Threads", "%i going to sleep", cur->TID);
472 LOCK( &glThreadListLock );
474 // Get thread before current thread
475 thread = Threads_int_GetPrev( &gActiveThreads, cur );
477 Warning("Threads_Sleep - Current thread is not on the active queue");
482 // Don't sleep if there is a message waiting
483 if( cur->Messages ) {
484 RELEASE( &glThreadListLock );
488 // Unset remaining timeslices (force a task switch on timer fire)
491 // Remove from active list
492 thread->Next = cur->Next;
494 // Add to Sleeping List (at the top)
495 cur->Next = gSleepingThreads;
496 gSleepingThreads = cur;
498 // Reduce the active count & ticket count
499 giNumActiveThreads --;
500 giTotalTickets -= cur->NumTickets;
502 // Mark thread as sleeping
503 cur->Status = THREAD_STAT_SLEEPING;
506 RELEASE( &glThreadListLock );
508 while(cur->Status != THREAD_STAT_ACTIVE) HALT();
513 * \fn void Threads_Wake( tThread *Thread )
514 * \brief Wakes a sleeping/waiting thread up
516 void Threads_Wake(tThread *Thread)
519 switch(Thread->Status)
521 case THREAD_STAT_ACTIVE: break;
522 case THREAD_STAT_SLEEPING:
523 //Log_Log("Threads", "Waking %i (%p) from sleeping", Thread->TID, Thread);
524 LOCK( &glThreadListLock );
525 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
526 prev->Next = Thread->Next; // Remove from sleeping queue
527 Thread->Next = gActiveThreads; // Add to active queue
528 gActiveThreads = Thread;
529 giNumActiveThreads ++;
530 giTotalTickets += Thread->NumTickets;
531 Thread->Status = THREAD_STAT_ACTIVE;
532 RELEASE( &glThreadListLock );
534 case THREAD_STAT_WAITING:
535 Warning("Thread_Wake - Waiting threads are not currently supported");
537 case THREAD_STAT_DEAD:
538 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
541 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
546 void Threads_WakeTID(tTID Thread)
548 Threads_Wake( Threads_GetThread(Thread) );
552 * \fn void Threads_AddActive(tThread *Thread)
553 * \brief Adds a thread to the active queue
555 void Threads_AddActive(tThread *Thread)
557 LOCK( &glThreadListLock );
558 Thread->Next = gActiveThreads;
559 gActiveThreads = Thread;
560 giNumActiveThreads ++;
561 giTotalTickets += Thread->NumTickets;
562 //Log("Threads_AddActive: giNumActiveThreads = %i, giTotalTickets = %i",
563 // giNumActiveThreads, giTotalTickets);
564 RELEASE( &glThreadListLock );
568 * \fn void Threads_SetFaultHandler(Uint Handler)
569 * \brief Sets the signal handler for a signal
571 void Threads_SetFaultHandler(Uint Handler)
573 Log_Log("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
574 Proc_GetCurThread()->FaultHandler = Handler;
578 * \fn void Threads_Fault(int Num)
579 * \brief Calls a fault handler
581 void Threads_Fault(int Num)
583 tThread *thread = Proc_GetCurThread();
585 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
589 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
591 switch(thread->FaultHandler)
594 Threads_Kill(thread, -1);
597 case 1: // Dump Core?
598 Threads_Kill(thread, -1);
603 // Double Fault? Oh, F**k
604 if(thread->CurFaultNum != 0) {
605 Threads_Kill(thread, -1); // For now, just kill
609 thread->CurFaultNum = Num;
611 Proc_CallFaultHandler(thread);
614 // --- Process Structure Access Functions ---
615 tPID Threads_GetPID(void)
617 return Proc_GetCurThread()->TGID;
619 tTID Threads_GetTID(void)
621 return Proc_GetCurThread()->TID;
623 tUID Threads_GetUID(void)
625 return Proc_GetCurThread()->UID;
627 tGID Threads_GetGID(void)
629 return Proc_GetCurThread()->GID;
632 int Threads_SetUID(Uint *Errno, tUID ID)
634 tThread *t = Proc_GetCurThread();
639 Log("Threads_SetUID - Setting User ID to %i", ID);
644 int Threads_SetGID(Uint *Errno, tGID ID)
646 tThread *t = Proc_GetCurThread();
651 Log("Threads_SetGID - Setting Group ID to %i", ID);
657 * \fn void Threads_Dump(void)
658 * \brief Dums a list of currently running threads
660 void Threads_Dump(void)
663 tThread *cur = Proc_GetCurThread();
665 Log("Active Threads:");
666 for(thread=gActiveThreads;thread;thread=thread->Next)
668 Log("%c%i (%i) - %s",
669 (thread==cur?'*':' '),
670 thread->TID, thread->TGID, thread->ThreadName);
671 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
672 Log(" KStack 0x%x", thread->KernelStack);
674 Log("Sleeping Threads:");
675 for(thread=gSleepingThreads;thread;thread=thread->Next)
677 Log("%c%i (%i) - %s",
678 (thread==cur?'*':' '),
679 thread->TID, thread->TGID, thread->ThreadName);
680 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
681 Log(" KStack 0x%x", thread->KernelStack);
686 * \fn tThread *Threads_GetNextToRun(int CPU)
687 * \brief Gets the next thread to run
689 tThread *Threads_GetNextToRun(int CPU)
695 if(giNumActiveThreads == 0) {
699 // Special case: 1 thread
700 if(giNumActiveThreads == 1) {
701 return gActiveThreads;
704 // Get the ticket number
705 ticket = number = rand() % giTotalTickets;
707 // Find the next thread
708 for(thread=gActiveThreads;thread;thread=thread->Next)
710 if(thread->NumTickets > number) break;
711 number -= thread->NumTickets;
718 for(thread=gActiveThreads;thread;thread=thread->Next)
719 number += thread->NumTickets;
720 Panic("Bookeeping Failed - giTotalTicketCount (%i) != true count (%i)",
721 giTotalTickets, number);
728 * \fn void Threads_SegFault(tVAddr Addr)
729 * \brief Called when a Segment Fault occurs
731 void Threads_SegFault(tVAddr Addr)
733 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
735 //Threads_Exit( 0, -1 );
739 EXPORT(Threads_GetUID);