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

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