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

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