Various Changes
[tpg/acess2.git] / Kernel / threads.c
1 /*
2  * Acess2
3  * threads.c
4  * - Common Thread Control
5  */
6 #include <common.h>
7 #include <threads.h>
8 #include <errno.h>
9
10 // === CONSTANTS ===
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
17         CFGT_NULL
18 };
19
20 // === IMPORTS ===
21 extern void     ArchThreads_Init();
22 extern void     Proc_Start();
23 extern tThread  *Proc_GetCurThread();
24 extern int      Proc_Clone(Uint *Err, Uint Flags);
25
26 // === PROTOTYPES ===
27 void    Threads_Init();
28 void    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);
38 void    Threads_Yield();
39 void    Threads_Sleep();
40 void    Threads_Wake(tThread *Thread);
41 void    Threads_AddActive(tThread *Thread);
42  int    Threads_GetPID();
43  int    Threads_GetTID();
44  int    Threads_GetUID();
45  int    Threads_GetGID();
46 void    Threads_Dump();
47
48 // === GLOBALS ===
49 // -- Core Thread --
50 tThread gThreadZero = {
51         NULL, 0,        // Next, Lock
52         THREAD_STAT_ACTIVE,     // Status
53         0,      // Exit Status
54         0, 0,   // TID, TGID
55         0, 0,   // UID, GID
56         0,      // Parent Thread ID
57         "ThreadZero",   // Name
58         
59         0,      // Kernel Stack
60         {0},    // Saved State
61         {0},    // VM State
62         
63         0, {0}, {0},    // Signal State
64         
65         NULL, NULL,     // Messages, Last Message
66         DEFAULT_QUANTUM, DEFAULT_QUANTUM,       // Quantum, Remaining
67         DEFAULT_TICKETS,
68         {0}     // Default config to zero
69         };
70 // -- Processes --
71 // --- Locks ---
72 volatile int    giThreadListLock = 0;   ///\note NEVER use a heap function while locked
73 // --- Current State ---
74 volatile int    giNumActiveThreads = 0;
75 volatile int    giTotalTickets = 0;
76 volatile Uint   giNextTID = 1;
77 // --- Thread Lists ---
78 tThread *gActiveThreads = NULL;         // Currently Running Threads
79 tThread *gSleepingThreads = NULL;       // Sleeping Threads
80 tThread *gDeleteThreads = NULL;         // Threads to delete
81  int    giNumCPUs = 1;
82
83 // === CODE ===
84 /**
85  * \fn void Threads_Init()
86  * \brief Initialse the thread list
87  */
88 void Threads_Init()
89 {
90         ArchThreads_Init();
91         
92         // Create Initial Task
93         gActiveThreads = &gThreadZero;
94         giTotalTickets = gThreadZero.NumTickets;
95         giNumActiveThreads = 1;
96         
97         #if 1
98         // Create Idle Task
99         if(Proc_Clone(0, 0) == 0)
100         {
101                 tThread *cur = Proc_GetCurThread();
102                 cur->ThreadName = "Idle Thread";
103                 Threads_SetTickets(0);  // Never called randomly
104                 cur->Quantum = 1;       // 1 slice quantum
105                 HALT();
106                 for(;;) {
107                         HALT(); // Just yeilds
108                 }
109         }
110         #endif
111         
112         Proc_Start();
113 }
114
115 /**
116  * \fn void Threads_SetName(char *NewName)
117  * \brief Sets the current thread's name
118  */
119 void Threads_SetName(char *NewName)
120 {
121         tThread *cur = Proc_GetCurThread();
122         if( IsHeap(cur->ThreadName) )
123                 free( cur->ThreadName );
124         cur->ThreadName = malloc(strlen(NewName)+1);
125         strcpy(cur->ThreadName, NewName);
126 }
127
128 /**
129  * \fn char *Threads_GetName(int ID)
130  * \brief Gets a thread's name
131  */
132 char *Threads_GetName(int ID)
133 {
134         if(ID == -1) {
135                 return Proc_GetCurThread()->ThreadName;
136         }
137         return NULL;
138 }
139
140 /**
141  * \fn void Threads_SetTickets(int Num)
142  * \brief Sets the 'priority' of a task
143  */
144 void Threads_SetTickets(int Num)
145 {
146         tThread *cur = Proc_GetCurThread();
147         if(Num < 0)     return;
148         if(Num > MAX_TICKETS)   Num = MAX_TICKETS;
149         
150         LOCK( &giThreadListLock );
151         giTotalTickets -= cur->NumTickets;
152         cur->NumTickets = Num;
153         giTotalTickets += Num;
154         //LOG("giTotalTickets = %i", giTotalTickets);
155         RELEASE( &giThreadListLock );
156 }
157
158 /**
159  * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
160  */
161 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
162 {
163         tThread *cur, *new;
164          int    i;
165         cur = Proc_GetCurThread();
166         
167         new = malloc(sizeof(tThread));
168         if(new == NULL) {
169                 *Err = -ENOMEM;
170                 return NULL;
171         }
172         
173         new->Next = NULL;
174         new->IsLocked = 0;
175         new->Status = THREAD_STAT_ACTIVE;
176         new->RetStatus = 0;
177         
178         // Get Thread ID
179         new->TID = giNextTID++;
180         new->PTID = cur->TID;
181         
182         // Clone Name
183         new->ThreadName = malloc(strlen(cur->ThreadName)+1);
184         strcpy(new->ThreadName, cur->ThreadName);
185         
186         // Set Thread Group ID (PID)
187         if(Flags & CLONE_VM)
188                 new->TGID = new->TID;
189         else
190                 new->TGID = cur->TGID;
191         
192         // Messages are not inherited
193         new->Messages = NULL;
194         new->LastMessage = NULL;
195         
196         // Set State
197         new->Remaining = new->Quantum = cur->Quantum;
198         new->NumTickets = cur->NumTickets;
199         
200         // Set Signal Handlers
201         new->CurSignal = 0;
202         if(Flags & CLONE_VM)
203                 memset(new->SignalHandlers, 0, sizeof(new->SignalHandlers));
204         else
205                 memcpy(new->SignalHandlers, cur->SignalHandlers, sizeof(new->SignalHandlers));
206         memset(&new->SignalState, 0, sizeof(tTaskState));
207         
208         for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
209         {
210                 switch(cCONFIG_TYPES[i])
211                 {
212                 default:
213                         new->Config[i] = cur->Config[i];
214                         break;
215                 case CFGT_HEAPSTR:
216                         if(cur->Config[i])
217                                 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
218                         else
219                                 new->Config[i] = 0;
220                         break;
221                 }
222         }
223         
224         return new;
225 }
226
227 /**
228  * \fn Uint *Threads_GetCfgPtr(int Id)
229  */
230 Uint *Threads_GetCfgPtr(int Id)
231 {
232         if(Id < 0 || Id >= NUM_CFG_ENTRIES) {
233                 Warning("Threads_GetCfgPtr: Index %i is out of bounds", Id);
234                 return NULL;
235         }
236         
237         return &Proc_GetCurThread()->Config[Id];
238 }
239
240 /**
241  * \fn void Threads_WaitTID(int TID, int *status)
242  * \brief Wait for a task to change state
243  */
244 int Threads_WaitTID(int TID, int *status)
245 {       
246         // Any Child
247         if(TID == -1) {
248                 
249                 return -1;
250         }
251         
252         // Any peer/child thread
253         if(TID == 0) {
254                 
255                 return -1;
256         }
257         
258         // TGID = abs(TID)
259         if(TID < -1) {
260                 return -1;
261         }
262         
263         // Specific Thread
264         if(TID > 0) {
265                 tThread *t = Threads_GetThread(TID);
266                  int    initStatus = t->Status;
267                  int    ret;
268                 
269                 if(initStatus != THREAD_STAT_ZOMBIE)
270                         while(t->Status == initStatus) {
271                                 Threads_Yield();
272                         }
273                 
274                 ret = t->RetStatus;
275                 switch(t->Status)
276                 {
277                 case THREAD_STAT_ZOMBIE:
278                         t->Status = THREAD_STAT_DEAD;
279                         if(status)      *status = 0;
280                         Threads_AddToDelete( t );
281                         break;
282                 default:
283                         if(status)      *status = -1;
284                         break;
285                 }
286                 return ret;
287         }
288         
289         return -1;
290 }
291
292 /**
293  * \fn tThread *Threads_GetThread(Uint TID)
294  * \brief Gets a thread given its TID
295  */
296 tThread *Threads_GetThread(Uint TID)
297 {
298         tThread *thread;
299         
300         // Search Active List
301         for(thread = gActiveThreads;
302                 thread;
303                 thread = thread->Next)
304         {
305                 if(thread->TID == TID)
306                         return thread;
307         }
308         
309         // Search Sleeping List
310         for(thread = gSleepingThreads;
311                 thread;
312                 thread = thread->Next)
313         {
314                 if(thread->TID == TID)
315                         return thread;
316         }
317         
318         return NULL;
319 }
320
321 /**
322  * \fn void Threads_AddToDelete(tThread *Thread)
323  * \brief Adds a thread to the delete queue
324  */
325 void Threads_AddToDelete(tThread *Thread)
326 {
327         // Add to delete queue
328         if(gDeleteThreads) {
329                 Thread->Next = gDeleteThreads;
330                 gDeleteThreads = Thread;
331         } else {
332                 Thread->Next = NULL;
333                 gDeleteThreads = Thread;
334         }
335 }
336
337 /**
338  * \fn tThread *Threads_int_GetPrev(tThread *List, tThread *Thread)
339  * \brief Gets the previous entry in a thead linked list
340  */
341 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
342 {
343         tThread *ret;
344         // First Entry
345         if(*List == Thread) {
346                 return (tThread*)List;
347         } else {
348                 for(ret = *List;
349                         ret->Next && ret->Next != Thread;
350                         ret = ret->Next
351                         );
352                 // Error if the thread is not on the list
353                 if(!ret->Next || ret->Next != Thread) {
354                         return NULL;
355                 }
356         }
357         return ret;
358 }
359
360 /**
361  * \fn void Threads_Exit(int TID, int Status)
362  * \brief Exit the current process
363  */
364 void Threads_Exit(int TID, int Status)
365 {
366         if( TID == 0 )
367                 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
368         else
369                 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
370         for(;;) HALT(); // Just in case
371 }
372
373 /**
374  * \fn void Threads_Kill(tThread *Thread, int Status)
375  * \brief Kill a thread
376  * \param TID   Thread ID (0 for current)
377  */
378 void Threads_Kill(tThread *Thread, int Status)
379 {
380         tThread *prev;
381         tMsg    *msg;
382         
383         // Kill all children
384         #if 0
385         {
386                 tThread *child;
387                 for(child = gActiveThreads;
388                         child;
389                         child = child->Next)
390                 {
391                         if(child->PTID == Thread->TID)
392                                 Threads_Kill(child, -1);
393                 }
394         }
395         #endif
396         
397         ///\note Double lock is needed due to overlap of locks
398         
399         // Lock thread (stop us recieving messages)
400         LOCK( &Thread->IsLocked );
401         
402         // Lock thread list
403         LOCK( &giThreadListLock );
404         
405         // Get previous thread on list
406         prev = Threads_int_GetPrev( &gActiveThreads, Thread );
407         if(!prev) {
408                 Warning("Proc_Exit - Current thread is not on the active queue");
409                 return;
410         }
411         
412         // Clear Message Queue
413         while( Thread->Messages )
414         {
415                 msg = Thread->Messages->Next;
416                 free( Thread->Messages );
417                 Thread->Messages = msg;
418         }
419         
420         Thread->Remaining = 0;  // Clear Remaining Quantum
421         Thread->Quantum = 0;    // Clear Quantum to indicate dead thread
422         prev->Next = Thread->Next;      // Remove from active
423         
424         giNumActiveThreads --;
425         giTotalTickets -= Thread->NumTickets;
426         
427         // Mark thread as a zombie
428         Thread->RetStatus = Status;
429         
430         // Don't Zombie if we are being killed as part of a tree
431         if(Status == -1)
432         {
433                 Thread->Status = THREAD_STAT_DEAD;
434                 Threads_AddToDelete( Thread );
435         } else {
436                 Thread->Status = THREAD_STAT_ZOMBIE;
437         }
438         
439         // Release spinlocks
440         RELEASE( &Thread->IsLocked );   // Released first so that it IS released
441         RELEASE( &giThreadListLock );
442         
443         //Log("Thread %i went *hurk*", Thread->TID);
444         
445         if(Status != -1)        HALT();
446 }
447
448 /**
449  * \fn void Threads_Yield()
450  * \brief Yield remainder of timeslice
451  */
452 void Threads_Yield()
453 {
454         Proc_GetCurThread()->Remaining = 0;
455         HALT();
456 }
457
458 /**
459  * \fn void Threads_Sleep()
460  * \brief Take the current process off the run queue
461  */
462 void Threads_Sleep()
463 {
464         tThread *cur = Proc_GetCurThread();
465         tThread *thread;
466         
467         Log("Proc_Sleep: %i going to sleep", cur->TID);
468         
469         // Acquire Spinlock
470         LOCK( &giThreadListLock );
471         
472         // Get thread before current thread
473         thread = Threads_int_GetPrev( &gActiveThreads, cur );
474         if(!thread) {
475                 Warning("Threads_Sleep - Current thread is not on the active queue");
476                 Threads_Dump();
477                 return;
478         }
479         
480         // Don't sleep if there is a message waiting
481         if( cur->Messages ) {
482                 RELEASE( &giThreadListLock );
483                 return;
484         }
485         
486         // Unset remaining timeslices (force a task switch on timer fire)
487         cur->Remaining = 0;
488         
489         // Remove from active list
490         thread->Next = cur->Next;
491         
492         // Add to Sleeping List (at the top)
493         cur->Next = gSleepingThreads;
494         gSleepingThreads = cur;
495         
496         // Reduce the active count & ticket count
497         giNumActiveThreads --;
498         giTotalTickets -= cur->NumTickets;
499         
500         // Mark thread as sleeping
501         cur->Status = THREAD_STAT_SLEEPING;
502         
503         // Release Spinlock
504         RELEASE( &giThreadListLock );
505         
506         while(cur->Status != THREAD_STAT_ACTIVE)        HALT();
507 }
508
509
510 /**
511  * \fn void Threads_Wake( tThread *Thread )
512  * \brief Wakes a sleeping/waiting thread up
513  */
514 void Threads_Wake(tThread *Thread)
515 {
516         tThread *prev;
517         switch(Thread->Status)
518         {
519         case THREAD_STAT_ACTIVE:        break;
520         case THREAD_STAT_SLEEPING:
521                 LOCK( &giThreadListLock );
522                 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
523                 prev->Next = Thread->Next;      // Remove from sleeping queue
524                 Thread->Next = gActiveThreads;  // Add to active queue
525                 gActiveThreads = Thread;
526                 Thread->Status = THREAD_STAT_ACTIVE;
527                 RELEASE( &giThreadListLock );
528                 break;
529         case THREAD_STAT_WAITING:
530                 Warning("Thread_Wake - Waiting threads are not currently supported");
531                 break;
532         case THREAD_STAT_DEAD:
533                 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
534                 break;
535         default:
536                 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
537                 break;
538         }
539 }
540
541 /**
542  * \fn void Threads_AddActive(tThread *Thread)
543  * \brief Adds a thread to the active queue
544  */
545 void Threads_AddActive(tThread *Thread)
546 {
547         LOCK( &giThreadListLock );
548         Thread->Next = gActiveThreads;
549         gActiveThreads = Thread;
550         giNumActiveThreads ++;
551         giTotalTickets += Thread->NumTickets;
552         //Log("Threads_AddActive: giNumActiveThreads = %i, giTotalTickets = %i",
553         //      giNumActiveThreads, giTotalTickets);
554         RELEASE( &giThreadListLock );
555 }
556
557 #if 0
558 /**
559  * \fn void Threads_SetSignalHandler(int Num, void *Handler)
560  * \brief Sets the signal handler for a signal
561  */
562 void Threads_SetSignalHandler(int Num, void *Handler)
563 {
564         if(Num < 0 || Num >= NSIG)      return;
565         
566         gCurrentThread->SignalHandlers[Num] = Handler;
567 }
568
569 /**
570  * \fn void Threads_SendSignal(int TID, int Num)
571  * \brief Send a signal to a thread
572  */
573 void Threads_SendSignal(int TID, int Num)
574 {
575         tThread *thread = Proc_GetThread(TID);
576         void    *handler;
577         
578         if(!thread)     return ;
579         
580         handler = thread->SignalHandlers[Num];
581         
582         // Panic?
583         if(handler == SIG_ERR) {
584                 Proc_Kill(TID);
585                 return ;
586         }
587         // Dump Core?
588         if(handler == -2) {
589                 Proc_Kill(TID);
590                 return ;
591         }
592         // Ignore?
593         if(handler == -2)       return;
594         
595         // Check the type and handle if the thread is already in a signal
596         if(thread->CurSignal != 0) {
597                 if(Num < _SIGTYPE_FATAL)
598                         Proc_Kill(TID);
599                 } else {
600                         while(thread->CurSignal != 0)
601                                 Proc_Yield();
602                 }
603         }
604         
605         //TODO: 
606 }
607 #endif
608
609 // --- Process Structure Access Functions ---
610 int Threads_GetPID()
611 {
612         return Proc_GetCurThread()->TGID;
613 }
614 int Threads_GetTID()
615 {
616         return Proc_GetCurThread()->TID;
617 }
618 int Threads_GetUID()
619 {
620         return Proc_GetCurThread()->UID;
621 }
622 int Threads_GetGID()
623 {
624         return Proc_GetCurThread()->GID;
625 }
626
627 /**
628  * \fn void Threads_Dump()
629  * \brief Dums a list of currently running threads
630  */
631 void Threads_Dump()
632 {
633         tThread *thread;
634         tThread *cur = Proc_GetCurThread();
635         
636         Log("Active Threads:");
637         for(thread=gActiveThreads;thread;thread=thread->Next)
638         {
639                 Log("%c%i (%i) - %s",
640                         (thread==cur?'*':' '),
641                         thread->TID, thread->TGID, thread->ThreadName);
642                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
643                 Log("  KStack 0x%x", thread->KernelStack);
644         }
645         Log("Sleeping Threads:");
646         for(thread=gSleepingThreads;thread;thread=thread->Next)
647         {
648                 Log("%c%i (%i) - %s",
649                         (thread==cur?'*':' '),
650                         thread->TID, thread->TGID, thread->ThreadName);
651                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
652                 Log("  KStack 0x%x", thread->KernelStack);
653         }
654 }
655
656 /**
657  * \fn tThread *Threads_GetNextToRun(int CPU)
658  * \brief Gets the next thread to run
659  */
660 tThread *Threads_GetNextToRun(int CPU)
661 {
662         tThread *thread;
663          int    ticket;
664          int    number;
665         
666         if(giNumActiveThreads == 0)     return NULL;
667         
668         // Special case: 1 thread
669         if(giNumActiveThreads == 1) {
670                 return gActiveThreads;
671         }
672         
673         //Log(" Threads_GetNextToRun: giNumActiveThreads=%i,giTotalTickets=%i",
674         //      giNumActiveThreads, giTotalTickets);
675         // Get the ticket number
676         ticket = number = rand() % giTotalTickets;
677         
678         //Log(" Threads_GetNextToRun: ticket = %i", ticket);
679         
680         // Find the next thread
681         for(thread=gActiveThreads;thread;thread=thread->Next)
682         {
683                 if(thread->NumTickets > number) break;
684                 number -= thread->NumTickets;
685         }
686         
687         // Error Check
688         if(thread == NULL)
689         {
690                 number = 0;
691                 for(thread=gActiveThreads;thread;thread=thread->Next)
692                         number += thread->NumTickets;
693                 Panic("Bookeeping Failed - giTotalTicketCount (%i) != true count (%i)",
694                         giTotalTickets, number);
695         }
696         
697         return thread;
698 }
699
700 /**
701  * \fn void Threads_SegFault(tVAddr Addr)
702  * \brief Called when a Segment Fault occurs
703  */
704 void Threads_SegFault(tVAddr Addr)
705 {
706         //Threads_SendSignal( Proc_GetCurThread()->TID, SIGSEGV );
707         Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
708         Threads_Exit( 0, -1 );
709 }

UCC git Repository :: git.ucc.asn.au