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

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