Kernel - Implementing select()
[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         }
786         
787         // Set state
788         Thread->Status = THREAD_STAT_ACTIVE;
789         Thread->CurCPU = -1;
790         // Add to active list
791         #if SCHEDULER_TYPE == SCHED_RR_PRI
792         Thread->Next = gaActiveThreads[Thread->Priority];
793         gaActiveThreads[Thread->Priority] = Thread;
794         #else
795         Thread->Next = gActiveThreads;
796         gActiveThreads = Thread;
797         #endif
798         
799         // Update bookkeeping
800         giNumActiveThreads ++;
801         
802         #if SCHEDULER_TYPE == SCHED_LOTTERY
803         giFreeTickets += caiTICKET_COUNTS[ Thread->Priority ];
804         # if DEBUG_TRACE_TICKETS
805         Log("Threads_AddActive: CPU%i %p %i (%s) added, new giFreeTickets = %i",
806                 GetCPUNum(), Thread, Thread->TID, Thread->ThreadName, giFreeTickets);
807         # endif
808         #endif
809         
810         SHORTREL( &glThreadListLock );
811 }
812
813 /**
814  * \brief Removes the current thread from the active queue
815  * \warning This should ONLY be called with task switches disabled
816  * \return Current thread pointer
817  */
818 tThread *Threads_RemActive(void)
819 {
820         tThread *ret = Proc_GetCurThread();
821         
822         SHORTLOCK( &glThreadListLock );
823         
824         // Delete from active queue
825         #if SCHEDULER_TYPE == SCHED_RR_PRI
826         if( !Threads_int_DelFromQueue(&gaActiveThreads[ret->Priority], ret) )
827         #else
828         if( !Threads_int_DelFromQueue(&gActiveThreads, ret) )
829         #endif
830         {
831                 SHORTREL( &glThreadListLock );
832                 return NULL;
833         }
834         
835         ret->Next = NULL;
836         ret->Remaining = 0;
837         ret->CurCPU = -1;
838         
839         giNumActiveThreads --;
840         // no need to decrement tickets, scheduler did it for us
841         
842         #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
843         Log("Threads_RemActive: CPU%i %p %i (%s) removed, giFreeTickets = %i",
844                 GetCPUNum(), ret, ret->TID, ret->ThreadName, giFreeTickets);
845         #endif
846         
847         SHORTREL( &glThreadListLock );
848         
849         return ret;
850 }
851
852 /**
853  * \fn void Threads_SetFaultHandler(Uint Handler)
854  * \brief Sets the signal handler for a signal
855  */
856 void Threads_SetFaultHandler(Uint Handler)
857 {       
858         //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
859         Proc_GetCurThread()->FaultHandler = Handler;
860 }
861
862 /**
863  * \fn void Threads_Fault(int Num)
864  * \brief Calls a fault handler
865  */
866 void Threads_Fault(int Num)
867 {
868         tThread *thread = Proc_GetCurThread();
869         
870         Log_Log("Threads", "Threads_Fault: thread = %p", thread);
871         
872         if(!thread)     return ;
873         
874         Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
875         
876         switch(thread->FaultHandler)
877         {
878         case 0: // Panic?
879                 Threads_Kill(thread, -1);
880                 HALT();
881                 return ;
882         case 1: // Dump Core?
883                 Threads_Kill(thread, -1);
884                 HALT();
885                 return ;
886         }
887         
888         // Double Fault? Oh, F**k
889         if(thread->CurFaultNum != 0) {
890                 Threads_Kill(thread, -1);       // For now, just kill
891                 HALT();
892         }
893         
894         thread->CurFaultNum = Num;
895         
896         Proc_CallFaultHandler(thread);
897 }
898
899 /**
900  * \fn void Threads_SegFault(tVAddr Addr)
901  * \brief Called when a Segment Fault occurs
902  */
903 void Threads_SegFault(tVAddr Addr)
904 {
905         Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
906         Threads_Fault( 1 );
907         //Threads_Exit( 0, -1 );
908 }
909
910 // --- Process Structure Access Functions ---
911 tPID Threads_GetPID(void)
912 {
913         return Proc_GetCurThread()->TGID;
914 }
915 tTID Threads_GetTID(void)
916 {
917         return Proc_GetCurThread()->TID;
918 }
919 tUID Threads_GetUID(void)
920 {
921         return Proc_GetCurThread()->UID;
922 }
923 tGID Threads_GetGID(void)
924 {
925         return Proc_GetCurThread()->GID;
926 }
927
928 int Threads_SetUID(Uint *Errno, tUID ID)
929 {
930         tThread *t = Proc_GetCurThread();
931         if( t->UID != 0 ) {
932                 *Errno = -EACCES;
933                 return -1;
934         }
935         Log_Debug("Threads", "TID %i's UID set to %i", t->TID, ID);
936         t->UID = ID;
937         return 0;
938 }
939
940 int Threads_SetGID(Uint *Errno, tGID ID)
941 {
942         tThread *t = Proc_GetCurThread();
943         if( t->UID != 0 ) {
944                 *Errno = -EACCES;
945                 return -1;
946         }
947         Log_Debug("Threads", "TID %i's GID set to %i", t->TID, ID);
948         t->GID = ID;
949         return 0;
950 }
951
952 /**
953  * \fn void Threads_Dump(void)
954  */
955 void Threads_DumpActive(void)
956 {
957         tThread *thread;
958         #if SCHEDULER_TYPE == SCHED_RR_PRI
959          int    i;
960         #endif
961         
962         Log("Active Threads: (%i reported)", giNumActiveThreads);
963         
964         #if SCHEDULER_TYPE == SCHED_RR_PRI
965         for( i = 0; i < MIN_PRIORITY+1; i++ )
966         {
967                 for(thread=gaActiveThreads[i];thread;thread=thread->Next)
968         #else
969                 for(thread=gActiveThreads;thread;thread=thread->Next)
970         #endif
971                 {
972                         Log(" %p %i (%i) - %s (CPU %i)",
973                                 thread, thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
974                         if(thread->Status != THREAD_STAT_ACTIVE)
975                                 Log("  ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
976                         Log("  Priority %i, Quantum %i", thread->Priority, thread->Quantum);
977                         Log("  KStack 0x%x", thread->KernelStack);
978                 }
979         
980         #if SCHEDULER_TYPE == SCHED_RR_PRI
981         }
982         #endif
983 }
984
985 /**
986  * \fn void Threads_Dump(void)
987  * \brief Dumps a list of currently running threads
988  */
989 void Threads_Dump(void)
990 {
991         tThread *thread;
992         
993         Log("--- Thread Dump ---");
994         Threads_DumpActive();
995         
996         Log("All Threads:");
997         for(thread=gAllThreads;thread;thread=thread->GlobalNext)
998         {
999                 Log(" %p %i (%i) - %s (CPU %i)",
1000                         thread, thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
1001                 Log("  State %i (%s)", thread->Status, casTHREAD_STAT[thread->Status]);
1002                 switch(thread->Status)
1003                 {
1004                 case THREAD_STAT_MUTEXSLEEP:
1005                         Log("  Mutex Pointer: %p", thread->WaitPointer);
1006                         break;
1007                 case THREAD_STAT_SEMAPHORESLEEP:
1008                         Log("  Semaphore Pointer: %p", thread->WaitPointer);
1009                         Log("  Semaphore Name: %s:%s", 
1010                                 ((tSemaphore*)thread->WaitPointer)->ModName,
1011                                 ((tSemaphore*)thread->WaitPointer)->Name
1012                                 );
1013                         break;
1014                 case THREAD_STAT_ZOMBIE:
1015                         Log("  Return Status: %i", thread->RetStatus);
1016                         break;
1017                 default:        break;
1018                 }
1019                 Log("  Priority %i, Quantum %i", thread->Priority, thread->Quantum);
1020                 Log("  KStack 0x%x", thread->KernelStack);
1021         }
1022 }
1023
1024 /**
1025  * \brief Gets the next thread to run
1026  * \param CPU   Current CPU
1027  * \param Last  The thread the CPU was running
1028  */
1029 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
1030 {
1031         tThread *thread;
1032         
1033         // If this CPU has the lock, we must let it complete
1034         if( CPU_HAS_LOCK( &glThreadListLock ) )
1035                 return Last;
1036         
1037         // Don't change threads if the current CPU has switches disabled
1038         if( gaThreads_NoTaskSwitch[CPU] )
1039                 return Last;
1040
1041
1042         // Lock thread list
1043         SHORTLOCK( &glThreadListLock );
1044         
1045         // Clear Delete Queue
1046         // - I should probably put this in a worker thread to avoid calling free() in the scheduler
1047         //   DEFINITELY - free() can deadlock in this case
1048         while(gDeleteThreads)
1049         {
1050                 thread = gDeleteThreads->Next;
1051                 // Only free if structure is unused
1052                 if( !IS_LOCKED(&gDeleteThreads->IsLocked) )
1053                 {
1054                         // Set to dead
1055                         gDeleteThreads->Status = THREAD_STAT_BURIED;
1056                         // Free name
1057                         if( IsHeap(gDeleteThreads->ThreadName) )
1058                                 free(gDeleteThreads->ThreadName);
1059                         // Remove from global list
1060                         if( gDeleteThreads == gAllThreads )
1061                                 gAllThreads = gDeleteThreads->GlobalNext;
1062                         else
1063                                 gDeleteThreads->GlobalPrev->GlobalNext = gDeleteThreads->GlobalNext;
1064                         free( gDeleteThreads );
1065                 }
1066                 gDeleteThreads = thread;
1067         }
1068         
1069         // No active threads, just take a nap
1070         if(giNumActiveThreads == 0) {
1071                 SHORTREL( &glThreadListLock );
1072                 #if DEBUG_TRACE_TICKETS
1073                 Log("No active threads");
1074                 #endif
1075                 return NULL;
1076         }
1077         
1078         #if SCHEDULER_TYPE != SCHED_RR_PRI
1079         // Special case: 1 thread
1080         if(giNumActiveThreads == 1) {
1081                 if( gActiveThreads->CurCPU == -1 )
1082                         gActiveThreads->CurCPU = CPU;
1083                 
1084                 SHORTREL( &glThreadListLock );
1085                 
1086                 if( gActiveThreads->CurCPU == CPU )
1087                         return gActiveThreads;
1088                 
1089                 return NULL;    // CPU has nothing to do
1090         }
1091         #endif
1092         
1093         // Allow the old thread to be scheduled again
1094         if( Last ) {
1095                 if( Last->Status == THREAD_STAT_ACTIVE ) {
1096                         #if SCHEDULER_TYPE == SCHED_LOTTERY
1097                         giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
1098                         # if DEBUG_TRACE_TICKETS
1099                         LogF(" CPU %i released %p (%i %s) into the pool (%i tickets in pool)\n",
1100                                 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets);
1101                         # endif
1102                         #endif
1103                 }
1104                 #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
1105                 else
1106                         LogF(" CPU %i released %p (%i %s)->Status = %i (Released)\n",
1107                                 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
1108                 #endif
1109                 Last->CurCPU = -1;
1110         }
1111         
1112         // ---
1113         // Lottery Scheduler
1114         // ---
1115         #if SCHEDULER_TYPE == SCHED_LOTTERY
1116         {
1117                  int    ticket, number;
1118                 # if 1
1119                 number = 0;
1120                 for(thread = gActiveThreads; thread; thread = thread->Next) {
1121                         if(thread->CurCPU >= 0) continue;
1122                         if(thread->Status != THREAD_STAT_ACTIVE)
1123                                 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
1124                                         thread, thread->TID, thread->ThreadName, thread->Status);
1125                         if(thread->Next == thread) {
1126                                 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
1127                                         thread, thread->TID, thread->ThreadName, thread->Status);
1128                         }
1129                         number += caiTICKET_COUNTS[ thread->Priority ];
1130                 }
1131                 if(number != giFreeTickets) {
1132                         Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
1133                                 giFreeTickets, number, CPU);
1134                 }
1135                 # endif
1136                 
1137                 // No free tickets (all tasks delegated to cores)
1138                 if( giFreeTickets == 0 ) {
1139                         SHORTREL(&glThreadListLock);
1140                         return NULL;
1141                 }
1142                 
1143                 // Get the ticket number
1144                 ticket = number = rand() % giFreeTickets;
1145                 
1146                 // Find the next thread
1147                 for(thread=gActiveThreads;thread;thread=thread->Next)
1148                 {
1149                         if(thread->CurCPU >= 0) continue;
1150                         if( caiTICKET_COUNTS[ thread->Priority ] > number)      break;
1151                         number -= caiTICKET_COUNTS[ thread->Priority ];
1152                 }
1153                 
1154                 // If we didn't find a thread, something went wrong
1155                 if(thread == NULL)
1156                 {
1157                         number = 0;
1158                         for(thread=gActiveThreads;thread;thread=thread->Next) {
1159                                 if(thread->CurCPU >= 0) continue;
1160                                 number += caiTICKET_COUNTS[ thread->Priority ];
1161                         }
1162                         Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
1163                                 giFreeTickets, number);
1164                 }
1165                 # if DEBUG_TRACE_TICKETS
1166                 LogF(" CPU%i giFreeTickets = %i, running %p (%i %s CPU=%i)\n",
1167                         CPU, giFreeTickets, thread, thread->TID, thread->ThreadName, thread->CurCPU);
1168                 # endif
1169                 
1170                 giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
1171         }
1172         
1173         // ---
1174         // Priority based round robin scheduler
1175         // ---
1176         #elif SCHEDULER_TYPE == SCHED_RR_PRI
1177         {
1178                  int    i;
1179                 for( i = 0; i < MIN_PRIORITY + 1; i ++ )
1180                 {
1181                         for(thread = gaActiveThreads[i]; thread; thread = thread->Next)
1182                         {
1183                                 if( thread->CurCPU == -1 )      break;
1184                         }
1185                         // If we fall onto the same queue again, special handling is
1186                         // needed
1187                         if( i == Last->Priority ) {
1188                                 tThread *savedThread = thread;
1189                                 
1190                                 // Find the next unscheduled thread in the list
1191                                 for( thread = Last->Next; thread; thread = thread->Next )
1192                                 {
1193                                         if( thread->CurCPU == -1 )      break;
1194                                 }
1195                                 // If we don't find anything after, just use the one 
1196                                 // found above.
1197                                 if( !thread )   thread = savedThread;
1198                         }
1199                         // Found a thread? Schedule it!
1200                         if( thread )    break;
1201                 }
1202                 
1203                 // Anything to do?
1204                 if( !thread ) {
1205                         SHORTREL(&glThreadListLock);
1206                         return NULL;
1207                 }
1208         }
1209         #elif SCHEDULER_TYPE == SCHED_RR_SIM
1210         {               
1211                 // Find the next unscheduled thread in the list
1212                 for( thread = Last->Next; thread; thread = thread->Next )
1213                 {
1214                         if( thread->CurCPU == -1 )      break;
1215                 }
1216                 // If we don't find anything after, search from the beginning
1217                 if( !thread )
1218                 {
1219                         for(thread = gActiveThreads; thread; thread = thread->Next)
1220                         {
1221                                 if( thread->CurCPU == -1 )      break;
1222                         }       
1223                 }
1224                 
1225                 // Anything to do?
1226                 if( !thread ) {
1227                         SHORTREL(&glThreadListLock);
1228                         return NULL;
1229                 }
1230         }
1231         #else
1232         # error "Unimplemented scheduling algorithm"
1233         #endif
1234         
1235         // Make the new thread non-schedulable
1236         thread->CurCPU = CPU;
1237         
1238         SHORTREL( &glThreadListLock );
1239         
1240         return thread;
1241 }
1242
1243 /**
1244  * \brief Acquire a heavy mutex
1245  * \param Mutex Mutex to acquire
1246  * 
1247  * This type of mutex checks if the mutex is avaliable, and acquires it
1248  * if it is. Otherwise, the current thread is added to the mutex's wait
1249  * queue and the thread suspends. When the holder of the mutex completes,
1250  * the oldest thread (top thread) on the queue is given the lock and
1251  * restarted.
1252  */
1253 int Mutex_Acquire(tMutex *Mutex)
1254 {
1255         tThread *us = Proc_GetCurThread();
1256         
1257         // Get protector
1258         SHORTLOCK( &Mutex->Protector );
1259         
1260         //Log("Mutex_Acquire: (%p)", Mutex);
1261         
1262         // Check if the lock is already held
1263         if( Mutex->Owner ) {
1264                 SHORTLOCK( &glThreadListLock );
1265                 // - Remove from active list
1266                 us = Threads_RemActive();
1267                 us->Next = NULL;
1268                 // - Mark as sleeping
1269                 us->Status = THREAD_STAT_MUTEXSLEEP;
1270                 us->WaitPointer = Mutex;
1271                 
1272                 // - Add to waiting
1273                 if(Mutex->LastWaiting) {
1274                         Mutex->LastWaiting->Next = us;
1275                         Mutex->LastWaiting = us;
1276                 }
1277                 else {
1278                         Mutex->Waiting = us;
1279                         Mutex->LastWaiting = us;
1280                 }
1281                 #if 0
1282                 {
1283                          int    i = 0;
1284                         tThread *t;
1285                         for( t = Mutex->Waiting; t; t = t->Next, i++ )
1286                                 Log("[%i] (tMutex)%p->Waiting[%i] = %p (%i %s)", us->TID, Mutex, i,
1287                                         t, t->TID, t->ThreadName);
1288                 }
1289                 #endif
1290                 
1291                 SHORTREL( &glThreadListLock );
1292                 SHORTREL( &Mutex->Protector );
1293                 while(us->Status == THREAD_STAT_MUTEXSLEEP)     Threads_Yield();
1294                 // We're only woken when we get the lock
1295                 us->WaitPointer = NULL;
1296         }
1297         // Ooh, let's take it!
1298         else {
1299                 Mutex->Owner = us;
1300                 SHORTREL( &Mutex->Protector );
1301         }
1302         
1303         #if 0
1304         extern tMutex   glPhysAlloc;
1305         if( Mutex != &glPhysAlloc )
1306                 LogF("Mutex %p taken by %i %p\n", Mutex, us->TID, __builtin_return_address(0));
1307         #endif
1308         
1309         return 0;
1310 }
1311
1312 /**
1313  * \brief Release a held mutex
1314  * \param Mutex Mutex to release
1315  * \note Releasing a non-held mutex has no effect
1316  */
1317 void Mutex_Release(tMutex *Mutex)
1318 {
1319         SHORTLOCK( &Mutex->Protector );
1320         //Log("Mutex_Release: (%p)", Mutex);
1321         if( Mutex->Waiting ) {
1322                 Mutex->Owner = Mutex->Waiting;  // Set owner
1323                 Mutex->Waiting = Mutex->Waiting->Next;  // Next!
1324                 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
1325                 // 2010-10-02 21:50 - Comemerating the death of the longest single
1326                 //                    blocker in the Acess2 history. REMEMBER TO
1327                 //                    FUCKING MAINTAIN YOUR FUCKING LISTS DIPWIT
1328                 if( Mutex->LastWaiting == Mutex->Owner )
1329                         Mutex->LastWaiting = NULL;
1330                 
1331                 // Wake new owner
1332                 SHORTLOCK( &glThreadListLock );
1333                 if( Mutex->Owner->Status != THREAD_STAT_ACTIVE )
1334                         Threads_AddActive(Mutex->Owner);
1335                 SHORTREL( &glThreadListLock );
1336         }
1337         else {
1338                 Mutex->Owner = NULL;
1339         }
1340         SHORTREL( &Mutex->Protector );
1341         
1342         #if 0
1343         extern tMutex   glPhysAlloc;
1344         if( Mutex != &glPhysAlloc )
1345                 LogF("Mutex %p released by %i %p\n", Mutex, Threads_GetTID(), __builtin_return_address(0));
1346         #endif
1347 }
1348
1349 /**
1350  * \brief Is this mutex locked?
1351  * \param Mutex Mutex pointer
1352  */
1353 int Mutex_IsLocked(tMutex *Mutex)
1354 {
1355         return Mutex->Owner != NULL;
1356 }
1357
1358 //
1359 // Initialise a semaphore
1360 //
1361 void Semaphore_Init(tSemaphore *Sem, int Value, int MaxValue, const char *Module, const char *Name)
1362 {
1363         Sem->Value = Value;
1364         Sem->ModName = Module;
1365         Sem->Name = Name;
1366 }
1367 //
1368 // Wait for items to be avaliable
1369 //
1370 int Semaphore_Wait(tSemaphore *Sem, int MaxToTake)
1371 {
1372         tThread *us;
1373          int    taken;
1374         if( MaxToTake < 0 ) {
1375                 Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)",
1376                         MaxToTake, Sem, Sem->Name);
1377         }
1378         
1379         SHORTLOCK( &Sem->Protector );
1380         
1381         // Check if there's already items avaliable
1382         if( Sem->Value > 0 ) {
1383                 // Take what we need
1384                 if( MaxToTake && Sem->Value > MaxToTake )
1385                         taken = MaxToTake;
1386                 else
1387                         taken = Sem->Value;
1388                 Sem->Value -= taken;
1389                 SHORTREL( &Sem->Protector );
1390         }
1391         else
1392         {
1393                 SHORTLOCK( &glThreadListLock );
1394                 
1395                 // - Remove from active list
1396                 us = Threads_RemActive();
1397                 us->Next = NULL;
1398                 // - Mark as sleeping
1399                 us->Status = THREAD_STAT_SEMAPHORESLEEP;
1400                 us->WaitPointer = Sem;
1401                 us->RetStatus = MaxToTake;      // Use RetStatus as a temp variable
1402                 
1403                 // - Add to waiting
1404                 if(Sem->LastWaiting) {
1405                         Sem->LastWaiting->Next = us;
1406                         Sem->LastWaiting = us;
1407                 }
1408                 else {
1409                         Sem->Waiting = us;
1410                         Sem->LastWaiting = us;
1411                 }
1412                 
1413                 SHORTREL( &glThreadListLock );  
1414                 SHORTREL( &Sem->Protector );
1415                 while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
1416                 // We're only woken when there's something avaliable
1417                 us->WaitPointer = NULL;
1418                 
1419                 taken = us->RetStatus;
1420                 
1421                 // Get the lock again
1422                 SHORTLOCK( &Sem->Protector );
1423         }
1424         
1425         // While there is space, and there are thread waiting
1426         // wake the first thread and give it what it wants (or what's left)
1427         while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling )
1428         {
1429                  int    given;
1430                 tThread *toWake = Sem->Signaling;
1431                 
1432                 Sem->Signaling = Sem->Signaling->Next;
1433                 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
1434                 if( Sem->Signaling == NULL )
1435                         Sem->LastSignaling = NULL;
1436                 
1437                 // Figure out how much to give
1438                 if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
1439                         given = toWake->RetStatus;
1440                 else
1441                         given = Sem->MaxValue - Sem->Value;
1442                 Sem->Value -= given;
1443                 
1444                 // Save the number we gave to the thread's status
1445                 toWake->RetStatus = given;
1446                 
1447                 // Wake the sleeper
1448                 SHORTLOCK( &glThreadListLock );
1449                 if( toWake->Status != THREAD_STAT_ACTIVE )
1450                         Threads_AddActive(toWake);
1451                 SHORTREL( &glThreadListLock );
1452         }
1453         SHORTREL( &Sem->Protector );
1454         
1455         return taken;
1456 }
1457
1458 //
1459 // Add items to a semaphore
1460 //
1461 int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
1462 {
1463          int    given;
1464          int    added;
1465         
1466         if( AmmountToAdd < 0 ) {
1467                 Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
1468                         AmmountToAdd, Sem, Sem->Name);
1469         }
1470         SHORTLOCK( &Sem->Protector );
1471         
1472         // Check if we have to block
1473         if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
1474         {
1475                 tThread *us;
1476                 SHORTLOCK( &glThreadListLock );
1477                 
1478                 // - Remove from active list
1479                 us = Threads_RemActive();
1480                 us->Next = NULL;
1481                 // - Mark as sleeping
1482                 us->Status = THREAD_STAT_SEMAPHORESLEEP;
1483                 us->WaitPointer = Sem;
1484                 us->RetStatus = AmmountToAdd;   // Use RetStatus as a temp variable
1485                 
1486                 // - Add to waiting
1487                 if(Sem->LastSignaling) {
1488                         Sem->LastSignaling->Next = us;
1489                         Sem->LastSignaling = us;
1490                 }
1491                 else {
1492                         Sem->Signaling = us;
1493                         Sem->LastSignaling = us;
1494                 }
1495                 
1496                 SHORTREL( &glThreadListLock );  
1497                 SHORTREL( &Sem->Protector );
1498                 while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
1499                 // We're only woken when there's something avaliable
1500                 us->WaitPointer = NULL;
1501                 
1502                 added = us->RetStatus;
1503                 
1504                 // Get the lock again
1505                 SHORTLOCK( &Sem->Protector );
1506         }
1507         // Non blocking
1508         else
1509         {
1510                 // Figure out how much we need to take off
1511                 if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
1512                         added = Sem->MaxValue - Sem->Value;
1513                 else
1514                         added = AmmountToAdd;
1515                 Sem->Value += added;
1516         }
1517         
1518         // While there are items avaliable, and there are thread waiting
1519         // wake the first thread and give it what it wants (or what's left)
1520         while( Sem->Value && Sem->Waiting )
1521         {
1522                 tThread *toWake = Sem->Waiting;
1523                 
1524                 Sem->Waiting = Sem->Waiting->Next;
1525                 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
1526                 if( Sem->Waiting == NULL )
1527                         Sem->LastWaiting = NULL;
1528                 
1529                 // Figure out how much to give
1530                 if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
1531                         given = toWake->RetStatus;
1532                 else
1533                         given = Sem->Value;
1534                 Sem->Value -= given;
1535                 
1536                 // Save the number we gave to the thread's status
1537                 toWake->RetStatus = given;
1538                 
1539                 // Wake the sleeper
1540                 SHORTLOCK( &glThreadListLock );
1541                 if( toWake->Status != THREAD_STAT_ACTIVE )
1542                         Threads_AddActive(toWake);
1543                 SHORTREL( &glThreadListLock );
1544         }
1545         SHORTREL( &Sem->Protector );
1546         
1547         return added;
1548 }
1549
1550 //
1551 // Get the current value of a semaphore
1552 //
1553 int Semaphore_GetValue(tSemaphore *Sem)
1554 {
1555         return Sem->Value;
1556 }
1557
1558 // === EXPORTS ===
1559 EXPORT(Threads_GetUID);
1560 EXPORT(Threads_GetGID);
1561 EXPORT(Mutex_Acquire);
1562 EXPORT(Mutex_Release);
1563 EXPORT(Mutex_IsLocked);
1564 EXPORT(Semaphore_Init);
1565 EXPORT(Semaphore_Wait);
1566 EXPORT(Semaphore_Signal);

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