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

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