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

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