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();
22 extern void Proc_Start();
23 extern tThread *Proc_GetCurThread();
24 extern int Proc_Clone(Uint *Err, Uint Flags);
28 int Threads_SetName(char *NewName);
29 char *Threads_GetName(int ID);
30 void Threads_SetTickets(int Num);
31 tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
32 int Threads_WaitTID(int TID, int *status);
33 tThread *Threads_GetThread(Uint TID);
34 void Threads_AddToDelete(tThread *Thread);
35 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread);
36 void Threads_Exit(int TID, int Status);
37 void Threads_Kill(tThread *Thread, int Status);
40 void Threads_Wake(tThread *Thread);
41 void Threads_AddActive(tThread *Thread);
44 tUID Threads_GetUID();
45 int Threads_SetUID(Uint *Errno, tUID ID);
46 tGID Threads_GetGID();
47 int Threads_SetGID(Uint *Errno, tUID ID);
52 tThread gThreadZero = {
53 NULL, 0, // Next, Lock
54 THREAD_STAT_ACTIVE, // Status
58 0, // Parent Thread ID
65 0, {0}, {0}, // Signal State
67 NULL, NULL, // Messages, Last Message
68 DEFAULT_QUANTUM, DEFAULT_QUANTUM, // Quantum, Remaining
70 {0} // Default config to zero
74 volatile int giThreadListLock = 0; ///\note NEVER use a heap function while locked
75 // --- Current State ---
76 volatile int giNumActiveThreads = 0;
77 volatile int giTotalTickets = 0;
78 volatile Uint giNextTID = 1;
79 // --- Thread Lists ---
80 tThread *gActiveThreads = NULL; // Currently Running Threads
81 tThread *gSleepingThreads = NULL; // Sleeping Threads
82 tThread *gDeleteThreads = NULL; // Threads to delete
87 * \fn void Threads_Init()
88 * \brief Initialse the thread list
94 // Create Initial Task
95 gActiveThreads = &gThreadZero;
96 giTotalTickets = gThreadZero.NumTickets;
97 giNumActiveThreads = 1;
101 if(Proc_Clone(0, 0) == 0)
103 tThread *cur = Proc_GetCurThread();
104 cur->ThreadName = "Idle Thread";
105 Threads_SetTickets(0); // Never called randomly
106 cur->Quantum = 1; // 1 slice quantum
109 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 cur->ThreadName = malloc(strlen(NewName)+1);
127 strcpy(cur->ThreadName, NewName);
132 * \fn char *Threads_GetName(int ID)
133 * \brief Gets a thread's name
135 char *Threads_GetName(int ID)
138 return Proc_GetCurThread()->ThreadName;
144 * \fn void Threads_SetTickets(int Num)
145 * \brief Sets the 'priority' of a task
147 void Threads_SetTickets(int Num)
149 tThread *cur = Proc_GetCurThread();
151 if(Num > MAX_TICKETS) Num = MAX_TICKETS;
153 LOCK( &giThreadListLock );
154 giTotalTickets -= cur->NumTickets;
155 cur->NumTickets = Num;
156 giTotalTickets += Num;
157 //LOG("giTotalTickets = %i", giTotalTickets);
158 RELEASE( &giThreadListLock );
162 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
164 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
168 cur = Proc_GetCurThread();
170 new = malloc(sizeof(tThread));
178 new->Status = THREAD_STAT_ACTIVE;
182 new->TID = giNextTID++;
183 new->PTID = cur->TID;
186 new->ThreadName = malloc(strlen(cur->ThreadName)+1);
187 strcpy(new->ThreadName, cur->ThreadName);
189 // Set Thread Group ID (PID)
191 new->TGID = new->TID;
193 new->TGID = cur->TGID;
195 // Messages are not inherited
196 new->Messages = NULL;
197 new->LastMessage = NULL;
200 new->Remaining = new->Quantum = cur->Quantum;
201 new->NumTickets = cur->NumTickets;
203 // Set Signal Handlers
206 memset(new->SignalHandlers, 0, sizeof(new->SignalHandlers));
208 memcpy(new->SignalHandlers, cur->SignalHandlers, sizeof(new->SignalHandlers));
209 memset(&new->SignalState, 0, sizeof(tTaskState));
211 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
213 switch(cCONFIG_TYPES[i])
216 new->Config[i] = cur->Config[i];
220 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
231 * \fn Uint *Threads_GetCfgPtr(int Id)
233 Uint *Threads_GetCfgPtr(int Id)
235 if(Id < 0 || Id >= NUM_CFG_ENTRIES) {
236 Warning("Threads_GetCfgPtr: Index %i is out of bounds", Id);
240 return &Proc_GetCurThread()->Config[Id];
244 * \fn void Threads_WaitTID(int TID, int *status)
245 * \brief Wait for a task to change state
247 int Threads_WaitTID(int TID, int *status)
255 // Any peer/child thread
268 tThread *t = Threads_GetThread(TID);
269 int initStatus = t->Status;
272 if(initStatus != THREAD_STAT_ZOMBIE)
273 while(t->Status == initStatus) {
280 case THREAD_STAT_ZOMBIE:
281 t->Status = THREAD_STAT_DEAD;
282 if(status) *status = 0;
283 Threads_AddToDelete( t );
286 if(status) *status = -1;
296 * \fn tThread *Threads_GetThread(Uint TID)
297 * \brief Gets a thread given its TID
299 tThread *Threads_GetThread(Uint TID)
303 // Search Active List
304 for(thread = gActiveThreads;
306 thread = thread->Next)
308 if(thread->TID == TID)
312 // Search Sleeping List
313 for(thread = gSleepingThreads;
315 thread = thread->Next)
317 if(thread->TID == TID)
325 * \fn void Threads_AddToDelete(tThread *Thread)
326 * \brief Adds a thread to the delete queue
328 void Threads_AddToDelete(tThread *Thread)
330 // Add to delete queue
332 Thread->Next = gDeleteThreads;
333 gDeleteThreads = Thread;
336 gDeleteThreads = Thread;
341 * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
342 * \brief Gets the previous entry in a thead linked list
344 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
348 if(*List == Thread) {
349 return (tThread*)List;
352 ret->Next && ret->Next != Thread;
355 // Error if the thread is not on the list
356 if(!ret->Next || ret->Next != Thread) {
364 * \fn void Threads_Exit(int TID, int Status)
365 * \brief Exit the current process
367 void Threads_Exit(int TID, int Status)
370 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
372 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
373 for(;;) HALT(); // Just in case
377 * \fn void Threads_Kill(tThread *Thread, int Status)
378 * \brief Kill a thread
379 * \param Thread Thread to kill
380 * \param Status Status code to return to the parent
382 void Threads_Kill(tThread *Thread, int Status)
391 for(child = gActiveThreads;
395 if(child->PTID == Thread->TID)
396 Threads_Kill(child, -1);
401 ///\note Double lock is needed due to overlap of locks
403 // Lock thread (stop us recieving messages)
404 LOCK( &Thread->IsLocked );
407 LOCK( &giThreadListLock );
409 // Get previous thread on list
410 prev = Threads_int_GetPrev( &gActiveThreads, Thread );
412 Warning("Proc_Exit - Current thread is not on the active queue");
416 // Clear Message Queue
417 while( Thread->Messages )
419 msg = Thread->Messages->Next;
420 free( Thread->Messages );
421 Thread->Messages = msg;
424 Thread->Remaining = 0; // Clear Remaining Quantum
425 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
426 prev->Next = Thread->Next; // Remove from active
428 giNumActiveThreads --;
429 giTotalTickets -= Thread->NumTickets;
431 // Mark thread as a zombie
432 Thread->RetStatus = Status;
434 // Don't Zombie if we are being killed as part of a tree
437 Thread->Status = THREAD_STAT_DEAD;
438 Threads_AddToDelete( Thread );
440 Thread->Status = THREAD_STAT_ZOMBIE;
444 RELEASE( &Thread->IsLocked ); // Released first so that it IS released
445 RELEASE( &giThreadListLock );
447 //Log("Thread %i went *hurk*", Thread->TID);
449 if(Status != -1) HALT();
453 * \fn void Threads_Yield()
454 * \brief Yield remainder of timeslice
458 Proc_GetCurThread()->Remaining = 0;
463 * \fn void Threads_Sleep()
464 * \brief Take the current process off the run queue
468 tThread *cur = Proc_GetCurThread();
471 //Log_Log("Threads", "%i going to sleep", cur->TID);
474 LOCK( &giThreadListLock );
476 // Get thread before current thread
477 thread = Threads_int_GetPrev( &gActiveThreads, cur );
479 Warning("Threads_Sleep - Current thread is not on the active queue");
484 // Don't sleep if there is a message waiting
485 if( cur->Messages ) {
486 RELEASE( &giThreadListLock );
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( &giThreadListLock );
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( &giThreadListLock );
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 giTotalTickets += Thread->NumTickets;
533 Thread->Status = THREAD_STAT_ACTIVE;
534 RELEASE( &giThreadListLock );
536 case THREAD_STAT_WAITING:
537 Warning("Thread_Wake - Waiting threads are not currently supported");
539 case THREAD_STAT_DEAD:
540 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
543 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
548 void Threads_WakeTID(tTID Thread)
550 Threads_Wake( Threads_GetThread(Thread) );
554 * \fn void Threads_AddActive(tThread *Thread)
555 * \brief Adds a thread to the active queue
557 void Threads_AddActive(tThread *Thread)
559 LOCK( &giThreadListLock );
560 Thread->Next = gActiveThreads;
561 gActiveThreads = Thread;
562 giNumActiveThreads ++;
563 giTotalTickets += Thread->NumTickets;
564 //Log("Threads_AddActive: giNumActiveThreads = %i, giTotalTickets = %i",
565 // giNumActiveThreads, giTotalTickets);
566 RELEASE( &giThreadListLock );
571 * \fn void Threads_SetSignalHandler(int Num, void *Handler)
572 * \brief Sets the signal handler for a signal
574 void Threads_SetSignalHandler(int Num, void *Handler)
576 if(Num < 0 || Num >= NSIG) return;
578 gCurrentThread->SignalHandlers[Num] = Handler;
582 * \fn void Threads_SendSignal(int TID, int Num)
583 * \brief Send a signal to a thread
585 void Threads_SendSignal(int TID, int Num)
587 tThread *thread = Proc_GetThread(TID);
592 handler = thread->SignalHandlers[Num];
595 if(handler == SIG_ERR) {
605 if(handler == -2) return;
607 // Check the type and handle if the thread is already in a signal
608 if(thread->CurSignal != 0) {
609 if(Num < _SIGTYPE_FATAL)
612 while(thread->CurSignal != 0)
621 // --- Process Structure Access Functions ---
622 tPID Threads_GetPID()
624 return Proc_GetCurThread()->TGID;
626 tTID Threads_GetTID()
628 return Proc_GetCurThread()->TID;
630 tUID Threads_GetUID()
632 return Proc_GetCurThread()->UID;
634 tGID Threads_GetGID()
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()
665 * \brief Dums a list of currently running threads
670 tThread *cur = Proc_GetCurThread();
672 Log("Active Threads:");
673 for(thread=gActiveThreads;thread;thread=thread->Next)
675 Log("%c%i (%i) - %s",
676 (thread==cur?'*':' '),
677 thread->TID, thread->TGID, thread->ThreadName);
678 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
679 Log(" KStack 0x%x", thread->KernelStack);
681 Log("Sleeping Threads:");
682 for(thread=gSleepingThreads;thread;thread=thread->Next)
684 Log("%c%i (%i) - %s",
685 (thread==cur?'*':' '),
686 thread->TID, thread->TGID, thread->ThreadName);
687 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
688 Log(" KStack 0x%x", thread->KernelStack);
693 * \fn tThread *Threads_GetNextToRun(int CPU)
694 * \brief Gets the next thread to run
696 tThread *Threads_GetNextToRun(int CPU)
702 if(giNumActiveThreads == 0) {
703 //Log_Debug("Threads", "CPU%i has no threads to run", CPU);
707 // Special case: 1 thread
708 if(giNumActiveThreads == 1) {
709 //Log_Debug("Threads", "CPU%i has only one thread %i %s",
710 // CPU, gActiveThreads->TID, gActiveThreads->ThreadName);
711 return gActiveThreads;
714 //Log(" Threads_GetNextToRun: giNumActiveThreads=%i,giTotalTickets=%i",
715 // giNumActiveThreads, giTotalTickets);
716 // Get the ticket number
717 ticket = number = rand() % giTotalTickets;
719 //Log(" Threads_GetNextToRun: ticket = %i", ticket);
721 // Find the next thread
722 for(thread=gActiveThreads;thread;thread=thread->Next)
724 if(thread->NumTickets > number) break;
725 number -= thread->NumTickets;
732 for(thread=gActiveThreads;thread;thread=thread->Next)
733 number += thread->NumTickets;
734 Panic("Bookeeping Failed - giTotalTicketCount (%i) != true count (%i)",
735 giTotalTickets, number);
738 //Log_Debug("Threads", "Switching CPU%i to %p (%s)",
739 // CPU, thread, thread->ThreadName);
745 * \fn void Threads_SegFault(tVAddr Addr)
746 * \brief Called when a Segment Fault occurs
748 void Threads_SegFault(tVAddr Addr)
750 //Threads_SendSignal( Proc_GetCurThread()->TID, SIGSEGV );
751 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
752 Threads_Exit( 0, -1 );
756 EXPORT(Threads_GetUID);