Big bugfixes from trying a Clone/fork bomb
[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         
469         // TODO: Kill all children
470         #if 1
471         {
472                 tThread *child;
473                 // TODO: I should keep a .Parent pointer, and a .Children list
474                 for(child = gAllThreads;
475                         child;
476                         child = child->GlobalNext)
477                 {
478                         if(child->Parent == Thread)
479                                 Threads_Kill(child, -1);
480                 }
481         }
482         #endif
483         
484         ///\note Double lock is needed due to overlap of lock areas
485         
486         // Lock thread (stop us recieving messages)
487         SHORTLOCK( &Thread->IsLocked );
488         
489         // Clear Message Queue
490         while( Thread->Messages )
491         {
492                 msg = Thread->Messages->Next;
493                 free( Thread->Messages );
494                 Thread->Messages = msg;
495         }
496         
497         // Lock thread list
498         SHORTLOCK( &glThreadListLock );
499         
500         switch(Thread->Status)
501         {
502         case THREAD_STAT_PREINIT:       // Only on main list
503                 break;
504         
505         // Currently active thread
506         case THREAD_STAT_ACTIVE:
507                 #if SCHEDULER_TYPE == SCHED_RR_PRI
508                 if( Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread ) )
509                 #else
510                 if( Threads_int_DelFromQueue( &gActiveThreads, Thread ) )
511                 #endif
512                 {
513                         // Ensure that we are not rescheduled
514                         Thread->Remaining = 0;  // Clear Remaining Quantum
515                         Thread->Quantum = 0;    // Clear Quantum to indicate dead thread
516                         
517                         // Update bookkeeping
518                         giNumActiveThreads --;
519                         #if SCHEDULER_TYPE == SCHED_LOTTERY
520                         if( Thread != Proc_GetCurThread() )
521                                 giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ];
522                         #endif
523                 }
524                 else
525                 {
526                         Log_Warning("Threads",
527                                 "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list",
528                                 Thread, Thread->TID, Thread->ThreadName
529                                 );
530                 }
531                 break;
532         case THREAD_STAT_SLEEPING:
533                 if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) )
534                 {
535                         Log_Warning("Threads",
536                                 "Threads_Kill - Thread %p(%i,%s) marked as sleeping, but not on list",
537                                 Thread, Thread->TID, Thread->ThreadName
538                                 );
539                 }
540                 break;
541         default:
542                 Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)",
543                         Thread->Status);
544                 break;
545         }
546         
547         // Save exit status
548         Thread->RetStatus = Status;
549         
550         // Don't Zombie if we are being killed because our parent is
551         if(Status == -1)
552         {
553                 Thread->Status = THREAD_STAT_DEAD;
554                 Threads_AddToDelete( Thread );
555         } else {
556                 Thread->Status = THREAD_STAT_ZOMBIE;
557                 // Wake parent
558                 Threads_Wake( Thread->Parent );
559         }
560         
561         Log("Thread %i went *hurk* (%i)", Thread->TID, Status);
562         
563         // Release spinlocks
564         SHORTREL( &glThreadListLock );
565         SHORTREL( &Thread->IsLocked );  // TODO: We may not actually be released...
566         
567         // And, reschedule
568         if(Status != -1) {
569                 for( ;; )
570                         HALT();
571         }
572 }
573
574 /**
575  * \brief Yield remainder of the current thread's timeslice
576  */
577 void Threads_Yield(void)
578 {
579         tThread *thread = Proc_GetCurThread();
580         thread->Remaining = 0;
581         //while(thread->Remaining == 0)
582                 HALT();
583 }
584
585 /**
586  * \fn void Threads_Sleep(void)
587  * \brief Take the current process off the run queue
588  */
589 void Threads_Sleep(void)
590 {
591         tThread *cur = Proc_GetCurThread();
592         
593         // Acquire Spinlock
594         SHORTLOCK( &glThreadListLock );
595         
596         // Don't sleep if there is a message waiting
597         if( cur->Messages ) {
598                 SHORTREL( &glThreadListLock );
599                 return;
600         }
601         
602         // Remove us from running queue
603         Threads_RemActive();
604         // Mark thread as sleeping
605         cur->Status = THREAD_STAT_SLEEPING;
606         
607         // Add to Sleeping List (at the top)
608         cur->Next = gSleepingThreads;
609         gSleepingThreads = cur;
610         
611         
612         #if DEBUG_TRACE_STATE
613         Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
614         #endif
615         
616         // Release Spinlock
617         SHORTREL( &glThreadListLock );
618         
619         while(cur->Status != THREAD_STAT_ACTIVE)        HALT();
620 }
621
622
623 /**
624  * \fn int Threads_Wake( tThread *Thread )
625  * \brief Wakes a sleeping/waiting thread up
626  * \param Thread        Thread to wake
627  * \return Boolean Failure (Returns ERRNO)
628  * \warning This should ONLY be called with task switches disabled
629  */
630 int Threads_Wake(tThread *Thread)
631 {
632         if(!Thread)
633                 return -EINVAL;
634         
635         switch(Thread->Status)
636         {
637         case THREAD_STAT_ACTIVE:
638                 Log("Thread_Wake: Waking awake thread (%i)", Thread->TID);
639                 return -EALREADY;
640         
641         case THREAD_STAT_SLEEPING:
642                 SHORTLOCK( &glThreadListLock );
643                 // Remove from sleeping queue
644                 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
645                 
646                 Threads_AddActive( Thread );
647                 
648                 #if DEBUG_TRACE_STATE
649                 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
650                 #endif
651                 SHORTREL( &glThreadListLock );
652                 return -EOK;
653         
654         case THREAD_STAT_WAITING:
655                 Warning("Thread_Wake - Waiting threads are not currently supported");
656                 return -ENOTIMPL;
657         
658         case THREAD_STAT_DEAD:
659                 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
660                 return -ENOTIMPL;
661         
662         default:
663                 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
664                 return -EINTERNAL;
665         }
666 }
667
668 /**
669  * \brief Wake a thread given the TID
670  * \param TID   Thread ID to wake
671  * \return Boolean Faulure (errno)
672  */
673 int Threads_WakeTID(tTID TID)
674 {
675         tThread *thread = Threads_GetThread(TID);
676          int    ret;
677         if(!thread)
678                 return -ENOENT;
679         ret = Threads_Wake( thread );
680         //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
681         return ret;
682 }
683
684 /**
685  * \brief Adds a thread to the active queue
686  */
687 void Threads_AddActive(tThread *Thread)
688 {
689         SHORTLOCK( &glThreadListLock );
690         
691         if( Thread->Status == THREAD_STAT_ACTIVE ) {
692                 tThread *cur = Proc_GetCurThread();
693                 Warning("WTF, CPU%i %p (%i %s) is adding %p (%i %s) when it is active",
694                         GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName);
695         }
696         
697         // Set state
698         Thread->Status = THREAD_STAT_ACTIVE;
699         Thread->CurCPU = -1;
700         // Add to active list
701         #if SCHEDULER_TYPE == SCHED_RR_PRI
702         Thread->Next = gaActiveThreads[Thread->Priority];
703         gaActiveThreads[Thread->Priority] = Thread;
704         #else
705         Thread->Next = gActiveThreads;
706         gActiveThreads = Thread;
707         #endif
708         
709         // Update bookkeeping
710         giNumActiveThreads ++;
711         
712         #if SCHEDULER_TYPE == SCHED_LOTTERY
713         giFreeTickets += caiTICKET_COUNTS[ Thread->Priority ];
714         # if DEBUG_TRACE_TICKETS
715         Log("Threads_AddActive: CPU%i %p %i (%s) added, new giFreeTickets = %i",
716                 GetCPUNum(), Thread, Thread->TID, Thread->ThreadName, giFreeTickets);
717         # endif
718         #endif
719         
720         SHORTREL( &glThreadListLock );
721 }
722
723 /**
724  * \brief Removes the current thread from the active queue
725  * \warning This should ONLY be called with task switches disabled
726  * \return Current thread pointer
727  */
728 tThread *Threads_RemActive(void)
729 {
730         tThread *ret = Proc_GetCurThread();
731         
732         SHORTLOCK( &glThreadListLock );
733         
734         // Delete from active queue
735         #if SCHEDULER_TYPE == SCHED_RR_PRI
736         if( !Threads_int_DelFromQueue(&gaActiveThreads[ret->Priority], ret) )
737         #else
738         if( !Threads_int_DelFromQueue(&gActiveThreads, ret) )
739         #endif
740         {
741                 SHORTREL( &glThreadListLock );
742                 return NULL;
743         }
744         
745         ret->Next = NULL;
746         ret->Remaining = 0;
747         ret->CurCPU = -1;
748         
749         giNumActiveThreads --;
750         // no need to decrement tickets, scheduler did it for us
751         
752         #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
753         Log("Threads_RemActive: CPU%i %p %i (%s) removed, giFreeTickets = %i",
754                 GetCPUNum(), ret, ret->TID, ret->ThreadName, giFreeTickets);
755         #endif
756         
757         SHORTREL( &glThreadListLock );
758         
759         return ret;
760 }
761
762 /**
763  * \fn void Threads_SetFaultHandler(Uint Handler)
764  * \brief Sets the signal handler for a signal
765  */
766 void Threads_SetFaultHandler(Uint Handler)
767 {       
768         //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
769         Proc_GetCurThread()->FaultHandler = Handler;
770 }
771
772 /**
773  * \fn void Threads_Fault(int Num)
774  * \brief Calls a fault handler
775  */
776 void Threads_Fault(int Num)
777 {
778         tThread *thread = Proc_GetCurThread();
779         
780         Log_Log("Threads", "Threads_Fault: thread = %p", thread);
781         
782         if(!thread)     return ;
783         
784         Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
785         
786         switch(thread->FaultHandler)
787         {
788         case 0: // Panic?
789                 Threads_Kill(thread, -1);
790                 HALT();
791                 return ;
792         case 1: // Dump Core?
793                 Threads_Kill(thread, -1);
794                 HALT();
795                 return ;
796         }
797         
798         // Double Fault? Oh, F**k
799         if(thread->CurFaultNum != 0) {
800                 Threads_Kill(thread, -1);       // For now, just kill
801                 HALT();
802         }
803         
804         thread->CurFaultNum = Num;
805         
806         Proc_CallFaultHandler(thread);
807 }
808
809 // --- Process Structure Access Functions ---
810 tPID Threads_GetPID(void)
811 {
812         return Proc_GetCurThread()->TGID;
813 }
814 tTID Threads_GetTID(void)
815 {
816         return Proc_GetCurThread()->TID;
817 }
818 tUID Threads_GetUID(void)
819 {
820         return Proc_GetCurThread()->UID;
821 }
822 tGID Threads_GetGID(void)
823 {
824         return Proc_GetCurThread()->GID;
825 }
826
827 int Threads_SetUID(Uint *Errno, tUID ID)
828 {
829         tThread *t = Proc_GetCurThread();
830         if( t->UID != 0 ) {
831                 *Errno = -EACCES;
832                 return -1;
833         }
834         Log_Debug("Threads", "TID %i's UID set to %i", t->TID, ID);
835         t->UID = ID;
836         return 0;
837 }
838
839 int Threads_SetGID(Uint *Errno, tGID ID)
840 {
841         tThread *t = Proc_GetCurThread();
842         if( t->UID != 0 ) {
843                 *Errno = -EACCES;
844                 return -1;
845         }
846         Log_Debug("Threads", "TID %i's GID set to %i", t->TID, ID);
847         t->GID = ID;
848         return 0;
849 }
850
851 /**
852  * \fn void Threads_Dump(void)
853  */
854 void Threads_DumpActive(void)
855 {
856         tThread *thread;
857         #if SCHEDULER_TYPE == SCHED_RR_PRI
858          int    i;
859         #endif
860         
861         Log("Active Threads: (%i reported)", giNumActiveThreads);
862         
863         #if SCHEDULER_TYPE == SCHED_RR_PRI
864         for( i = 0; i < MIN_PRIORITY+1; i++ )
865         {
866                 for(thread=gaActiveThreads[i];thread;thread=thread->Next)
867         #else
868                 for(thread=gActiveThreads;thread;thread=thread->Next)
869         #endif
870                 {
871                         Log(" %p %i (%i) - %s (CPU %i)",
872                                 thread, thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
873                         if(thread->Status != THREAD_STAT_ACTIVE)
874                                 Log("  ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
875                         Log("  Priority %i, Quantum %i", thread->Priority, thread->Quantum);
876                         Log("  KStack 0x%x", thread->KernelStack);
877                 }
878         
879         #if SCHEDULER_TYPE == SCHED_RR_PRI
880         }
881         #endif
882 }
883
884 /**
885  * \fn void Threads_Dump(void)
886  * \brief Dumps a list of currently running threads
887  */
888 void Threads_Dump(void)
889 {
890         tThread *thread;
891         
892         Log("--- Thread Dump ---");
893         Threads_DumpActive();
894         
895         Log("All Threads:");
896         for(thread=gAllThreads;thread;thread=thread->GlobalNext)
897         {
898                 Log(" %p %i (%i) - %s (CPU %i)",
899                         thread, thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
900                 Log("  State %i (%s)", thread->Status, casTHREAD_STAT[thread->Status]);
901                 switch(thread->Status)
902                 {
903                 case THREAD_STAT_MUTEXSLEEP:
904                         Log("  Mutex Pointer: %p", thread->WaitPointer);
905                         break;
906                 case THREAD_STAT_ZOMBIE:
907                         Log("  Return Status: %i", thread->RetStatus);
908                         break;
909                 default:        break;
910                 }
911                 Log("  Priority %i, Quantum %i", thread->Priority, thread->Quantum);
912                 Log("  KStack 0x%x", thread->KernelStack);
913         }
914 }
915
916 /**
917  * \brief Gets the next thread to run
918  * \param CPU   Current CPU
919  * \param Last  The thread the CPU was running
920  */
921 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
922 {
923         tThread *thread;
924         
925         // If this CPU has the lock, we must let it complete
926         if( CPU_HAS_LOCK( &glThreadListLock ) )
927                 return Last;
928         
929         // Don't change threads if the current CPU has switches disabled
930         if( gaThreads_NoTaskSwitch[CPU] )
931                 return Last;
932
933
934         // Lock thread list
935         SHORTLOCK( &glThreadListLock );
936         
937         // Clear Delete Queue
938         // - I should probably put this in a worker thread to avoid calling free() in the scheduler
939         while(gDeleteThreads)
940         {
941                 thread = gDeleteThreads->Next;
942                 // Only free if structure is unused
943                 if( !IS_LOCKED(&gDeleteThreads->IsLocked) )
944                 {
945                         // Set to dead
946                         gDeleteThreads->Status = THREAD_STAT_BURIED;
947                         // Free name
948                         if( IsHeap(gDeleteThreads->ThreadName) )
949                                 free(gDeleteThreads->ThreadName);
950                         // Remove from global list
951                         if( gDeleteThreads == gAllThreads )
952                                 gAllThreads = gDeleteThreads->GlobalNext;
953                         else
954                                 gDeleteThreads->GlobalPrev->GlobalNext = gDeleteThreads->GlobalNext;
955                         free( gDeleteThreads );
956                 }
957                 gDeleteThreads = thread;
958         }
959         
960         // No active threads, just take a nap
961         if(giNumActiveThreads == 0) {
962                 SHORTREL( &glThreadListLock );
963                 #if DEBUG_TRACE_TICKETS
964                 Log("No active threads");
965                 #endif
966                 return NULL;
967         }
968         
969         #if SCHEDULER_TYPE != SCHED_RR_PRI
970         // Special case: 1 thread
971         if(giNumActiveThreads == 1) {
972                 if( gActiveThreads->CurCPU == -1 )
973                         gActiveThreads->CurCPU = CPU;
974                 
975                 SHORTREL( &glThreadListLock );
976                 
977                 if( gActiveThreads->CurCPU == CPU )
978                         return gActiveThreads;
979                 
980                 return NULL;    // CPU has nothing to do
981         }
982         #endif
983         
984         // Allow the old thread to be scheduled again
985         if( Last ) {
986                 if( Last->Status == THREAD_STAT_ACTIVE ) {
987                         #if SCHEDULER_TYPE == SCHED_LOTTERY
988                         giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
989                         # if DEBUG_TRACE_TICKETS
990                         LogF(" CPU %i released %p (%i %s) into the pool (%i tickets in pool)\n",
991                                 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets);
992                         # endif
993                         #endif
994                 }
995                 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
996                 else
997                         LogF(" CPU %i released %p (%i %s)->Status = %i (Released)\n",
998                                 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
999                 #endif
1000                 Last->CurCPU = -1;
1001         }
1002         
1003         // ---
1004         // Lottery Scheduler
1005         // ---
1006         #if SCHEDULER_TYPE == SCHED_LOTTERY
1007         {
1008                  int    ticket, number;
1009                 # if 1
1010                 number = 0;
1011                 for(thread = gActiveThreads; thread; thread = thread->Next) {
1012                         if(thread->CurCPU >= 0) continue;
1013                         if(thread->Status != THREAD_STAT_ACTIVE)
1014                                 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
1015                                         thread, thread->TID, thread->ThreadName, thread->Status);
1016                         if(thread->Next == thread) {
1017                                 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
1018                                         thread, thread->TID, thread->ThreadName, thread->Status);
1019                         }
1020                         number += caiTICKET_COUNTS[ thread->Priority ];
1021                 }
1022                 if(number != giFreeTickets) {
1023                         Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
1024                                 giFreeTickets, number, CPU);
1025                 }
1026                 # endif
1027                 
1028                 // No free tickets (all tasks delegated to cores)
1029                 if( giFreeTickets == 0 ) {
1030                         SHORTREL(&glThreadListLock);
1031                         return NULL;
1032                 }
1033                 
1034                 // Get the ticket number
1035                 ticket = number = rand() % giFreeTickets;
1036                 
1037                 // Find the next thread
1038                 for(thread=gActiveThreads;thread;thread=thread->Next)
1039                 {
1040                         if(thread->CurCPU >= 0) continue;
1041                         if( caiTICKET_COUNTS[ thread->Priority ] > number)      break;
1042                         number -= caiTICKET_COUNTS[ thread->Priority ];
1043                 }
1044                 
1045                 // If we didn't find a thread, something went wrong
1046                 if(thread == NULL)
1047                 {
1048                         number = 0;
1049                         for(thread=gActiveThreads;thread;thread=thread->Next) {
1050                                 if(thread->CurCPU >= 0) continue;
1051                                 number += caiTICKET_COUNTS[ thread->Priority ];
1052                         }
1053                         Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
1054                                 giFreeTickets, number);
1055                 }
1056                 # if DEBUG_TRACE_TICKETS
1057                 LogF(" CPU%i giFreeTickets = %i, running %p (%i %s CPU=%i)\n",
1058                         CPU, giFreeTickets, thread, thread->TID, thread->ThreadName, thread->CurCPU);
1059                 # endif
1060                 
1061                 giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
1062         }
1063         
1064         // ---
1065         // Priority based round robin scheduler
1066         // ---
1067         #elif SCHEDULER_TYPE == SCHED_RR_PRI
1068         {
1069                  int    i;
1070                 for( i = 0; i < MIN_PRIORITY + 1; i ++ )
1071                 {
1072                         for(thread = gaActiveThreads[i]; thread; thread = thread->Next)
1073                         {
1074                                 if( thread->CurCPU == -1 )      break;
1075                         }
1076                         // If we fall onto the same queue again, special handling is
1077                         // needed
1078                         if( i == Last->Priority ) {
1079                                 tThread *savedThread = thread;
1080                                 
1081                                 // Find the next unscheduled thread in the list
1082                                 for( thread = Last->Next; thread; thread = thread->Next )
1083                                 {
1084                                         if( thread->CurCPU == -1 )      break;
1085                                 }
1086                                 // If we don't find anything after, just use the one 
1087                                 // found above.
1088                                 if( !thread )   thread = savedThread;
1089                         }
1090                         // Found a thread? Schedule it!
1091                         if( thread )    break;
1092                 }
1093                 
1094                 // Anything to do?
1095                 if( !thread ) {
1096                         SHORTREL(&glThreadListLock);
1097                         return NULL;
1098                 }
1099         }
1100         #elif SCHEDULER_TYPE == SCHED_RR_SIM
1101         {               
1102                 // Find the next unscheduled thread in the list
1103                 for( thread = Last->Next; thread; thread = thread->Next )
1104                 {
1105                         if( thread->CurCPU == -1 )      break;
1106                 }
1107                 // If we don't find anything after, search from the beginning
1108                 if( !thread )
1109                 {
1110                         for(thread = gActiveThreads; thread; thread = thread->Next)
1111                         {
1112                                 if( thread->CurCPU == -1 )      break;
1113                         }       
1114                 }
1115                 
1116                 // Anything to do?
1117                 if( !thread ) {
1118                         SHORTREL(&glThreadListLock);
1119                         return NULL;
1120                 }
1121         }
1122         #else
1123         # error "Unimplemented scheduling algorithm"
1124         #endif
1125         
1126         // Make the new thread non-schedulable
1127         thread->CurCPU = CPU;
1128         
1129         SHORTREL( &glThreadListLock );
1130         
1131         return thread;
1132 }
1133
1134 /**
1135  * \fn void Threads_SegFault(tVAddr Addr)
1136  * \brief Called when a Segment Fault occurs
1137  */
1138 void Threads_SegFault(tVAddr Addr)
1139 {
1140         Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
1141         Threads_Fault( 1 );
1142         //Threads_Exit( 0, -1 );
1143 }
1144
1145 /**
1146  * \brief Acquire a heavy mutex
1147  * \param Mutex Mutex to acquire
1148  * 
1149  * This type of mutex checks if the mutex is avaliable, and acquires it
1150  * if it is. Otherwise, the current thread is added to the mutex's wait
1151  * queue and the thread suspends. When the holder of the mutex completes,
1152  * the oldest thread (top thread) on the queue is given the lock and
1153  * restarted.
1154  */
1155 void Mutex_Acquire(tMutex *Mutex)
1156 {
1157         tThread *us = Proc_GetCurThread();
1158         
1159         // Get protector
1160         SHORTLOCK( &Mutex->Protector );
1161         
1162         //Log("Mutex_Acquire: (%p)", Mutex);
1163         
1164         // Check if the lock is already held
1165         if( Mutex->Owner ) {
1166                 SHORTLOCK( &glThreadListLock );
1167                 // - Remove from active list
1168                 us = Threads_RemActive();
1169                 us->Next = NULL;
1170                 // - Mark as sleeping
1171                 us->Status = THREAD_STAT_MUTEXSLEEP;
1172                 us->WaitPointer = Mutex;
1173                 
1174                 // - Add to waiting
1175                 if(Mutex->LastWaiting) {
1176                         Mutex->LastWaiting->Next = us;
1177                         Mutex->LastWaiting = us;
1178                 }
1179                 else {
1180                         Mutex->Waiting = us;
1181                         Mutex->LastWaiting = us;
1182                 }
1183                 #if 0
1184                 {
1185                          int    i = 0;
1186                         tThread *t;
1187                         for( t = Mutex->Waiting; t; t = t->Next, i++ )
1188                                 Log("[%i] (tMutex)%p->Waiting[%i] = %p (%i %s)", us->TID, Mutex, i,
1189                                         t, t->TID, t->ThreadName);
1190                 }
1191                 #endif
1192                 
1193                 SHORTREL( &glThreadListLock );
1194                 SHORTREL( &Mutex->Protector );
1195                 while(us->Status == THREAD_STAT_MUTEXSLEEP)     Threads_Yield();
1196                 // We're only woken when we get the lock
1197                 us->WaitPointer = NULL;
1198         }
1199         // Ooh, let's take it!
1200         else {
1201                 Mutex->Owner = us;
1202                 SHORTREL( &Mutex->Protector );
1203         }
1204         
1205         #if 0
1206         extern tMutex   glPhysAlloc;
1207         if( Mutex != &glPhysAlloc )
1208                 LogF("Mutex %p taken by %i %p\n", Mutex, us->TID, __builtin_return_address(0));
1209         #endif
1210 }
1211
1212 /**
1213  * \brief Release a held mutex
1214  * \param Mutex Mutex to release
1215  */
1216 void Mutex_Release(tMutex *Mutex)
1217 {
1218         SHORTLOCK( &Mutex->Protector );
1219         //Log("Mutex_Release: (%p)", Mutex);
1220         if( Mutex->Waiting ) {
1221                 Mutex->Owner = Mutex->Waiting;  // Set owner
1222                 Mutex->Waiting = Mutex->Waiting->Next;  // Next!
1223                 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
1224                 // 2010-10-02 21:50 - Comemerating the death of the longest single
1225                 //                    blocker in the Acess2 history. REMEMBER TO
1226                 //                    FUCKING MAINTAIN YOUR FUCKING LISTS DIPWIT
1227                 if( Mutex->LastWaiting == Mutex->Owner )
1228                         Mutex->LastWaiting = NULL;
1229                 
1230                 // Wake new owner
1231                 SHORTLOCK( &glThreadListLock );
1232                 if( Mutex->Owner->Status != THREAD_STAT_ACTIVE )
1233                         Threads_AddActive(Mutex->Owner);
1234                 SHORTREL( &glThreadListLock );
1235         }
1236         else {
1237                 Mutex->Owner = NULL;
1238         }
1239         SHORTREL( &Mutex->Protector );
1240         
1241         #if 0
1242         extern tMutex   glPhysAlloc;
1243         if( Mutex != &glPhysAlloc )
1244                 LogF("Mutex %p released by %i %p\n", Mutex, Threads_GetTID(), __builtin_return_address(0));
1245         #endif
1246 }
1247
1248 /**
1249  * \brief Is this mutex locked?
1250  * \param Mutex Mutex pointer
1251  */
1252 int Mutex_IsLocked(tMutex *Mutex)
1253 {
1254         return Mutex->Owner != NULL;
1255 }
1256
1257 /**
1258  * \brief Initialise the semaphore
1259  * \param Value Initial value of the semaphore
1260  * \param Label Symbolic name
1261  */
1262 void Semaphore_Init(tSemaphore *Sem, int Value, const char *Label)
1263 {
1264         Sem->Value = Value;
1265         Sem->Name = Label;
1266 }
1267
1268 /**
1269  * \brief Acquire a "item" from the semaphore
1270  */
1271 void Semaphore_Wait(tSemaphore *Sem)
1272 {
1273         tThread *us;
1274         
1275         SHORTLOCK( &Sem->Protector );
1276         if( Sem->Value > 0 ) {
1277                 Sem->Value --;
1278                 SHORTREL( &Sem->Protector );
1279                 return ;
1280         }
1281         
1282         SHORTLOCK( &glThreadListLock );
1283         
1284         // - Remove from active list
1285         us = Threads_RemActive();
1286         us->Next = NULL;
1287         // - Mark as sleeping
1288         us->Status = THREAD_STAT_SEMAPHORESLEEP;
1289         us->WaitPointer = Sem;
1290         
1291         // - Add to waiting
1292         if(Sem->LastWaiting) {
1293                 Sem->LastWaiting->Next = us;
1294                 Sem->LastWaiting = us;
1295         }
1296         else {
1297                 Sem->Waiting = us;
1298                 Sem->LastWaiting = us;
1299         }
1300         
1301         SHORTREL( &glThreadListLock );
1302         SHORTREL( &Sem->Protector );
1303         while(us->Status == THREAD_STAT_MUTEXSLEEP)     Threads_Yield();
1304         // We're only woken when there's something avaliable
1305         us->WaitPointer = NULL;
1306 }
1307
1308 /**
1309  * \brief Add an "item" to the semaphore
1310  */
1311 void Semaphore_Signal(tSemaphore *Sem)
1312 {
1313         SHORTLOCK( &Sem->Protector );
1314         Sem->Value ++;
1315         
1316         if( Sem->Waiting )
1317         {
1318                 tThread *toWake = Sem->Waiting;
1319                 
1320                 Sem->Waiting = Sem->Waiting->Next;      // Next!
1321                 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
1322                 if( Sem->Waiting == NULL )
1323                         Sem->LastWaiting = NULL;
1324                 
1325                 // Wake new owner
1326                 SHORTLOCK( &glThreadListLock );
1327                 if( toWake->Status != THREAD_STAT_ACTIVE )
1328                         Threads_AddActive(toWake);
1329                 SHORTREL( &glThreadListLock );
1330                 
1331                 // Decrement (the value is now "owned" by `toWake`)
1332                 Sem->Value --;
1333         }
1334         SHORTREL( &Sem->Protector );
1335 }
1336
1337 // === EXPORTS ===
1338 EXPORT(Threads_GetUID);
1339 EXPORT(Threads_GetGID);
1340 EXPORT(Mutex_Acquire);
1341 EXPORT(Mutex_Release);
1342 EXPORT(Mutex_IsLocked);

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