3b731118b2fbff9c9ea265da6c38d87dfef171c6
[tpg/acess2.git] / Kernel / threads.c
1 /*
2  * Acess2
3  * threads.c
4  * - Common Thread Control
5  */
6 #include <acess.h>
7 #include <threads.h>
8 #include <errno.h>
9
10 #define DEBUG_TRACE_TICKETS     0       // Trace ticket counts
11 #define DEBUG_TRACE_STATE       0       // Trace state changes (sleep/wake)
12
13 // === CONSTANTS ===
14 #define DEFAULT_QUANTUM 10
15 #define DEFAULT_TICKETS 5
16 #define MAX_TICKETS             10
17 const enum eConfigTypes cCONFIG_TYPES[] = {
18         CFGT_HEAPSTR,   // e.g. CFG_VFS_CWD
19         CFGT_INT,       // e.g. CFG_VFS_MAXFILES
20         CFGT_NULL
21 };
22
23 // === IMPORTS ===
24 extern void     ArchThreads_Init(void);
25 extern void     Proc_Start(void);
26 extern tThread  *Proc_GetCurThread(void);
27 extern int      Proc_Clone(Uint *Err, Uint Flags);
28 extern void     Proc_CallFaultHandler(tThread *Thread);
29
30 // === PROTOTYPES ===
31 void    Threads_Init(void);
32  int    Threads_SetName(char *NewName);
33 char    *Threads_GetName(int ID);
34 void    Threads_SetTickets(tThread *Thread, int Num);
35 tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
36  int    Threads_WaitTID(int TID, int *status);
37 tThread *Threads_GetThread(Uint TID);
38 void    Threads_AddToDelete(tThread *Thread);
39 tThread *Threads_int_DelFromQueue(tThread **List, tThread *Thread);
40 void    Threads_Exit(int TID, int Status);
41 void    Threads_Kill(tThread *Thread, int Status);
42 void    Threads_Yield(void);
43 void    Threads_Sleep(void);
44  int    Threads_Wake(tThread *Thread);
45 void    Threads_AddActive(tThread *Thread);
46 tThread *Threads_RemActive(void);
47  int    Threads_GetPID(void);
48  int    Threads_GetTID(void);
49 tUID    Threads_GetUID(void);
50  int    Threads_SetUID(Uint *Errno, tUID ID);
51 tGID    Threads_GetGID(void);
52  int    Threads_SetGID(Uint *Errno, tUID ID);
53 void    Threads_Dump(void);
54 void    Threads_DumpActive(void);
55 void    Mutex_Acquire(tMutex *Mutex);
56 void    Mutex_Release(tMutex *Mutex);
57  int    Mutex_IsLocked(tMutex *Mutex);
58
59 // === GLOBALS ===
60 // -- Core Thread --
61 // Only used for the core kernel
62 tThread gThreadZero = {
63         Status: THREAD_STAT_ACTIVE,     // Status
64         ThreadName:     "ThreadZero",   // Name
65         Quantum: DEFAULT_QUANTUM,       // Default Quantum
66         Remaining:      DEFAULT_QUANTUM,        // Current Quantum
67         NumTickets:     DEFAULT_TICKETS // Number of tickets
68         };
69 // -- Processes --
70 // --- Locks ---
71 tShortSpinlock  glThreadListLock;       ///\note NEVER use a heap function while locked
72 // --- Current State ---
73 volatile int    giNumActiveThreads = 0; // Number of threads on the active queue
74 volatile int    giFreeTickets = 0;      // Number of tickets held by non-scheduled threads
75 volatile Uint   giNextTID = 1;  // Next TID to allocate
76 // --- Thread Lists ---
77 tThread *gAllThreads = NULL;            // All allocated threads
78 tThread *gActiveThreads = NULL;         // Currently Running Threads
79 tThread *gSleepingThreads = NULL;       // Sleeping Threads
80 tThread *gDeleteThreads = NULL;         // Threads to delete
81  int    giNumCPUs = 1;  // Number of CPUs
82 BOOL     gaThreads_NoTaskSwitch[MAX_CPUS];      // Disables task switches for each core (Pseudo-IF)
83
84 // === CODE ===
85 /**
86  * \fn void Threads_Init(void)
87  * \brief Initialse the thread list
88  */
89 void Threads_Init(void)
90 {
91         ArchThreads_Init();
92         
93         // Create Initial Task
94         gActiveThreads = &gThreadZero;
95         gAllThreads = &gThreadZero;
96         //giFreeTickets = gThreadZero.NumTickets;       // Not needed, as ThreadZero is scheduled
97         giNumActiveThreads = 1;
98                 
99         Proc_Start();
100 }
101
102 /**
103  * \fn void Threads_SetName(char *NewName)
104  * \brief Sets the current thread's name
105  * \param NewName       New name for the thread
106  * \return Boolean Failure
107  */
108 int Threads_SetName(char *NewName)
109 {
110         tThread *cur = Proc_GetCurThread();
111         char    *oldname = cur->ThreadName;
112         
113         // NOTE: There is a possibility of non-thread safety here
114         // A thread could read the current name pointer before it is zeroed
115         
116         cur->ThreadName = NULL;
117         
118         if( IsHeap(oldname) )   free( oldname );
119         
120         cur->ThreadName = strdup(NewName);
121         return 0;
122 }
123
124 /**
125  * \fn char *Threads_GetName(int ID)
126  * \brief Gets a thread's name
127  * \param ID    Thread ID (-1 indicates current thread)
128  * \return Pointer to name
129  * \retval NULL Failure
130  */
131 char *Threads_GetName(tTID ID)
132 {
133         if(ID == -1) {
134                 return Proc_GetCurThread()->ThreadName;
135         }
136         return Threads_GetThread(ID)->ThreadName;
137 }
138
139 /**
140  * \fn void Threads_SetTickets(tThread *Thread, int Num)
141  * \brief Sets the 'priority' of a task
142  * \param Thread        Thread to update ticket count (NULL means current thread)
143  * \param Num   New ticket count (must be >= 0, clipped to \a MAX_TICKETS)
144  */
145 void Threads_SetTickets(tThread *Thread, int Num)
146 {
147         // Get current thread
148         if(Thread == NULL)      Thread = Proc_GetCurThread();
149         // Bounds checking
150         if(Num < 0)     return;
151         if(Num > MAX_TICKETS)   Num = MAX_TICKETS;
152         
153         // If this isn't the current thread, we need to lock
154         if( Thread != Proc_GetCurThread() ) {
155                 SHORTLOCK( &glThreadListLock );
156                 giFreeTickets -= Thread->NumTickets - Num;
157                 Thread->NumTickets = Num;
158                 #if DEBUG_TRACE_TICKETS
159                 Log("Threads_SetTickets: new giFreeTickets = %i", giFreeTickets);
160                 #endif
161                 SHORTREL( &glThreadListLock );
162         }
163         else
164                 Thread->NumTickets = Num;
165 }
166
167 /**
168  * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
169  * \brief Clone the TCB of the current thread
170  * \param Err   Error pointer
171  * \param Flags Flags for something... (What is this for?)
172  */
173 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
174 {
175         tThread *cur, *new;
176          int    i;
177         cur = Proc_GetCurThread();
178         
179         // Allocate and duplicate
180         new = malloc(sizeof(tThread));
181         if(new == NULL) {
182                 *Err = -ENOMEM;
183                 return NULL;
184         }
185         memcpy(new, cur, sizeof(tThread));
186         
187         new->CurCPU = -1;
188         new->Next = NULL;
189         memset( &new->IsLocked, 0, sizeof(new->IsLocked));
190         new->Status = THREAD_STAT_PREINIT;
191         new->RetStatus = 0;
192         
193         // Get Thread ID
194         new->TID = giNextTID++;
195         new->Parent = cur;
196         
197         // Clone Name
198         new->ThreadName = strdup(cur->ThreadName);
199         
200         // Set Thread Group ID (PID)
201         if(Flags & CLONE_VM)
202                 new->TGID = new->TID;
203         else
204                 new->TGID = cur->TGID;
205         
206         // Messages are not inherited
207         new->Messages = NULL;
208         new->LastMessage = NULL;
209         
210         // Set State
211         new->Remaining = new->Quantum = cur->Quantum;
212         new->NumTickets = cur->NumTickets;
213         
214         // Set Signal Handlers
215         new->CurFaultNum = 0;
216         new->FaultHandler = cur->FaultHandler;
217         
218         for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
219         {
220                 switch(cCONFIG_TYPES[i])
221                 {
222                 default:
223                         new->Config[i] = cur->Config[i];
224                         break;
225                 case CFGT_HEAPSTR:
226                         if(cur->Config[i])
227                                 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
228                         else
229                                 new->Config[i] = 0;
230                         break;
231                 }
232         }
233         
234         // Maintain a global list of threads
235         SHORTLOCK( &glThreadListLock );
236         new->GlobalPrev = NULL; // Protect against bugs
237         new->GlobalNext = gAllThreads;
238         gAllThreads = new;
239         SHORTREL( &glThreadListLock );
240         
241         return new;
242 }
243
244 /**
245  * \brief Get a configuration pointer from the Per-Thread data area
246  * \param ID    Config slot ID
247  * \return Pointer at ID
248  */
249 Uint *Threads_GetCfgPtr(int ID)
250 {
251         if(ID < 0 || ID >= NUM_CFG_ENTRIES) {
252                 Warning("Threads_GetCfgPtr: Index %i is out of bounds", ID);
253                 return NULL;
254         }
255         
256         return &Proc_GetCurThread()->Config[ID];
257 }
258
259 /**
260  * \brief Wait for a task to change state
261  * \param TID   Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
262  * \param Status        Thread return status
263  * \return TID of child that changed state
264  */
265 tTID Threads_WaitTID(int TID, int *Status)
266 {       
267         // Any Child
268         if(TID == -1) {
269                 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
270                 return -1;
271         }
272         
273         // Any peer/child thread
274         if(TID == 0) {
275                 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
276                 return -1;
277         }
278         
279         // TGID = abs(TID)
280         if(TID < -1) {
281                 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
282                 return -1;
283         }
284         
285         // Specific Thread
286         if(TID > 0) {
287                 tThread *t = Threads_GetThread(TID);
288                  int    initStatus = t->Status;
289                 tTID    ret;
290                 
291                 // Wait for the thread to die!
292                 if(initStatus != THREAD_STAT_ZOMBIE) {
293                         // TODO: Handle child also being suspended if wanted
294                         while(t->Status != THREAD_STAT_ZOMBIE) {
295                                 Threads_Sleep();
296                                 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
297                                         Threads_GetTID(), t->TID, t->Status);
298                         }
299                 }
300                 
301                 // Set return status
302                 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
303                         Threads_GetTID(), t->TID, t->Status);
304                 ret = t->TID;
305                 switch(t->Status)
306                 {
307                 case THREAD_STAT_ZOMBIE:
308                         // Kill the thread
309                         t->Status = THREAD_STAT_DEAD;
310                         // TODO: Child return value?
311                         if(Status)      *Status = t->RetStatus;
312                         // add to delete queue
313                         Threads_AddToDelete( t );
314                         break;
315                 default:
316                         if(Status)      *Status = -1;
317                         break;
318                 }
319                 return ret;
320         }
321         
322         return -1;
323 }
324
325 /**
326  * \brief Gets a thread given its TID
327  * \param TID   Thread ID
328  * \return Thread pointer
329  */
330 tThread *Threads_GetThread(Uint TID)
331 {
332         tThread *thread;
333         
334         // Search global list
335         for(thread = gAllThreads;
336                 thread;
337                 thread = thread->GlobalNext)
338         {
339                 if(thread->TID == TID)
340                         return thread;
341         }
342
343         Log("Unable to find TID %i on main list\n", TID);
344         
345         return NULL;
346 }
347
348 /**
349  * \brief Adds a thread to the delete queue
350  * \param Thread        Thread to delete
351  */
352 void Threads_AddToDelete(tThread *Thread)
353 {
354         // Add to delete queue
355         // TODO: Is locking needed?
356         if(gDeleteThreads) {
357                 Thread->Next = gDeleteThreads;
358                 gDeleteThreads = Thread;
359         } else {
360                 Thread->Next = NULL;
361                 gDeleteThreads = Thread;
362         }
363 }
364
365 /**
366  * \brief Deletes an entry from a list
367  * \param List  Pointer to the list head
368  * \param Thread        Thread to find
369  * \return \a Thread
370  */
371 tThread *Threads_int_DelFromQueue(tThread **List, tThread *Thread)
372 {
373         tThread *ret, *prev = NULL;
374         
375         for(ret = *List;
376                 ret && ret != Thread;
377                 prev = ret, ret = ret->Next
378                 );
379         
380         // Is the thread on the list
381         if(!ret) {
382                 //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
383                 return NULL;
384         }
385         
386         if( !prev ) {
387                 *List = Thread->Next;
388                 //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
389         }
390         else {
391                 prev->Next = Thread->Next;
392                 //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
393         }
394         
395         return Thread;
396 }
397
398 /**
399  * \brief Exit the current process (or another?)
400  * \param TID   Thread ID to kill
401  * \param Status        Exit status
402  */
403 void Threads_Exit(int TID, int Status)
404 {
405         if( TID == 0 )
406                 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
407         else
408                 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
409         
410         // Halt forever, just in case
411         for(;;) HALT();
412 }
413
414 /**
415  * \fn void Threads_Kill(tThread *Thread, int Status)
416  * \brief Kill a thread
417  * \param Thread        Thread to kill
418  * \param Status        Status code to return to the parent
419  */
420 void Threads_Kill(tThread *Thread, int Status)
421 {
422         tMsg    *msg;
423         
424         // TODO: Kill all children
425         #if 0
426         {
427                 tThread *child;
428                 // TODO: I should keep a .Parent pointer, and a .Children list
429                 for(child = gActiveThreads;
430                         child;
431                         child = child->Next)
432                 {
433                         if(child->PTID == Thread->TID)
434                                 Threads_Kill(child, -1);
435                 }
436         }
437         #endif
438         
439         ///\note Double lock is needed due to overlap of lock areas
440         
441         // Lock thread (stop us recieving messages)
442         SHORTLOCK( &Thread->IsLocked );
443         
444         // Clear Message Queue
445         while( Thread->Messages )
446         {
447                 msg = Thread->Messages->Next;
448                 free( Thread->Messages );
449                 Thread->Messages = msg;
450         }
451         
452         // Lock thread list
453         SHORTLOCK( &glThreadListLock );
454         
455         // Delete from active list
456         if( !Threads_int_DelFromQueue( &gActiveThreads, Thread ) )
457         {
458                 Warning("Proc_Exit - Current thread is not on the active queue");
459                 SHORTREL( &glThreadListLock );
460                 SHORTREL( &Thread->IsLocked );
461                 return;
462         }
463         
464         // Ensure that we are not rescheduled
465         Thread->Remaining = 0;  // Clear Remaining Quantum
466         Thread->Quantum = 0;    // Clear Quantum to indicate dead thread
467         
468         // Update bookkeeping
469         giNumActiveThreads --;
470         if( Thread != Proc_GetCurThread() )
471                 giFreeTickets -= Thread->NumTickets;
472         
473         // Save exit status
474         Thread->RetStatus = Status;
475         
476         // Don't Zombie if we are being killed because our parent is
477         if(Status == -1)
478         {
479                 Thread->Status = THREAD_STAT_DEAD;
480                 Threads_AddToDelete( Thread );
481         } else {
482                 Thread->Status = THREAD_STAT_ZOMBIE;
483                 // Wake parent
484                 Threads_Wake( Thread->Parent );
485         }
486         
487         Log("Thread %i went *hurk* (%i)", Thread->TID, Thread->Status);
488         
489         // Release spinlocks
490         SHORTREL( &glThreadListLock );
491         SHORTREL( &Thread->IsLocked );  // TODO: We may not actually be released...
492         
493         // And, reschedule
494         if(Status != -1) {
495                 for( ;; )
496                         HALT();
497         }
498 }
499
500 /**
501  * \brief Yield remainder of the current thread's timeslice
502  */
503 void Threads_Yield(void)
504 {
505         tThread *thread = Proc_GetCurThread();
506         thread->Remaining = 0;
507         //while(thread->Remaining == 0)
508                 HALT();
509 }
510
511 /**
512  * \fn void Threads_Sleep(void)
513  * \brief Take the current process off the run queue
514  */
515 void Threads_Sleep(void)
516 {
517         tThread *cur = Proc_GetCurThread();
518         
519         // Acquire Spinlock
520         SHORTLOCK( &glThreadListLock );
521         
522         // Don't sleep if there is a message waiting
523         if( cur->Messages ) {
524                 SHORTREL( &glThreadListLock );
525                 return;
526         }
527         
528         // Remove us from running queue
529         Threads_RemActive();
530         // Mark thread as sleeping
531         cur->Status = THREAD_STAT_SLEEPING;
532         
533         // Add to Sleeping List (at the top)
534         cur->Next = gSleepingThreads;
535         gSleepingThreads = cur;
536         
537         
538         #if DEBUG_TRACE_STATE
539         Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
540         #endif
541         
542         // Release Spinlock
543         SHORTREL( &glThreadListLock );
544         
545         while(cur->Status != THREAD_STAT_ACTIVE)        HALT();
546 }
547
548
549 /**
550  * \fn int Threads_Wake( tThread *Thread )
551  * \brief Wakes a sleeping/waiting thread up
552  * \param Thread        Thread to wake
553  * \return Boolean Failure (Returns ERRNO)
554  * \warning This should ONLY be called with task switches disabled
555  */
556 int Threads_Wake(tThread *Thread)
557 {
558         if(!Thread)
559                 return -EINVAL;
560         
561         switch(Thread->Status)
562         {
563         case THREAD_STAT_ACTIVE:
564                 Log("Thread_Wake: Waking awake thread (%i)", Thread->TID);
565                 return -EALREADY;
566         
567         case THREAD_STAT_SLEEPING:
568                 SHORTLOCK( &glThreadListLock );
569                 // Remove from sleeping queue
570                 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
571                 
572                 Threads_AddActive( Thread );
573                 
574                 #if DEBUG_TRACE_STATE
575                 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
576                 #endif
577                 SHORTREL( &glThreadListLock );
578                 return -EOK;
579         
580         case THREAD_STAT_WAITING:
581                 Warning("Thread_Wake - Waiting threads are not currently supported");
582                 return -ENOTIMPL;
583         
584         case THREAD_STAT_DEAD:
585                 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
586                 return -ENOTIMPL;
587         
588         default:
589                 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
590                 return -EINTERNAL;
591         }
592 }
593
594 /**
595  * \brief Wake a thread given the TID
596  * \param TID   Thread ID to wake
597  * \return Boolean Faulure (errno)
598  */
599 int Threads_WakeTID(tTID TID)
600 {
601         tThread *thread = Threads_GetThread(TID);
602          int    ret;
603         if(!thread)
604                 return -ENOENT;
605         ret = Threads_Wake( thread );
606         //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
607         return ret;
608 }
609
610 /**
611  * \brief Adds a thread to the active queue
612  */
613 void Threads_AddActive(tThread *Thread)
614 {
615         SHORTLOCK( &glThreadListLock );
616         
617         #if 1
618         {
619                 tThread *t;
620                 for( t = gActiveThreads; t; t = t->Next )
621                 {
622                         if( t == Thread ) {
623                                 Panic("Threads_AddActive: Attempting a double add of TID %i (0x%x)",
624                                         Thread->TID, __builtin_return_address(0));
625                         }
626                         
627                         if(t->Status != THREAD_STAT_ACTIVE) {
628                                 Panic("Threads_AddActive: TID %i status != THREAD_STAT_ACTIVE",
629                                         Thread->TID);
630                         }
631                 }
632         }
633         #endif
634         
635         // Set state
636         Thread->Status = THREAD_STAT_ACTIVE;
637         Thread->CurCPU = -1;
638         // Add to active list
639         Thread->Next = gActiveThreads;
640         gActiveThreads = Thread;
641         
642         // Update bookkeeping
643         giNumActiveThreads ++;
644         giFreeTickets += Thread->NumTickets;
645         
646         #if DEBUG_TRACE_TICKETS
647         Log("Threads_AddActive: %p %i (%s) added, new giFreeTickets = %i",
648                 Thread, Thread->TID, Thread->ThreadName, giFreeTickets);
649         #endif
650         SHORTREL( &glThreadListLock );
651 }
652
653 /**
654  * \brief Removes the current thread from the active queue
655  * \warning This should ONLY be called with task switches disabled
656  * \return Current thread pointer
657  */
658 tThread *Threads_RemActive(void)
659 {
660         tThread *ret = Proc_GetCurThread();
661         
662         SHORTLOCK( &glThreadListLock );
663         
664         // Delete from active queue
665         if( !Threads_int_DelFromQueue(&gActiveThreads, ret) ) {
666                 SHORTREL( &glThreadListLock );
667                 return NULL;
668         }
669         
670         ret->Remaining = 0;
671         ret->CurCPU = -1;
672         
673         giNumActiveThreads --;
674         // no need to decrement tickets, scheduler did it for us
675         
676         #if DEBUG_TRACE_TICKETS
677         Log("Threads_RemActive: %p %i (%s) removed, giFreeTickets = %i",
678                 ret, ret->TID, ret->ThreadName, giFreeTickets);
679         #endif
680         
681         SHORTREL( &glThreadListLock );
682         
683         return ret;
684 }
685
686 /**
687  * \fn void Threads_SetFaultHandler(Uint Handler)
688  * \brief Sets the signal handler for a signal
689  */
690 void Threads_SetFaultHandler(Uint Handler)
691 {       
692         //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
693         Proc_GetCurThread()->FaultHandler = Handler;
694 }
695
696 /**
697  * \fn void Threads_Fault(int Num)
698  * \brief Calls a fault handler
699  */
700 void Threads_Fault(int Num)
701 {
702         tThread *thread = Proc_GetCurThread();
703         
704         Log_Log("Threads", "Threads_Fault: thread = %p", thread);
705         
706         if(!thread)     return ;
707         
708         Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
709         
710         switch(thread->FaultHandler)
711         {
712         case 0: // Panic?
713                 Threads_Kill(thread, -1);
714                 HALT();
715                 return ;
716         case 1: // Dump Core?
717                 Threads_Kill(thread, -1);
718                 HALT();
719                 return ;
720         }
721         
722         // Double Fault? Oh, F**k
723         if(thread->CurFaultNum != 0) {
724                 Threads_Kill(thread, -1);       // For now, just kill
725                 HALT();
726         }
727         
728         thread->CurFaultNum = Num;
729         
730         Proc_CallFaultHandler(thread);
731 }
732
733 // --- Process Structure Access Functions ---
734 tPID Threads_GetPID(void)
735 {
736         return Proc_GetCurThread()->TGID;
737 }
738 tTID Threads_GetTID(void)
739 {
740         return Proc_GetCurThread()->TID;
741 }
742 tUID Threads_GetUID(void)
743 {
744         return Proc_GetCurThread()->UID;
745 }
746 tGID Threads_GetGID(void)
747 {
748         return Proc_GetCurThread()->GID;
749 }
750
751 int Threads_SetUID(Uint *Errno, tUID ID)
752 {
753         tThread *t = Proc_GetCurThread();
754         if( t->UID != 0 ) {
755                 *Errno = -EACCES;
756                 return -1;
757         }
758         Log_Debug("Threads", "TID %i's UID set to %i", t->TID, ID);
759         t->UID = ID;
760         return 0;
761 }
762
763 int Threads_SetGID(Uint *Errno, tGID ID)
764 {
765         tThread *t = Proc_GetCurThread();
766         if( t->UID != 0 ) {
767                 *Errno = -EACCES;
768                 return -1;
769         }
770         Log_Debug("Threads", "TID %i's GID set to %i", t->TID, ID);
771         t->GID = ID;
772         return 0;
773 }
774
775 /**
776  * \fn void Threads_Dump(void)
777  * \brief Dumps a list of currently running threads
778  */
779 void Threads_Dump(void)
780 {
781         tThread *thread;
782         
783         Log("--- Thread Dump ---");
784         Log("Active Threads: (%i reported)", giNumActiveThreads);
785         for(thread=gActiveThreads;thread;thread=thread->Next)
786         {
787                 Log(" %i (%i) - %s (CPU %i)",
788                         thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
789                 if(thread->Status != THREAD_STAT_ACTIVE)
790                         Log("  ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
791                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
792                 Log("  KStack 0x%x", thread->KernelStack);
793         }
794         
795         Log("All Threads:");
796         for(thread=gAllThreads;thread;thread=thread->GlobalNext)
797         {
798                 Log(" %i (%i) - %s (CPU %i)",
799                         thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
800                 Log("  State %i", thread->Status);
801                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
802                 Log("  KStack 0x%x", thread->KernelStack);
803         }
804 }
805 /**
806  * \fn void Threads_Dump(void)
807  */
808 void Threads_DumpActive(void)
809 {
810         tThread *thread;
811         
812         Log("Active Threads:");
813         for(thread=gActiveThreads;thread;thread=thread->Next)
814         {
815                 Log(" %i (%i) - %s (CPU %i)",
816                         thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
817                 if(thread->Status != THREAD_STAT_ACTIVE)
818                         Log("  ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
819                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
820                 Log("  KStack 0x%x", thread->KernelStack);
821         }
822 }
823
824 /**
825  * \brief Gets the next thread to run
826  * \param CPU   Current CPU
827  * \param Last  The thread the CPU was running
828  */
829 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
830 {
831         tThread *thread;
832          int    ticket;
833          int    number;
834         
835         // If this CPU has the lock, we must let it complete
836         if( CPU_HAS_LOCK( &glThreadListLock ) )
837                 return Last;
838         
839         // Don't change threads if the current CPU has switches disabled
840         if( gaThreads_NoTaskSwitch[CPU] )
841                 return Last;
842
843
844         // Lock thread list
845         SHORTLOCK( &glThreadListLock );
846         
847         // Clear Delete Queue
848         // - I should probably put this in a worker thread to avoid calling free() in the scheduler
849         while(gDeleteThreads)
850         {
851                 thread = gDeleteThreads->Next;
852                 if( IS_LOCKED(&gDeleteThreads->IsLocked) ) {    // Only free if structure is unused
853                         // Set to dead
854                         gDeleteThreads->Status = THREAD_STAT_DEAD;
855                         // Free name
856                         if( IsHeap(gDeleteThreads->ThreadName) )
857                                 free(gDeleteThreads->ThreadName);
858                         // Remove from global list
859                         if( gDeleteThreads == gAllThreads )
860                                 gAllThreads = gDeleteThreads->GlobalNext;
861                         else
862                                 gDeleteThreads->GlobalPrev->GlobalNext = gDeleteThreads->GlobalNext;
863                         free( gDeleteThreads );
864                 }
865                 gDeleteThreads = thread;
866         }
867         
868         // No active threads, just take a nap
869         if(giNumActiveThreads == 0) {
870                 SHORTREL( &glThreadListLock );
871                 #if DEBUG_TRACE_TICKETS
872                 Log("No active threads");
873                 #endif
874                 return NULL;
875         }
876         
877         // Special case: 1 thread
878         if(giNumActiveThreads == 1) {
879                 if( gActiveThreads->CurCPU == -1 )
880                         gActiveThreads->CurCPU = CPU;
881                 
882                 SHORTREL( &glThreadListLock );
883                 
884                 if( gActiveThreads->CurCPU == CPU )
885                         return gActiveThreads;
886                 
887                 return NULL;    // CPU has nothing to do
888         }
889         
890         // Allow the old thread to be scheduled again
891         if( Last ) {
892                 if( Last->Status == THREAD_STAT_ACTIVE ) {
893                         giFreeTickets += Last->NumTickets;
894                         #if DEBUG_TRACE_TICKETS
895                         LogF(" CPU %i released %p (%i %s) into the pool (%i tickets in pool)\n",
896                                 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets);
897                         #endif
898                 }
899                 #if DEBUG_TRACE_TICKETS
900                 else
901                         LogF(" CPU %i released %p (%i %s)->Status = %i (Released)\n",
902                                 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
903                 #endif
904                 Last->CurCPU = -1;
905         }
906         
907         #if DEBUG_TRACE_TICKETS
908         //Threads_DumpActive();
909         #endif
910         
911         #if 1
912         number = 0;
913         for(thread = gActiveThreads; thread; thread = thread->Next) {
914                 if(thread->CurCPU >= 0) continue;
915                 if(thread->Status != THREAD_STAT_ACTIVE)
916                         Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
917                                 thread, thread->TID, thread->ThreadName, thread->Status);
918                 if(thread->Next == thread) {
919                         Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
920                                 thread, thread->TID, thread->ThreadName, thread->Status);
921                 }
922                 number += thread->NumTickets;
923         }
924         if(number != giFreeTickets) {
925                 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
926                         giFreeTickets, number, CPU);
927         }
928         #endif
929         
930         // No free tickets (all tasks delegated to cores)
931         if( giFreeTickets == 0 ) {
932                 SHORTREL(&glThreadListLock);
933                 return NULL;
934         }
935         
936         // Get the ticket number
937         ticket = number = rand() % giFreeTickets;
938         
939         // Find the next thread
940         for(thread=gActiveThreads;thread;thread=thread->Next)
941         {
942                 if(thread->CurCPU >= 0) continue;
943                 if(thread->NumTickets > number) break;
944                 number -= thread->NumTickets;
945         }
946         // Error Check
947         if(thread == NULL)
948         {
949                 number = 0;
950                 for(thread=gActiveThreads;thread;thread=thread->Next) {
951                         if(thread->CurCPU >= 0) continue;
952                         number += thread->NumTickets;
953                 }
954                 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
955                         giFreeTickets, number);
956         }
957         #if DEBUG_TRACE_TICKETS
958         LogF(" CPU%i giFreeTickets = %i\n", CPU, giFreeTickets);
959         #endif
960         
961         // Make the new thread non-schedulable
962         giFreeTickets -= thread->NumTickets;    
963         thread->CurCPU = CPU;
964         
965         //Threads_Dump();
966         #if DEBUG_TRACE_TICKETS
967         LogF(" CPU%i giFreeTickets = %i, giving %p (%i %s CPU=%i)\n",
968                 CPU, giFreeTickets, thread, thread->TID, thread->ThreadName, thread->CurCPU);
969         #endif
970         
971         SHORTREL( &glThreadListLock );
972         
973         return thread;
974 }
975
976 /**
977  * \fn void Threads_SegFault(tVAddr Addr)
978  * \brief Called when a Segment Fault occurs
979  */
980 void Threads_SegFault(tVAddr Addr)
981 {
982         Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
983         Threads_Fault( 1 );
984         //Threads_Exit( 0, -1 );
985 }
986
987 /**
988  * \brief Acquire a heavy mutex
989  * \param Mutex Mutex to acquire
990  * 
991  * This type of mutex checks if the mutex is avaliable, and acquires it
992  * if it is. Otherwise, the current thread is added to the mutex's wait
993  * queue and the thread suspends. When the holder of the mutex completes,
994  * the oldest thread (top thread) on the queue is given the lock and
995  * restarted.
996  */
997 void Mutex_Acquire(tMutex *Mutex)
998 {
999         tThread *us = Proc_GetCurThread();
1000         
1001         // Get protector
1002         SHORTLOCK( &Mutex->Protector );
1003         
1004         //Log("Mutex_Acquire: (%p)", Mutex);
1005         
1006         // Check if the lock is already held
1007         if( Mutex->Owner ) {
1008                 SHORTLOCK( &glThreadListLock );
1009                 // - Remove from active list
1010                 us = Threads_RemActive();
1011                 // - Mark as sleeping
1012                 us->Status = THREAD_STAT_OFFSLEEP;
1013                 
1014                 // - Add to waiting
1015                 if(Mutex->LastWaiting) {
1016                         Mutex->LastWaiting->Next = us;
1017                         Mutex->LastWaiting = us;
1018                 }
1019                 else {
1020                         Mutex->Waiting = us;
1021                         Mutex->LastWaiting = us;
1022                 }
1023                 SHORTREL( &glThreadListLock );
1024                 SHORTREL( &Mutex->Protector );
1025                 while(us->Status == THREAD_STAT_OFFSLEEP)       Threads_Yield();
1026                 // We're only woken when we get the lock
1027         }
1028         // Ooh, let's take it!
1029         else {
1030                 Mutex->Owner = us;
1031                 SHORTREL( &Mutex->Protector );
1032         }
1033 }
1034
1035 /**
1036  * \brief Release a held mutex
1037  * \param Mutex Mutex to release
1038  */
1039 void Mutex_Release(tMutex *Mutex)
1040 {
1041         SHORTLOCK( &Mutex->Protector );
1042         //Log("Mutex_Release: (%p)", Mutex);
1043         if( Mutex->Waiting ) {
1044                 Mutex->Owner = Mutex->Waiting;  // Set owner
1045                 Mutex->Waiting = Mutex->Waiting->Next;  // Next!
1046                 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
1047                 if( Mutex->LastWaiting == Mutex->Owner )
1048                         Mutex->LastWaiting = NULL;
1049                 
1050                 // Wake new owner
1051                 SHORTLOCK( &glThreadListLock );
1052                 if( Mutex->Owner->Status != THREAD_STAT_ACTIVE )
1053                         Threads_AddActive(Mutex->Owner);
1054                 SHORTREL( &glThreadListLock );
1055         }
1056         else {
1057                 Mutex->Owner = NULL;
1058         }
1059         SHORTREL( &Mutex->Protector );
1060 }
1061
1062 /**
1063  * \brief Is this mutex locked?
1064  * \param Mutex Mutex pointer
1065  */
1066 int Mutex_IsLocked(tMutex *Mutex)
1067 {
1068         return Mutex->Owner != NULL;
1069 }
1070
1071 // === EXPORTS ===
1072 EXPORT(Threads_GetUID);
1073 EXPORT(Threads_GetGID);
1074 EXPORT(Mutex_Acquire);
1075 EXPORT(Mutex_Release);
1076 EXPORT(Mutex_IsLocked);

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