Kernel - Split Mutexes/Semaphores out
[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 <threads_int.h>
9 #include <errno.h>
10 #include <hal_proc.h>
11 #include <semaphore.h>
12
13 // Configuration
14 #define DEBUG_TRACE_TICKETS     0       // Trace ticket counts
15 #define DEBUG_TRACE_STATE       0       // Trace state changes (sleep/wake)
16
17 // --- Schedulers ---
18 #define SCHED_UNDEF     0
19 #define SCHED_LOTTERY   1       // Lottery scheduler
20 #define SCHED_RR_SIM    2       // Single Queue Round Robin
21 #define SCHED_RR_PRI    3       // Multi Queue Round Robin
22 // Set scheduler type
23 #define SCHEDULER_TYPE  SCHED_RR_PRI
24
25 // === CONSTANTS ===
26 #define DEFAULT_QUANTUM 5
27 #define DEFAULT_PRIORITY        5
28 #define MIN_PRIORITY            10
29 const enum eConfigTypes cCONFIG_TYPES[] = {
30         CFGT_HEAPSTR,   // e.g. CFG_VFS_CWD
31         CFGT_INT,       // e.g. CFG_VFS_MAXFILES
32         CFGT_NULL
33 };
34
35 // === IMPORTS ===
36
37 // === PROTOTYPES ===
38 void    Threads_Init(void);
39 #if 0
40  int    Threads_SetName(const char *NewName);
41 #endif
42 char    *Threads_GetName(int ID);
43 #if 0
44 void    Threads_SetPriority(tThread *Thread, int Pri);
45 tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
46  int    Threads_WaitTID(int TID, int *status);
47 tThread *Threads_GetThread(Uint TID);
48 #endif
49 void    Threads_AddToDelete(tThread *Thread);
50 tThread *Threads_int_DelFromQueue(tThread **List, tThread *Thread);
51 #if 0
52 void    Threads_Exit(int TID, int Status);
53 void    Threads_Kill(tThread *Thread, int Status);
54 void    Threads_Yield(void);
55 void    Threads_Sleep(void);
56  int    Threads_Wake(tThread *Thread);
57 void    Threads_AddActive(tThread *Thread);
58 tThread *Threads_RemActive(void);
59 #endif
60 void    Threads_ToggleTrace(int TID);
61 void    Threads_Fault(int Num);
62 void    Threads_SegFault(tVAddr Addr);
63 #if 0
64  int    Threads_GetPID(void);
65  int    Threads_GetTID(void);
66 tUID    Threads_GetUID(void);
67 tGID    Threads_GetGID(void);
68  int    Threads_SetUID(Uint *Errno, tUID ID);
69  int    Threads_SetGID(Uint *Errno, tUID ID);
70 #endif
71 void    Threads_Dump(void);
72 void    Threads_DumpActive(void);
73
74 // === GLOBALS ===
75 // -- Core Thread --
76 // Only used for the core kernel
77 tThread gThreadZero = {
78         .Status         = THREAD_STAT_ACTIVE,   // Status
79         .ThreadName     = (char*)"ThreadZero",  // Name
80         .Quantum        = DEFAULT_QUANTUM,      // Default Quantum
81         .Remaining      = DEFAULT_QUANTUM,      // Current Quantum
82         .Priority       = DEFAULT_PRIORITY      // Number of tickets
83         };
84 // -- Processes --
85 // --- Locks ---
86 tShortSpinlock  glThreadListLock;       ///\note NEVER use a heap function while locked
87 // --- Current State ---
88 volatile int    giNumActiveThreads = 0; // Number of threads on the active queue
89 volatile Uint   giNextTID = 1;  // Next TID to allocate
90 // --- Thread Lists ---
91 tThread *gAllThreads = NULL;            // All allocated threads
92 tThread *gSleepingThreads = NULL;       // Sleeping Threads
93 tThread *gDeleteThreads = NULL;         // Threads to delete
94  int    giNumCPUs = 1;  // Number of CPUs
95 BOOL     gaThreads_NoTaskSwitch[MAX_CPUS];      // Disables task switches for each core (Pseudo-IF)
96 // --- Scheduler Types ---
97 #if SCHEDULER_TYPE == SCHED_LOTTERY
98 const int       caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0};
99 volatile int    giFreeTickets = 0;      // Number of tickets held by non-scheduled threads
100 tThread *gActiveThreads = NULL;         // Currently Running Threads
101 #elif SCHEDULER_TYPE == SCHED_RR_SIM
102 tThread *gActiveThreads = NULL;         // Currently Running Threads
103 #elif SCHEDULER_TYPE == SCHED_RR_PRI
104 tThread *gaActiveThreads[MIN_PRIORITY+1];       // Active threads for each priority level
105 #else
106 # error "Unkown scheduler type"
107 #endif
108
109 // === CODE ===
110 /**
111  * \fn void Threads_Init(void)
112  * \brief Initialse the thread list
113  */
114 void Threads_Init(void)
115 {
116         ArchThreads_Init();
117         
118         Log_Debug("Threads", "Offsets of tThread");
119         Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority));
120         Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack));
121         
122         // Create Initial Task
123         #if SCHEDULER_TYPE == SCHED_RR_PRI
124         gaActiveThreads[gThreadZero.Priority] = &gThreadZero;
125         #else
126         gActiveThreads = &gThreadZero;
127         #endif
128         
129         gAllThreads = &gThreadZero;
130         giNumActiveThreads = 1;
131                 
132         Proc_Start();
133 }
134
135 /**
136  * \fn void Threads_SetName(const char *NewName)
137  * \brief Sets the current thread's name
138  * \param NewName       New name for the thread
139  * \return Boolean Failure
140  */
141 int Threads_SetName(const char *NewName)
142 {
143         tThread *cur = Proc_GetCurThread();
144         char    *oldname = cur->ThreadName;
145         
146         // NOTE: There is a possibility of non-thread safety here
147         // A thread could read the current name pointer before it is zeroed
148         
149         cur->ThreadName = NULL;
150         
151         if( IsHeap(oldname) )   free( oldname );
152         
153         cur->ThreadName = strdup(NewName);
154         return 0;
155 }
156
157 /**
158  * \fn char *Threads_GetName(int ID)
159  * \brief Gets a thread's name
160  * \param ID    Thread ID (-1 indicates current thread)
161  * \return Pointer to name
162  * \retval NULL Failure
163  */
164 char *Threads_GetName(tTID ID)
165 {
166         if(ID == -1) {
167                 return Proc_GetCurThread()->ThreadName;
168         }
169         return Threads_GetThread(ID)->ThreadName;
170 }
171
172 /**
173  * \fn void Threads_SetPriority(tThread *Thread, int Pri)
174  * \brief Sets the priority of a task
175  * \param Thread        Thread to update ticket count (NULL means current thread)
176  * \param Pri   New priority
177  */
178 void Threads_SetPriority(tThread *Thread, int Pri)
179 {
180         // Get current thread
181         if(Thread == NULL)      Thread = Proc_GetCurThread();
182         // Bounds checking
183         // - If < 0, set to lowest priority
184         // - Minumum priority is actualy a high number, 0 is highest
185         if(Pri < 0)     Pri = MIN_PRIORITY;
186         if(Pri > MIN_PRIORITY)  Pri = MIN_PRIORITY;
187         
188         // Do we actually have to do anything?
189         if( Pri == Thread->Priority )   return;
190         
191         #if SCHEDULER_TYPE == SCHED_RR_PRI
192         SHORTLOCK( &glThreadListLock );
193         // Remove from old priority
194         Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread );
195         // And add to new
196         Thread->Next = gaActiveThreads[Pri];
197         gaActiveThreads[Pri] = Thread;
198         Thread->Priority = Pri;
199         SHORTREL( &glThreadListLock );
200         #else
201         // If this isn't the current thread, we need to lock
202         if( Thread != Proc_GetCurThread() )
203         {
204                 SHORTLOCK( &glThreadListLock );
205                 
206                 #if SCHEDULER_TYPE == SCHED_LOTTERY
207                 giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri];
208                 # if DEBUG_TRACE_TICKETS
209                 Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]",
210                         giFreeTickets,
211                         caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]);
212                 # endif
213                 #endif
214                 Thread->Priority = Pri;
215                 SHORTREL( &glThreadListLock );
216         }
217         else
218                 Thread->Priority = Pri;
219         #endif
220         
221         #if DEBUG_TRACE_STATE
222         Log("Threads_SetPriority: %p(%i %s) pri set %i",
223                 Thread, Thread->TID, Thread->ThreadName,
224                 Pri);
225         #endif
226 }
227
228 /**
229  * \brief Clone the TCB of the current thread
230  * \param Flags Flags for something... (What is this for?)
231  */
232 tThread *Threads_CloneTCB(Uint Flags)
233 {
234         tThread *cur, *new;
235          int    i;
236         cur = Proc_GetCurThread();
237         
238         // Allocate and duplicate
239         new = malloc(sizeof(tThread));
240         if(new == NULL) { errno = -ENOMEM; return NULL; }
241         memcpy(new, cur, sizeof(tThread));
242         
243         new->CurCPU = -1;
244         new->Next = NULL;
245         memset( &new->IsLocked, 0, sizeof(new->IsLocked));
246         new->Status = THREAD_STAT_PREINIT;
247         new->RetStatus = 0;
248         
249         // Get Thread ID
250         new->TID = giNextTID++;
251         new->Parent = cur;
252         new->bInstrTrace = 0;
253         
254         // Clone Name
255         new->ThreadName = strdup(cur->ThreadName);
256         
257         // Set Thread Group ID (PID)
258         if(Flags & CLONE_VM)
259                 new->TGID = new->TID;
260         else
261                 new->TGID = cur->TGID;
262         
263         // Messages are not inherited
264         new->Messages = NULL;
265         new->LastMessage = NULL;
266         
267         // Set State
268         new->Remaining = new->Quantum = cur->Quantum;
269         new->Priority = cur->Priority;
270         
271         // Set Signal Handlers
272         new->CurFaultNum = 0;
273         new->FaultHandler = cur->FaultHandler;
274         
275         for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
276         {
277                 switch(cCONFIG_TYPES[i])
278                 {
279                 default:
280                         new->Config[i] = cur->Config[i];
281                         break;
282                 case CFGT_HEAPSTR:
283                         if(cur->Config[i])
284                                 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
285                         else
286                                 new->Config[i] = 0;
287                         break;
288                 }
289         }
290         
291         // Maintain a global list of threads
292         SHORTLOCK( &glThreadListLock );
293         new->GlobalPrev = NULL; // Protect against bugs
294         new->GlobalNext = gAllThreads;
295         gAllThreads->GlobalPrev = new;
296         gAllThreads = new;
297         SHORTREL( &glThreadListLock );
298         
299         return new;
300 }
301
302 /**
303  * \brief Clone the TCB of the kernel thread
304  */
305 tThread *Threads_CloneThreadZero(void)
306 {
307         tThread *new;
308          int    i;
309         
310         // Allocate and duplicate
311         new = malloc(sizeof(tThread));
312         if(new == NULL) {
313                 return NULL;
314         }
315         memcpy(new, &gThreadZero, sizeof(tThread));
316         
317         new->CurCPU = -1;
318         new->Next = NULL;
319         memset( &new->IsLocked, 0, sizeof(new->IsLocked));
320         new->Status = THREAD_STAT_PREINIT;
321         new->RetStatus = 0;
322         
323         // Get Thread ID
324         new->TID = giNextTID++;
325         new->Parent = 0;
326         
327         // Clone Name
328         new->ThreadName = NULL;
329         
330         // Messages are not inherited
331         new->Messages = NULL;
332         new->LastMessage = NULL;
333         
334         // Set State
335         new->Remaining = new->Quantum = DEFAULT_QUANTUM;
336         new->Priority = DEFAULT_PRIORITY;
337         new->bInstrTrace = 0;
338         
339         // Set Signal Handlers
340         new->CurFaultNum = 0;
341         new->FaultHandler = 0;
342         
343         for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
344         {
345                 new->Config[i] = 0;
346         }
347         
348         // Maintain a global list of threads
349         SHORTLOCK( &glThreadListLock );
350         new->GlobalPrev = NULL; // Protect against bugs
351         new->GlobalNext = gAllThreads;
352         gAllThreads->GlobalPrev = new;
353         gAllThreads = new;
354         SHORTREL( &glThreadListLock );
355         
356         return new;
357 }
358
359 /**
360  * \brief Get a configuration pointer from the Per-Thread data area
361  * \param ID    Config slot ID
362  * \return Pointer at ID
363  */
364 Uint *Threads_GetCfgPtr(int ID)
365 {
366         if(ID < 0 || ID >= NUM_CFG_ENTRIES) {
367                 Warning("Threads_GetCfgPtr: Index %i is out of bounds", ID);
368                 return NULL;
369         }
370         
371         return &Proc_GetCurThread()->Config[ID];
372 }
373
374 /**
375  * \brief Wait for a task to change state
376  * \param TID   Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
377  * \param Status        Thread return status
378  * \return TID of child that changed state
379  */
380 tTID Threads_WaitTID(int TID, int *Status)
381 {       
382         // Any Child
383         if(TID == -1) {
384                 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
385                 return -1;
386         }
387         
388         // Any peer/child thread
389         if(TID == 0) {
390                 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
391                 return -1;
392         }
393         
394         // TGID = abs(TID)
395         if(TID < -1) {
396                 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
397                 return -1;
398         }
399         
400         // Specific Thread
401         if(TID > 0) {
402                 tThread *t = Threads_GetThread(TID);
403                  int    initStatus = t->Status;
404                 tTID    ret;
405                 
406                 // Wait for the thread to die!
407                 if(initStatus != THREAD_STAT_ZOMBIE) {
408                         // TODO: Handle child also being suspended if wanted
409                         while(t->Status != THREAD_STAT_ZOMBIE) {
410                                 Threads_Sleep();
411                                 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
412                                         Threads_GetTID(), t->TID, t->Status);
413                         }
414                 }
415                 
416                 // Set return status
417                 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
418                         Threads_GetTID(), t->TID, t->Status);
419                 ret = t->TID;
420                 switch(t->Status)
421                 {
422                 case THREAD_STAT_ZOMBIE:
423                         // Kill the thread
424                         t->Status = THREAD_STAT_DEAD;
425                         // TODO: Child return value?
426                         if(Status)      *Status = t->RetStatus;
427                         // add to delete queue
428                         Threads_AddToDelete( t );
429                         break;
430                 default:
431                         if(Status)      *Status = -1;
432                         break;
433                 }
434                 return ret;
435         }
436         
437         return -1;
438 }
439
440 /**
441  * \brief Gets a thread given its TID
442  * \param TID   Thread ID
443  * \return Thread pointer
444  */
445 tThread *Threads_GetThread(Uint TID)
446 {
447         tThread *thread;
448         
449         // Search global list
450         for(thread = gAllThreads;
451                 thread;
452                 thread = thread->GlobalNext)
453         {
454                 if(thread->TID == TID)
455                         return thread;
456         }
457
458         Log("Unable to find TID %i on main list\n", TID);
459         
460         return NULL;
461 }
462
463 /**
464  * \brief Adds a thread to the delete queue
465  * \param Thread        Thread to delete
466  */
467 void Threads_AddToDelete(tThread *Thread)
468 {
469         // Add to delete queue
470         // TODO: Is locking needed?
471         if(gDeleteThreads) {
472                 Thread->Next = gDeleteThreads;
473                 gDeleteThreads = Thread;
474         } else {
475                 Thread->Next = NULL;
476                 gDeleteThreads = Thread;
477         }
478 }
479
480 /**
481  * \brief Deletes an entry from a list
482  * \param List  Pointer to the list head
483  * \param Thread        Thread to find
484  * \return \a Thread
485  */
486 tThread *Threads_int_DelFromQueue(tThread **List, tThread *Thread)
487 {
488         tThread *ret, *prev = NULL;
489         
490         for(ret = *List;
491                 ret && ret != Thread;
492                 prev = ret, ret = ret->Next
493                 );
494         
495         // Is the thread on the list
496         if(!ret) {
497                 //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
498                 return NULL;
499         }
500         
501         if( !prev ) {
502                 *List = Thread->Next;
503                 //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
504         }
505         else {
506                 prev->Next = Thread->Next;
507                 //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
508         }
509         
510         return Thread;
511 }
512
513 /**
514  * \brief Exit the current process (or another?)
515  * \param TID   Thread ID to kill
516  * \param Status        Exit status
517  */
518 void Threads_Exit(int TID, int Status)
519 {
520         if( TID == 0 )
521                 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
522         else
523                 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
524         
525         // Halt forever, just in case
526         for(;;) HALT();
527 }
528
529 /**
530  * \fn void Threads_Kill(tThread *Thread, int Status)
531  * \brief Kill a thread
532  * \param Thread        Thread to kill
533  * \param Status        Status code to return to the parent
534  */
535 void Threads_Kill(tThread *Thread, int Status)
536 {
537         tMsg    *msg;
538          int    isCurThread = Thread == Proc_GetCurThread();
539         
540         // TODO: Kill all children
541         #if 1
542         {
543                 tThread *child;
544                 // TODO: I should keep a .Parent pointer, and a .Children list
545                 for(child = gAllThreads;
546                         child;
547                         child = child->GlobalNext)
548                 {
549                         if(child->Parent == Thread)
550                                 Threads_Kill(child, -1);
551                 }
552         }
553         #endif
554         
555         ///\note Double lock is needed due to overlap of lock areas
556         
557         // Lock thread (stop us recieving messages)
558         SHORTLOCK( &Thread->IsLocked );
559         
560         // Clear Message Queue
561         while( Thread->Messages )
562         {
563                 msg = Thread->Messages->Next;
564                 free( Thread->Messages );
565                 Thread->Messages = msg;
566         }
567         
568         // Lock thread list
569         SHORTLOCK( &glThreadListLock );
570         
571         switch(Thread->Status)
572         {
573         case THREAD_STAT_PREINIT:       // Only on main list
574                 break;
575         
576         // Currently active thread
577         case THREAD_STAT_ACTIVE:
578                 #if SCHEDULER_TYPE == SCHED_RR_PRI
579                 if( Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread ) )
580                 #else
581                 if( Threads_int_DelFromQueue( &gActiveThreads, Thread ) )
582                 #endif
583                 {
584                         // Ensure that we are not rescheduled
585                         Thread->Remaining = 0;  // Clear Remaining Quantum
586                         Thread->Quantum = 0;    // Clear Quantum to indicate dead thread
587                         
588                         // Update bookkeeping
589                         giNumActiveThreads --;
590                         #if SCHEDULER_TYPE == SCHED_LOTTERY
591                         if( Thread != Proc_GetCurThread() )
592                                 giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ];
593                         #endif
594                 }
595                 else
596                 {
597                         Log_Warning("Threads",
598                                 "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list",
599                                 Thread, Thread->TID, Thread->ThreadName
600                                 );
601                 }
602                 break;
603         // Kill it while it sleeps!
604         case THREAD_STAT_SLEEPING:
605                 if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) )
606                 {
607                         Log_Warning("Threads",
608                                 "Threads_Kill - Thread %p(%i,%s) marked as sleeping, but not on list",
609                                 Thread, Thread->TID, Thread->ThreadName
610                                 );
611                 }
612                 break;
613         
614         // Brains!... You cannot kill something that is already dead
615         case THREAD_STAT_ZOMBIE:
616                 Log_Warning("Threads", "Threads_Kill - Thread %p(%i,%s) is undead, you cannot kill it",
617                         Thread, Thread->TID, Thread->ThreadName);
618                 SHORTREL( &glThreadListLock );
619                 SHORTREL( &Thread->IsLocked );
620                 return ;
621         
622         default:
623                 Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)",
624                         Thread->Status);
625                 break;
626         }
627         
628         // Save exit status
629         Thread->RetStatus = Status;
630
631         SHORTREL( &Thread->IsLocked );
632
633         // Don't Zombie if we are being killed because our parent is
634         if(Status == -1)
635         {
636                 Thread->Status = THREAD_STAT_DEAD;
637                 Threads_AddToDelete( Thread );
638                 SHORTREL( &glThreadListLock );
639         } else {
640                 Thread->Status = THREAD_STAT_ZOMBIE;
641                 SHORTREL( &glThreadListLock );
642                 // Wake parent
643                 Threads_Wake( Thread->Parent );
644         }
645         
646         Log("Thread %i went *hurk* (%i)", Thread->TID, Status);
647         
648         // And, reschedule
649         if(isCurThread)
650         {
651                 for( ;; )
652                         Proc_Reschedule();
653         }
654 }
655
656 /**
657  * \brief Yield remainder of the current thread's timeslice
658  */
659 void Threads_Yield(void)
660 {
661 //      Log("Threads_Yield: by %p", __builtin_return_address(0));
662         Proc_Reschedule();
663 }
664
665 /**
666  * \fn void Threads_Sleep(void)
667  * \brief Take the current process off the run queue
668  */
669 void Threads_Sleep(void)
670 {
671         tThread *cur = Proc_GetCurThread();
672         
673         // Acquire Spinlock
674         SHORTLOCK( &glThreadListLock );
675         
676         // Don't sleep if there is a message waiting
677         if( cur->Messages ) {
678                 SHORTREL( &glThreadListLock );
679                 return;
680         }
681         
682         // Remove us from running queue
683         Threads_RemActive();
684         // Mark thread as sleeping
685         cur->Status = THREAD_STAT_SLEEPING;
686         
687         // Add to Sleeping List (at the top)
688         cur->Next = gSleepingThreads;
689         gSleepingThreads = cur;
690         
691         
692         #if DEBUG_TRACE_STATE
693         Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
694         #endif
695         
696         // Release Spinlock
697         SHORTREL( &glThreadListLock );
698
699         while(cur->Status != THREAD_STAT_ACTIVE) {
700                 Proc_Reschedule();
701                 if( cur->Status != THREAD_STAT_ACTIVE )
702                         Log("%i - Huh? why am I up? zzzz...", cur->TID);
703         }
704 }
705
706
707 /**
708  * \brief Wakes a sleeping/waiting thread up
709  * \param Thread        Thread to wake
710  * \return Boolean Failure (Returns ERRNO)
711  * \warning This should ONLY be called with task switches disabled
712  */
713 int Threads_Wake(tThread *Thread)
714 {
715         if(!Thread)
716                 return -EINVAL;
717         
718         switch(Thread->Status)
719         {
720         case THREAD_STAT_ACTIVE:
721                 Log("Threads_Wake - Waking awake thread (%i)", Thread->TID);
722                 return -EALREADY;
723         
724         case THREAD_STAT_SLEEPING:
725                 SHORTLOCK( &glThreadListLock );
726                 // Remove from sleeping queue
727                 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
728                 
729                 SHORTREL( &glThreadListLock );
730                 Threads_AddActive( Thread );
731                 
732                 #if DEBUG_TRACE_STATE
733                 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
734                 #endif
735                 return -EOK;
736         
737         case THREAD_STAT_SEMAPHORESLEEP: {
738                 tSemaphore      *sem;
739                 tThread *th, *prev=NULL;
740                 
741                 sem = Thread->WaitPointer;
742                 
743                 SHORTLOCK( &sem->Protector );
744                 
745                 // Remove from sleeping queue
746                 for( th = sem->Waiting; th; prev = th, th = th->Next )
747                         if( th == Thread )      break;
748                 if( th )
749                 {
750                         if(prev)
751                                 prev->Next = Thread->Next;
752                         else
753                                 sem->Waiting = Thread->Next;
754                         if(sem->LastWaiting == Thread)
755                                 sem->LastWaiting = prev;
756                 }
757                 else
758                 {
759                         prev = NULL;
760                         for( th = sem->Signaling; th; prev = th, th = th->Next )
761                                 if( th == Thread )      break;
762                         if( !th ) {
763                                 Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)",
764                                         Thread, Thread->TID, Thread->ThreadName,
765                                         sem, sem->ModName, sem->Name);
766                                 return -EINTERNAL;
767                         }
768                         
769                         if(prev)
770                                 prev->Next = Thread->Next;
771                         else
772                                 sem->Signaling = Thread->Next;
773                         if(sem->LastSignaling == Thread)
774                                 sem->LastSignaling = prev;
775                 }
776                 
777                 Thread->RetStatus = 0;  // It didn't get anything
778                 Threads_AddActive( Thread );
779                 
780                 #if DEBUG_TRACE_STATE
781                 Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName);
782                 #endif
783                 SHORTREL( &sem->Protector );
784                 } return -EOK;
785         
786         case THREAD_STAT_WAITING:
787                 Warning("Threads_Wake - Waiting threads are not currently supported");
788                 return -ENOTIMPL;
789         
790         case THREAD_STAT_DEAD:
791                 Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID);
792                 return -ENOTIMPL;
793         
794         default:
795                 Warning("Threads_Wake - Unknown process status (%i)\n", Thread->Status);
796                 return -EINTERNAL;
797         }
798 }
799
800 /**
801  * \brief Wake a thread given the TID
802  * \param TID   Thread ID to wake
803  * \return Boolean Faulure (errno)
804  */
805 int Threads_WakeTID(tTID TID)
806 {
807         tThread *thread = Threads_GetThread(TID);
808          int    ret;
809         if(!thread)
810                 return -ENOENT;
811         ret = Threads_Wake( thread );
812         //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
813         return ret;
814 }
815
816 void Threads_ToggleTrace(int TID)
817 {
818         tThread *thread = Threads_GetThread(TID);
819         if(!thread)     return ;
820         thread->bInstrTrace = !thread->bInstrTrace;
821 }
822
823 /**
824  * \brief Adds a thread to the active queue
825  */
826 void Threads_AddActive(tThread *Thread)
827 {
828         SHORTLOCK( &glThreadListLock );
829         
830         if( Thread->Status == THREAD_STAT_ACTIVE ) {
831                 tThread *cur = Proc_GetCurThread();
832                 Log_Warning("Threads", "WTF, %p CPU%i %p (%i %s) is adding %p (%i %s) when it is active",
833                         __builtin_return_address(0),
834                         GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName);
835                 SHORTREL( &glThreadListLock );
836                 return ;
837         }
838         
839         // Set state
840         Thread->Status = THREAD_STAT_ACTIVE;
841 //      Thread->CurCPU = -1;
842         // Add to active list
843         {
844                 tThread *tmp, *prev = NULL;
845                 #if SCHEDULER_TYPE == SCHED_RR_PRI
846                 for( tmp = gaActiveThreads[Thread->Priority]; tmp; prev = tmp, tmp = tmp->Next );
847                 if(prev)
848                         prev->Next = Thread;
849                 else
850                         gaActiveThreads[Thread->Priority] = Thread;
851                 #else
852                 for( tmp = gActiveThreads; tmp; prev = tmp, tmp = tmp->Next );
853                 if(prev)
854                         prev->Next = Thread;
855                 else
856                         gActiveThreads = Thread;
857                 #endif
858                 Thread->Next = NULL;
859         }
860         
861         // Update bookkeeping
862         giNumActiveThreads ++;
863         
864         #if SCHEDULER_TYPE == SCHED_LOTTERY
865         {
866                  int    delta;
867                 // Only change the ticket count if the thread is un-scheduled
868                 if(Thread->CurCPU != -1)
869                         delta = 0;
870                 else
871                         delta = caiTICKET_COUNTS[ Thread->Priority ];
872                 
873                 giFreeTickets += delta;
874                 # if DEBUG_TRACE_TICKETS
875                 Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]",
876                         GetCPUNum(), Thread, Thread->TID, Thread->ThreadName,
877                         giFreeTickets, delta
878                         );
879                 # endif
880         }
881         #endif
882         
883         SHORTREL( &glThreadListLock );
884 }
885
886 /**
887  * \brief Removes the current thread from the active queue
888  * \warning This should ONLY be called with the lock held
889  * \return Current thread pointer
890  */
891 tThread *Threads_RemActive(void)
892 {
893         tThread *ret = Proc_GetCurThread();
894
895         if( !IS_LOCKED(&glThreadListLock) ) {
896                 Log_KernelPanic("Threads", "Threads_RemActive called without lock held");
897                 return NULL;
898         }
899         
900         // Delete from active queue
901         #if SCHEDULER_TYPE == SCHED_RR_PRI
902         if( !Threads_int_DelFromQueue(&gaActiveThreads[ret->Priority], ret) )
903         #else
904         if( !Threads_int_DelFromQueue(&gActiveThreads, ret) )
905         #endif
906         {
907                 SHORTREL( &glThreadListLock );
908                 Log_Warning("Threads", "Current thread %p(%i %s) is not on active queue",
909                         ret, ret->TID, ret->ThreadName
910                         );
911                 return NULL;
912         }
913         
914         ret->Next = NULL;
915         ret->Remaining = 0;
916         
917         giNumActiveThreads --;
918         // no need to decrement tickets, scheduler did it for us
919         
920         #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
921         Log("CPU%i %p (%i %s) removed, giFreeTickets = %i [nc]",
922                 GetCPUNum(), ret, ret->TID, ret->ThreadName, giFreeTickets);
923         #endif
924         
925         return ret;
926 }
927
928 /**
929  * \fn void Threads_SetFaultHandler(Uint Handler)
930  * \brief Sets the signal handler for a signal
931  */
932 void Threads_SetFaultHandler(Uint Handler)
933 {       
934         //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
935         Proc_GetCurThread()->FaultHandler = Handler;
936 }
937
938 /**
939  * \fn void Threads_Fault(int Num)
940  * \brief Calls a fault handler
941  */
942 void Threads_Fault(int Num)
943 {
944         tThread *thread = Proc_GetCurThread();
945         
946         if(!thread)     return ;
947         
948         Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
949         
950         switch(thread->FaultHandler)
951         {
952         case 0: // Panic?
953                 Threads_Kill(thread, -1);
954                 HALT();
955                 return ;
956         case 1: // Dump Core?
957                 Threads_Kill(thread, -1);
958                 HALT();
959                 return ;
960         }
961         
962         // Double Fault? Oh, F**k
963         if(thread->CurFaultNum != 0) {
964                 Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID);
965                 Threads_Kill(thread, -1);       // For now, just kill
966                 HALT();
967         }
968         
969         thread->CurFaultNum = Num;
970         
971         Proc_CallFaultHandler(thread);
972 }
973
974 /**
975  * \fn void Threads_SegFault(tVAddr Addr)
976  * \brief Called when a Segment Fault occurs
977  */
978 void Threads_SegFault(tVAddr Addr)
979 {
980         tThread *cur = Proc_GetCurThread();
981         cur->bInstrTrace = 0;
982         Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr);
983         MM_DumpTables(0, USER_MAX);
984         Threads_Fault( 1 );
985         //Threads_Exit( 0, -1 );
986 }
987
988 // --- Process Structure Access Functions ---
989 tPID Threads_GetPID(void)
990 {
991         return Proc_GetCurThread()->TGID;
992 }
993 tTID Threads_GetTID(void)
994 {
995         return Proc_GetCurThread()->TID;
996 }
997 tUID Threads_GetUID(void)
998 {
999         return Proc_GetCurThread()->UID;
1000 }
1001 tGID Threads_GetGID(void)
1002 {
1003         return Proc_GetCurThread()->GID;
1004 }
1005
1006 int Threads_SetUID(Uint *Errno, tUID ID)
1007 {
1008         tThread *t = Proc_GetCurThread();
1009         if( t->UID != 0 ) {
1010                 *Errno = -EACCES;
1011                 return -1;
1012         }
1013         Log_Debug("Threads", "TID %i's UID set to %i", t->TID, ID);
1014         t->UID = ID;
1015         return 0;
1016 }
1017
1018 int Threads_SetGID(Uint *Errno, tGID ID)
1019 {
1020         tThread *t = Proc_GetCurThread();
1021         if( t->UID != 0 ) {
1022                 *Errno = -EACCES;
1023                 return -1;
1024         }
1025         Log_Debug("Threads", "TID %i's GID set to %i", t->TID, ID);
1026         t->GID = ID;
1027         return 0;
1028 }
1029
1030 /**
1031  * \fn void Threads_Dump(void)
1032  */
1033 void Threads_DumpActive(void)
1034 {
1035         tThread *thread;
1036         #if SCHEDULER_TYPE == SCHED_RR_PRI
1037          int    i;
1038         #endif
1039         
1040         Log("Active Threads: (%i reported)", giNumActiveThreads);
1041         
1042         #if SCHEDULER_TYPE == SCHED_RR_PRI
1043         for( i = 0; i < MIN_PRIORITY+1; i++ )
1044         {
1045                 for(thread=gaActiveThreads[i];thread;thread=thread->Next)
1046         #else
1047                 for(thread=gActiveThreads;thread;thread=thread->Next)
1048         #endif
1049                 {
1050                         Log(" %p %i (%i) - %s (CPU %i)",
1051                                 thread, thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
1052                         if(thread->Status != THREAD_STAT_ACTIVE)
1053                                 Log("  ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
1054                         Log("  Priority %i, Quantum %i", thread->Priority, thread->Quantum);
1055                         Log("  KStack 0x%x", thread->KernelStack);
1056                         if( thread->bInstrTrace )
1057                                 Log("  Tracing Enabled");
1058                         Proc_DumpThreadCPUState(thread);
1059                 }
1060         
1061         #if SCHEDULER_TYPE == SCHED_RR_PRI
1062         }
1063         #endif
1064 }
1065
1066 /**
1067  * \fn void Threads_Dump(void)
1068  * \brief Dumps a list of currently running threads
1069  */
1070 void Threads_Dump(void)
1071 {
1072         tThread *thread;
1073         
1074         Log("--- Thread Dump ---");
1075         Threads_DumpActive();
1076         
1077         Log("All Threads:");
1078         for(thread=gAllThreads;thread;thread=thread->GlobalNext)
1079         {
1080                 Log(" %p %i (%i) - %s (CPU %i)",
1081                         thread, thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
1082                 Log("  State %i (%s)", thread->Status, casTHREAD_STAT[thread->Status]);
1083                 switch(thread->Status)
1084                 {
1085                 case THREAD_STAT_MUTEXSLEEP:
1086                         Log("  Mutex Pointer: %p", thread->WaitPointer);
1087                         break;
1088                 case THREAD_STAT_SEMAPHORESLEEP:
1089                         Log("  Semaphore Pointer: %p", thread->WaitPointer);
1090                         Log("  Semaphore Name: %s:%s", 
1091                                 ((tSemaphore*)thread->WaitPointer)->ModName,
1092                                 ((tSemaphore*)thread->WaitPointer)->Name
1093                                 );
1094                         break;
1095                 case THREAD_STAT_ZOMBIE:
1096                         Log("  Return Status: %i", thread->RetStatus);
1097                         break;
1098                 default:        break;
1099                 }
1100                 Log("  Priority %i, Quantum %i", thread->Priority, thread->Quantum);
1101                 Log("  KStack 0x%x", thread->KernelStack);
1102                 if( thread->bInstrTrace )
1103                         Log("  Tracing Enabled");
1104                 Proc_DumpThreadCPUState(thread);
1105         }
1106 }
1107
1108 /**
1109  * \brief Gets the next thread to run
1110  * \param CPU   Current CPU
1111  * \param Last  The thread the CPU was running
1112  */
1113 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
1114 {
1115         tThread *thread;
1116         
1117         // If this CPU has the lock, we must let it complete
1118         if( CPU_HAS_LOCK( &glThreadListLock ) )
1119                 return Last;
1120         
1121         // Don't change threads if the current CPU has switches disabled
1122         if( gaThreads_NoTaskSwitch[CPU] )
1123                 return Last;
1124
1125         // Lock thread list
1126         SHORTLOCK( &glThreadListLock );
1127         
1128         // Clear Delete Queue
1129         // - I should probably put this in a worker thread to avoid calling free() in the scheduler
1130         //   DEFINITELY - free() can deadlock in this case
1131         //   I'll do it when it becomes an issue
1132         while(gDeleteThreads)
1133         {
1134                 thread = gDeleteThreads->Next;
1135                 // Only free if structure is unused
1136                 if( !IS_LOCKED(&gDeleteThreads->IsLocked) )
1137                 {
1138                         // Set to dead
1139                         gDeleteThreads->Status = THREAD_STAT_BURIED;
1140                         // Free name
1141                         if( IsHeap(gDeleteThreads->ThreadName) )
1142                                 free(gDeleteThreads->ThreadName);
1143                         // Remove from global list
1144                         if( gDeleteThreads == gAllThreads )
1145                                 gAllThreads = gDeleteThreads->GlobalNext;
1146                         else
1147                                 gDeleteThreads->GlobalPrev->GlobalNext = gDeleteThreads->GlobalNext;
1148                         free( gDeleteThreads );
1149                 }
1150                 gDeleteThreads = thread;
1151         }
1152
1153         // Make sure the current (well, old) thread is marked as de-scheduled   
1154         if(Last)        Last->CurCPU = -1;
1155
1156         // No active threads, just take a nap
1157         if(giNumActiveThreads == 0) {
1158                 SHORTREL( &glThreadListLock );
1159                 #if DEBUG_TRACE_TICKETS
1160                 Log("No active threads");
1161                 #endif
1162                 return NULL;
1163         }
1164         
1165         #if SCHEDULER_TYPE != SCHED_RR_PRI
1166         // Special case: 1 thread
1167         if(giNumActiveThreads == 1) {
1168                 if( gActiveThreads->CurCPU == -1 )
1169                         gActiveThreads->CurCPU = CPU;
1170                 
1171                 SHORTREL( &glThreadListLock );
1172                 
1173                 if( gActiveThreads->CurCPU == CPU )
1174                         return gActiveThreads;
1175                 
1176                 return NULL;    // CPU has nothing to do
1177         }
1178         #endif
1179         
1180         // Allow the old thread to be scheduled again
1181         if( Last ) {
1182                 if( Last->Status == THREAD_STAT_ACTIVE ) {
1183                         #if SCHEDULER_TYPE == SCHED_LOTTERY
1184                         giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
1185                         # if DEBUG_TRACE_TICKETS
1186                         LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n",
1187                                 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets,
1188                                 caiTICKET_COUNTS[ Last->Priority ]);
1189                         # endif
1190                         #endif
1191                 }
1192                 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
1193                 else
1194                         LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n",
1195                                 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
1196                 #endif
1197                 Last->CurCPU = -1;
1198         }
1199         
1200         // ---
1201         // Lottery Scheduler
1202         // ---
1203         #if SCHEDULER_TYPE == SCHED_LOTTERY
1204         {
1205                  int    ticket, number;
1206                 # if 1
1207                 number = 0;
1208                 for(thread = gActiveThreads; thread; thread = thread->Next) {
1209                         if(thread->CurCPU >= 0) continue;
1210                         if(thread->Status != THREAD_STAT_ACTIVE)
1211                                 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
1212                                         thread, thread->TID, thread->ThreadName, thread->Status);
1213                         if(thread->Next == thread) {
1214                                 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
1215                                         thread, thread->TID, thread->ThreadName, thread->Status);
1216                         }
1217                         number += caiTICKET_COUNTS[ thread->Priority ];
1218                 }
1219                 if(number != giFreeTickets) {
1220                         Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
1221                                 giFreeTickets, number, CPU);
1222                 }
1223                 # endif
1224                 
1225                 // No free tickets (all tasks delegated to cores)
1226                 if( giFreeTickets == 0 ) {
1227                         SHORTREL(&glThreadListLock);
1228                         return NULL;
1229                 }
1230                 
1231                 // Get the ticket number
1232                 ticket = number = rand() % giFreeTickets;
1233                 
1234                 // Find the next thread
1235                 for(thread=gActiveThreads;thread;thread=thread->Next)
1236                 {
1237                         if(thread->CurCPU >= 0) continue;
1238                         if( caiTICKET_COUNTS[ thread->Priority ] > number)      break;
1239                         number -= caiTICKET_COUNTS[ thread->Priority ];
1240                 }
1241                 
1242                 // If we didn't find a thread, something went wrong
1243                 if(thread == NULL)
1244                 {
1245                         number = 0;
1246                         for(thread=gActiveThreads;thread;thread=thread->Next) {
1247                                 if(thread->CurCPU >= 0) continue;
1248                                 number += caiTICKET_COUNTS[ thread->Priority ];
1249                         }
1250                         Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
1251                                 giFreeTickets, number);
1252                 }
1253                 
1254                 giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
1255                 # if DEBUG_TRACE_TICKETS
1256                 LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n",
1257                         CPU, thread, thread->TID, thread->ThreadName,
1258                         giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]);
1259                 # endif
1260         }
1261         
1262         // ---
1263         // Priority based round robin scheduler
1264         // ---
1265         #elif SCHEDULER_TYPE == SCHED_RR_PRI
1266         {
1267                  int    i;
1268                 for( i = 0; i < MIN_PRIORITY + 1; i ++ )
1269                 {
1270                         for(thread = gaActiveThreads[i]; thread; thread = thread->Next)
1271                         {
1272                                 if( thread->CurCPU == -1 )      break;
1273                         }
1274                         // If we fall onto the same queue again, special handling is
1275                         // needed
1276                         if( Last && Last->Status == THREAD_STAT_ACTIVE && i == Last->Priority ) {
1277                                 tThread *savedThread = thread;
1278                                 
1279                                 // Find the next unscheduled thread in the list
1280                                 for( thread = Last->Next; thread; thread = thread->Next )
1281                                 {
1282                                         if( thread->CurCPU == -1 )      break;
1283                                 }
1284                                 // If we don't find anything after, just use the one 
1285                                 // found above.
1286                                 if( !thread )   thread = savedThread;
1287                         }
1288                         // Found a thread? Schedule it!
1289                         if( thread )    break;
1290                 }
1291                 
1292                 // Anything to do?
1293                 if( !thread ) {
1294                         SHORTREL(&glThreadListLock);
1295                         return NULL;
1296                 }
1297                 if( thread->Status != THREAD_STAT_ACTIVE ) {
1298                         LogF("Oops, Thread %i (%s) is not active\n", thread->TID, thread->ThreadName);
1299                 }
1300         }
1301         #elif SCHEDULER_TYPE == SCHED_RR_SIM
1302         {               
1303                 // Find the next unscheduled thread in the list
1304                 for( thread = Last->Next; thread; thread = thread->Next )
1305                 {
1306                         if( thread->CurCPU == -1 )      break;
1307                 }
1308                 // If we don't find anything after, search from the beginning
1309                 if( !thread )
1310                 {
1311                         for(thread = gActiveThreads; thread; thread = thread->Next)
1312                         {
1313                                 if( thread->CurCPU == -1 )      break;
1314                         }       
1315                 }
1316                 
1317                 // Anything to do?
1318                 if( !thread ) {
1319                         SHORTREL(&glThreadListLock);
1320                         return NULL;
1321                 }
1322         }
1323         #else
1324         # error "Unimplemented scheduling algorithm"
1325         #endif
1326         
1327         // Make the new thread non-schedulable
1328         thread->CurCPU = CPU;
1329         thread->Remaining = thread->Quantum;
1330         
1331         SHORTREL( &glThreadListLock );
1332         
1333         return thread;
1334 }
1335
1336 // === EXPORTS ===
1337 EXPORT(Threads_GetUID);
1338 EXPORT(Threads_GetGID);

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