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

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