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

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