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

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