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

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