Changed spinlock mechananisim
[tpg/acess2.git] / Kernel / threads.c
1 /*
2  * Acess2
3  * threads.c
4  * - Common Thread Control
5  */
6 #include <acess.h>
7 #include <threads.h>
8 #include <errno.h>
9
10 #define DEBUG_TRACE_TICKETS     0
11
12 // === CONSTANTS ===
13 #define DEFAULT_QUANTUM 10
14 #define DEFAULT_TICKETS 5
15 #define MAX_TICKETS             10
16 const enum eConfigTypes cCONFIG_TYPES[] = {
17         CFGT_HEAPSTR,   // e.g. CFG_VFS_CWD
18         CFGT_INT,       // e.g. CFG_VFS_MAXFILES
19         CFGT_NULL
20 };
21
22 // === IMPORTS ===
23 extern void     ArchThreads_Init(void);
24 extern void     Proc_Start(void);
25 extern tThread  *Proc_GetCurThread(void);
26 extern int      Proc_Clone(Uint *Err, Uint Flags);
27 extern void     Proc_CallFaultHandler(tThread *Thread);
28
29 // === PROTOTYPES ===
30 void    Threads_Init(void);
31  int    Threads_SetName(char *NewName);
32 char    *Threads_GetName(int ID);
33 void    Threads_SetTickets(tThread *Thread, int Num);
34 tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
35  int    Threads_WaitTID(int TID, int *status);
36 tThread *Threads_GetThread(Uint TID);
37 void    Threads_AddToDelete(tThread *Thread);
38 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread);
39 void    Threads_Exit(int TID, int Status);
40 void    Threads_Kill(tThread *Thread, int Status);
41 void    Threads_Yield(void);
42 void    Threads_Sleep(void);
43  int    Threads_Wake(tThread *Thread);
44 void    Threads_AddActive(tThread *Thread);
45  int    Threads_GetPID(void);
46  int    Threads_GetTID(void);
47 tUID    Threads_GetUID(void);
48  int    Threads_SetUID(Uint *Errno, tUID ID);
49 tGID    Threads_GetGID(void);
50  int    Threads_SetGID(Uint *Errno, tUID ID);
51 void    Threads_Dump(void);
52 void    Mutex_Acquire(tMutex *Mutex);
53 void    Mutex_Release(tMutex *Mutex);
54  int    Mutex_IsLocked(tMutex *Mutex);
55
56 // === GLOBALS ===
57 // -- Core Thread --
58 // Only used for the core kernel
59 tThread gThreadZero = {
60         Status: THREAD_STAT_ACTIVE,     // Status
61         ThreadName:     "ThreadZero",   // Name
62         Quantum: DEFAULT_QUANTUM,       // Default Quantum
63         Remaining:      DEFAULT_QUANTUM,        // Current Quantum
64         NumTickets:     DEFAULT_TICKETS // Number of tickets
65         };
66 // -- Processes --
67 // --- Locks ---
68 tShortSpinlock  glThreadListLock;       ///\note NEVER use a heap function while locked
69 // --- Current State ---
70 volatile int    giNumActiveThreads = 0;
71 //volatile int  giTotalTickets = 0;
72 volatile int    giFreeTickets = 0;
73 volatile Uint   giNextTID = 1;
74 // --- Thread Lists ---
75 tThread *gActiveThreads = NULL;         // Currently Running Threads
76 tThread *gSleepingThreads = NULL;       // Sleeping Threads
77 tThread *gDeleteThreads = NULL;         // Threads to delete
78  int    giNumCPUs = 1;
79
80 // === CODE ===
81 /**
82  * \fn void Threads_Init(void)
83  * \brief Initialse the thread list
84  */
85 void Threads_Init(void)
86 {
87         ArchThreads_Init();
88         
89         // Create Initial Task
90         gActiveThreads = &gThreadZero;
91         //giFreeTickets = gThreadZero.NumTickets;
92         giNumActiveThreads = 1;
93                 
94         Proc_Start();
95 }
96
97 /**
98  * \fn void Threads_SetName(char *NewName)
99  * \brief Sets the current thread's name
100  */
101 int Threads_SetName(char *NewName)
102 {
103         tThread *cur = Proc_GetCurThread();
104         char    *oldname = cur->ThreadName;
105         
106         cur->ThreadName = NULL;
107         
108         if( IsHeap(oldname) )   free( oldname );
109         
110         cur->ThreadName = strdup(NewName);
111         return 0;
112 }
113
114 /**
115  * \fn char *Threads_GetName(int ID)
116  * \brief Gets a thread's name
117  */
118 char *Threads_GetName(int ID)
119 {
120         if(ID == -1) {
121                 return Proc_GetCurThread()->ThreadName;
122         }
123         // TODO: Find a thread and get its name
124         return NULL;
125 }
126
127 /**
128  * \fn void Threads_SetTickets(tThread *Thread, int Num)
129  * \brief Sets the 'priority' of a task
130  */
131 void Threads_SetTickets(tThread *Thread, int Num)
132 {
133         if(Thread == NULL)
134                 Thread = Proc_GetCurThread();
135         if(Num < 0)     return;
136         if(Num > MAX_TICKETS)   Num = MAX_TICKETS;
137         
138         if( Thread != Proc_GetCurThread() ) {
139                 SHORTLOCK( &glThreadListLock );
140                 giFreeTickets -= Thread->NumTickets - Num;
141                 Thread->NumTickets = Num;
142                 SHORTREL( &glThreadListLock );
143         }
144         else
145                 Thread->NumTickets = Num;
146 }
147
148 /**
149  * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
150  */
151 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
152 {
153         tThread *cur, *new;
154          int    i;
155         cur = Proc_GetCurThread();
156         
157         new = malloc(sizeof(tThread));
158         if(new == NULL) {
159                 *Err = -ENOMEM;
160                 return NULL;
161         }
162         memcpy(new, cur, sizeof(tThread));
163         
164         new->CurCPU = -1;
165         new->Next = NULL;
166         memset( &new->IsLocked, 0, sizeof(new->IsLocked));
167         new->Status = THREAD_STAT_ACTIVE;
168         new->RetStatus = 0;
169         
170         // Get Thread ID
171         new->TID = giNextTID++;
172         new->PTID = cur->TID;
173         
174         // Clone Name
175         new->ThreadName = strdup(cur->ThreadName);
176         
177         // Set Thread Group ID (PID)
178         if(Flags & CLONE_VM)
179                 new->TGID = new->TID;
180         else
181                 new->TGID = cur->TGID;
182         
183         // Messages are not inherited
184         new->Messages = NULL;
185         new->LastMessage = NULL;
186         
187         // Set State
188         new->Remaining = new->Quantum = cur->Quantum;
189         new->NumTickets = cur->NumTickets;
190         
191         // Set Signal Handlers
192         new->CurFaultNum = 0;
193         new->FaultHandler = cur->FaultHandler;
194         
195         for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
196         {
197                 switch(cCONFIG_TYPES[i])
198                 {
199                 default:
200                         new->Config[i] = cur->Config[i];
201                         break;
202                 case CFGT_HEAPSTR:
203                         if(cur->Config[i])
204                                 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
205                         else
206                                 new->Config[i] = 0;
207                         break;
208                 }
209         }
210         
211         return new;
212 }
213
214 /**
215  * \fn Uint *Threads_GetCfgPtr(int Id)
216  */
217 Uint *Threads_GetCfgPtr(int Id)
218 {
219         if(Id < 0 || Id >= NUM_CFG_ENTRIES) {
220                 Warning("Threads_GetCfgPtr: Index %i is out of bounds", Id);
221                 return NULL;
222         }
223         
224         return &Proc_GetCurThread()->Config[Id];
225 }
226
227 /**
228  * \fn void Threads_WaitTID(int TID, int *status)
229  * \brief Wait for a task to change state
230  */
231 int Threads_WaitTID(int TID, int *status)
232 {       
233         // Any Child
234         if(TID == -1) {
235                 
236                 return -1;
237         }
238         
239         // Any peer/child thread
240         if(TID == 0) {
241                 
242                 return -1;
243         }
244         
245         // TGID = abs(TID)
246         if(TID < -1) {
247                 return -1;
248         }
249         
250         // Specific Thread
251         if(TID > 0) {
252                 tThread *t = Threads_GetThread(TID);
253                  int    initStatus = t->Status;
254                  int    ret;
255                 
256                 if(initStatus != THREAD_STAT_ZOMBIE) {
257                         while(t->Status == initStatus) {
258                                 Threads_Yield();
259                         }
260                 }
261                 
262                 ret = t->RetStatus;
263                 switch(t->Status)
264                 {
265                 case THREAD_STAT_ZOMBIE:
266                         t->Status = THREAD_STAT_DEAD;
267                         if(status)      *status = 0;
268                         Threads_AddToDelete( t );
269                         break;
270                 default:
271                         if(status)      *status = -1;
272                         break;
273                 }
274                 return ret;
275         }
276         
277         return -1;
278 }
279
280 /**
281  * \fn tThread *Threads_GetThread(Uint TID)
282  * \brief Gets a thread given its TID
283  * \param TID   Thread ID
284  */
285 tThread *Threads_GetThread(Uint TID)
286 {
287         tThread *thread;
288         
289         // Search Active List
290         for(thread = gActiveThreads;
291                 thread;
292                 thread = thread->Next)
293         {
294                 if(thread->TID == TID)
295                         return thread;
296         }
297         
298         // Search Sleeping List
299         for(thread = gSleepingThreads;
300                 thread;
301                 thread = thread->Next)
302         {
303                 if(thread->TID == TID)
304                         return thread;
305         }
306         
307         return NULL;
308 }
309
310 /**
311  * \fn void Threads_AddToDelete(tThread *Thread)
312  * \brief Adds a thread to the delete queue
313  */
314 void Threads_AddToDelete(tThread *Thread)
315 {
316         // Add to delete queue
317         if(gDeleteThreads) {
318                 Thread->Next = gDeleteThreads;
319                 gDeleteThreads = Thread;
320         } else {
321                 Thread->Next = NULL;
322                 gDeleteThreads = Thread;
323         }
324 }
325
326 /**
327  * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
328  * \brief Gets the previous entry in a thead linked list
329  */
330 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
331 {
332         tThread *ret;
333         // First Entry
334         if(*List == Thread) {
335                 return (tThread*)List;
336         } else {
337                 for(ret = *List;
338                         ret->Next && ret->Next != Thread;
339                         ret = ret->Next
340                         );
341                 // Error if the thread is not on the list
342                 if(!ret->Next || ret->Next != Thread) {
343                         return NULL;
344                 }
345         }
346         return ret;
347 }
348
349 /**
350  * \fn void Threads_Exit(int TID, int Status)
351  * \brief Exit the current process
352  */
353 void Threads_Exit(int TID, int Status)
354 {
355         if( TID == 0 )
356                 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
357         else
358                 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
359         // Halt forever, just in case
360         for(;;)
361                 HALT();
362 }
363
364 /**
365  * \fn void Threads_Kill(tThread *Thread, int Status)
366  * \brief Kill a thread
367  * \param Thread        Thread to kill
368  * \param Status        Status code to return to the parent
369  */
370 void Threads_Kill(tThread *Thread, int Status)
371 {
372         tThread *prev;
373         tMsg    *msg;
374         
375         // Kill all children
376         #if 0
377         {
378                 tThread *child;
379                 for(child = gActiveThreads;
380                         child;
381                         child = child->Next)
382                 {
383                         if(child->PTID == Thread->TID)
384                                 Threads_Kill(child, -1);
385                 }
386         }
387         #endif
388         
389         ///\note Double lock is needed due to overlap of lock areas
390         
391         // Lock thread (stop us recieving messages)
392         SHORTLOCK( &Thread->IsLocked );
393         
394         // Lock thread list
395         SHORTLOCK( &glThreadListLock );
396         
397         // Get previous thread on list
398         prev = Threads_int_GetPrev( &gActiveThreads, Thread );
399         if(!prev) {
400                 Warning("Proc_Exit - Current thread is not on the active queue");
401                 Thread->IsLocked.Lock = 0;      // We can't use SHORTREL as that starts IRQs again
402                 SHORTREL( &glThreadListLock );
403                 return;
404         }
405         
406         // Clear Message Queue
407         while( Thread->Messages )
408         {
409                 msg = Thread->Messages->Next;
410                 free( Thread->Messages );
411                 Thread->Messages = msg;
412         }
413         
414         Thread->Remaining = 0;  // Clear Remaining Quantum
415         Thread->Quantum = 0;    // Clear Quantum to indicate dead thread
416         prev->Next = Thread->Next;      // Remove from active
417         
418         giNumActiveThreads --;
419         if( Thread != Proc_GetCurThread() )
420                 giFreeTickets -= Thread->NumTickets;
421         //Log("Threads_Kill: giFreeTickets = %i", giFreeTickets);
422         
423         // Mark thread as a zombie
424         Thread->RetStatus = Status;
425         
426         // Don't Zombie if we are being killed as part of a tree
427         if(Status == -1)
428         {
429                 Thread->Status = THREAD_STAT_DEAD;
430                 Threads_AddToDelete( Thread );
431         } else {
432                 Thread->Status = THREAD_STAT_ZOMBIE;
433         }
434         
435         // Release spinlocks
436         Thread->IsLocked.Lock = 0;      // Released first so that it IS released
437         SHORTREL( &glThreadListLock );
438         
439         //Log("Thread %i went *hurk*", Thread->TID);
440         
441         if(Status != -1)        HALT();
442 }
443
444 /**
445  * \fn void Threads_Yield(void)
446  * \brief Yield remainder of timeslice
447  */
448 void Threads_Yield(void)
449 {
450         Proc_GetCurThread()->Remaining = 0;
451         HALT();
452 }
453
454 /**
455  * \fn void Threads_Sleep(void)
456  * \brief Take the current process off the run queue
457  */
458 void Threads_Sleep(void)
459 {
460         tThread *cur = Proc_GetCurThread();
461         tThread *thread;
462         
463         //Log_Log("Threads", "%i going to sleep", cur->TID);
464         
465         // Acquire Spinlock
466         SHORTLOCK( &glThreadListLock );
467         
468         // Get thread before current thread
469         thread = Threads_int_GetPrev( &gActiveThreads, cur );
470         if(!thread) {
471                 Warning("Threads_Sleep - Current thread is not on the active queue");
472                 Threads_Dump();
473                 SHORTREL( &glThreadListLock );
474                 return;
475         }
476         
477         // Don't sleep if there is a message waiting
478         if( cur->Messages ) {
479                 SHORTREL( &glThreadListLock );
480                 return;
481         }
482         
483         // Unset remaining timeslices (force a task switch on timer fire)
484         cur->Remaining = 0;
485         
486         // Remove from active list
487         thread->Next = cur->Next;
488         
489         // Add to Sleeping List (at the top)
490         cur->Next = gSleepingThreads;
491         gSleepingThreads = cur;
492         
493         // Reduce the active count & ticket count
494         giNumActiveThreads --;
495         // - No need to alter giFreeTickets (we're being executed)
496         
497         // Mark thread as sleeping
498         cur->Status = THREAD_STAT_SLEEPING;
499         
500         // Release Spinlock
501         SHORTREL( &glThreadListLock );
502         
503         while(cur->Status != THREAD_STAT_ACTIVE)        HALT();
504 }
505
506
507 /**
508  * \fn int Threads_Wake( tThread *Thread )
509  * \brief Wakes a sleeping/waiting thread up
510  * \param Thread        Thread to wake
511  * \return Boolean Failure (Returns ERRNO)
512  */
513 int Threads_Wake(tThread *Thread)
514 {
515         tThread *prev;
516         
517         if(!Thread)
518                 return -EINVAL;
519         
520         switch(Thread->Status)
521         {
522         case THREAD_STAT_ACTIVE:
523                 Log("Thread_Wake: Waking awake thread (%i)", Thread->TID);
524                 return -EALREADY;
525         case THREAD_STAT_SLEEPING:      // TODO: Comment better
526                 //Log_Log("Threads", "Waking %i (%p) from sleeping (CPU=%i)",
527                 //      Thread->TID, Thread, Thread->CurCPU);
528                 SHORTLOCK( &glThreadListLock );
529                 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
530                 prev->Next = Thread->Next;      // Remove from sleeping queue
531                 Thread->Next = gActiveThreads;  // Add to active queue
532                 gActiveThreads = Thread;
533                 giNumActiveThreads ++;
534                 // Thread can't be the current, so no need to check
535                 Thread->CurCPU = -1;
536                 giFreeTickets += Thread->NumTickets;
537                 #if DEBUG_TRACE_TICKETS
538                 Log("Threads_Wake: giFreeTickets = %i", giFreeTickets);
539                 #endif
540                 Thread->Status = THREAD_STAT_ACTIVE;
541                 SHORTREL( &glThreadListLock );
542                 return -EOK;
543         case THREAD_STAT_WAITING:
544                 Warning("Thread_Wake - Waiting threads are not currently supported");
545                 return -ENOTIMPL;
546         case THREAD_STAT_DEAD:
547                 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
548                 return -ENOTIMPL;
549         default:
550                 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
551                 return -EINTERNAL;
552         }
553 }
554
555 /**
556  * \brief Wake a thread given the TID
557  * \param TID   Thread ID to wake
558  * \return Boolean Faulure (errno)
559  */
560 int Threads_WakeTID(tTID TID)
561 {
562         tThread *thread = Threads_GetThread(TID);
563         if(!thread)
564                 return -ENOENT;
565         return Threads_Wake( thread );
566 }
567
568 /**
569  * \fn void Threads_AddActive(tThread *Thread)
570  * \brief Adds a thread to the active queue
571  */
572 void Threads_AddActive(tThread *Thread)
573 {
574         SHORTLOCK( &glThreadListLock );
575         Thread->Next = gActiveThreads;
576         gActiveThreads = Thread;
577         giNumActiveThreads ++;
578         // Thread can't be the current, so no need to check
579         giFreeTickets += Thread->NumTickets;
580         #if DEBUG_TRACE_TICKETS
581         Log("Threads_AddActive: giFreeTickets = %i", giFreeTickets);
582         #endif
583         SHORTREL( &glThreadListLock );
584 }
585
586 /**
587  * \fn void Threads_SetFaultHandler(Uint Handler)
588  * \brief Sets the signal handler for a signal
589  */
590 void Threads_SetFaultHandler(Uint Handler)
591 {       
592         Log_Log("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
593         Proc_GetCurThread()->FaultHandler = Handler;
594 }
595
596 /**
597  * \fn void Threads_Fault(int Num)
598  * \brief Calls a fault handler
599  */
600 void Threads_Fault(int Num)
601 {
602         tThread *thread = Proc_GetCurThread();
603         
604         Log_Log("Threads", "Threads_Fault: thread = %p", thread);
605         
606         if(!thread)     return ;
607         
608         Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
609         
610         switch(thread->FaultHandler)
611         {
612         case 0: // Panic?
613                 Threads_Kill(thread, -1);
614                 HALT();
615                 return ;
616         case 1: // Dump Core?
617                 Threads_Kill(thread, -1);
618                 HALT();
619                 return ;
620         }
621         
622         // Double Fault? Oh, F**k
623         if(thread->CurFaultNum != 0) {
624                 Threads_Kill(thread, -1);       // For now, just kill
625                 HALT();
626         }
627         
628         thread->CurFaultNum = Num;
629         
630         Proc_CallFaultHandler(thread);
631 }
632
633 // --- Process Structure Access Functions ---
634 tPID Threads_GetPID(void)
635 {
636         return Proc_GetCurThread()->TGID;
637 }
638 tTID Threads_GetTID(void)
639 {
640         return Proc_GetCurThread()->TID;
641 }
642 tUID Threads_GetUID(void)
643 {
644         return Proc_GetCurThread()->UID;
645 }
646 tGID Threads_GetGID(void)
647 {
648         return Proc_GetCurThread()->GID;
649 }
650
651 int Threads_SetUID(Uint *Errno, tUID ID)
652 {
653         tThread *t = Proc_GetCurThread();
654         if( t->UID != 0 ) {
655                 *Errno = -EACCES;
656                 return -1;
657         }
658         Log("Threads_SetUID - Setting User ID to %i", ID);
659         t->UID = ID;
660         return 0;
661 }
662
663 int Threads_SetGID(Uint *Errno, tGID ID)
664 {
665         tThread *t = Proc_GetCurThread();
666         if( t->UID != 0 ) {
667                 *Errno = -EACCES;
668                 return -1;
669         }
670         Log("Threads_SetGID - Setting Group ID to %i", ID);
671         t->GID = ID;
672         return 0;
673 }
674
675 /**
676  * \fn void Threads_Dump(void)
677  * \brief Dums a list of currently running threads
678  */
679 void Threads_Dump(void)
680 {
681         tThread *thread;
682         
683         Log("Active Threads:");
684         for(thread=gActiveThreads;thread;thread=thread->Next)
685         {
686                 Log(" %i (%i) - %s (CPU %i)",
687                         thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
688                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
689                 Log("  KStack 0x%x", thread->KernelStack);
690         }
691         Log("Sleeping Threads:");
692         for(thread=gSleepingThreads;thread;thread=thread->Next)
693         {
694                 Log(" %i (%i) - %s",
695                         thread->TID, thread->TGID, thread->ThreadName);
696                 Log("  %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
697                 Log("  KStack 0x%x", thread->KernelStack);
698         }
699 }
700
701 /**
702  * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
703  * \brief Gets the next thread to run
704  * \param CPU   Current CPU
705  * \param Last  The thread the CPU was running
706  */
707 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
708 {
709         tThread *thread;
710          int    ticket;
711          int    number;
712         
713         // TODO: Enable the code to tell if the current CPU has the lock or
714         //       another does.
715         
716         // Check if the thread list is locked by other code
717         // - If so, don't switch (give it a chance to complete)
718         if( IS_LOCKED(&glThreadListLock) )
719                 return Last;
720         
721         // Clear Delete Queue
722         while(gDeleteThreads)
723         {
724                 thread = gDeleteThreads->Next;
725                 if( IS_LOCKED(&gDeleteThreads->IsLocked) ) {    // Only free if structure is unused
726                         gDeleteThreads->Status = THREAD_STAT_NULL;
727                         free( gDeleteThreads );
728                 }
729                 gDeleteThreads = thread;
730         }
731         
732         // No active threads, just take a nap
733         if(giNumActiveThreads == 0) {
734                 #if DEBUG_TRACE_TICKETS
735                 Log("No active threads");
736                 #endif
737                 return NULL;
738         }
739         
740         // Lock thread list
741         // - HLT lock (Used because only another CPU can obtain the lock,
742         //   but it has a potentially long lock period)
743         // - Well, this CPU can obtain the lock, but that is aliveviated by
744         //   the above.
745         SHORTLOCK( &glThreadListLock );
746         
747         // Special case: 1 thread
748         if(giNumActiveThreads == 1) {
749                 if( gActiveThreads->CurCPU == -1 )
750                         gActiveThreads->CurCPU = CPU;
751                 SHORTREL( &glThreadListLock );
752                 if( gActiveThreads->CurCPU == CPU )
753                         return gActiveThreads;
754                 return NULL;    // CPU has nothing to do
755         }
756         
757         // Allow the old thread to be scheduled again
758         if( Last ) {
759                 if( Last->Status == THREAD_STAT_ACTIVE ) {
760                         giFreeTickets += Last->NumTickets;
761                         #if DEBUG_TRACE_TICKETS
762                         LogF(" CPU %i released %p (%s) into the pool (%i tickets in pool)\n",
763                                 CPU, Last, Last->ThreadName, Last->NumTickets);
764                         #endif
765                 }
766                 #if DEBUG_TRACE_TICKETS
767                 else
768                         LogF(" %p (%s)->Status = %i\n", Last, Last->ThreadName, Last->Status);
769                 #endif
770                 Last->CurCPU = -1;
771         }
772         
773         #if 1
774         number = 0;
775         for(thread=gActiveThreads;thread;thread=thread->Next) {
776                 if(thread->CurCPU >= 0) continue;
777                 number += thread->NumTickets;
778         }
779         if(number != giFreeTickets) {
780                 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
781                         giFreeTickets, number, CPU);
782         }
783         #endif
784         
785         // No free tickets (all tasks delegated to cores)
786         if( giFreeTickets == 0 ) {
787                 SHORTREL(&glThreadListLock);
788                 return NULL;
789         }
790         
791         // Get the ticket number
792         ticket = number = rand() % giFreeTickets;
793         
794         // Find the next thread
795         for(thread=gActiveThreads;thread;thread=thread->Next)
796         {
797                 if(thread->CurCPU >= 0) continue;
798                 if(thread->NumTickets > number) break;
799                 number -= thread->NumTickets;
800         }
801         // Error Check
802         if(thread == NULL)
803         {
804                 number = 0;
805                 for(thread=gActiveThreads;thread;thread=thread->Next) {
806                         if(thread->CurCPU >= 0) continue;
807                         number += thread->NumTickets;
808                 }
809                 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
810                         giFreeTickets, number);
811         }
812         #if DEBUG_TRACE_TICKETS
813         LogF(" CPU%i giFreeTickets = %i\n", CPU, giFreeTickets);
814         #endif
815         
816         // Make the new thread non-schedulable
817         giFreeTickets -= thread->NumTickets;    
818         thread->CurCPU = CPU;
819         
820         //Threads_Dump();
821         #if DEBUG_TRACE_TICKETS
822         LogF(" CPU%i giFreeTickets = %i, giving %p (%s CPU=%i)\n",
823                 CPU, giFreeTickets, thread, thread->ThreadName, thread->CurCPU);
824         #endif
825         
826         SHORTREL( &glThreadListLock );
827         
828         return thread;
829 }
830
831 /**
832  * \fn void Threads_SegFault(tVAddr Addr)
833  * \brief Called when a Segment Fault occurs
834  */
835 void Threads_SegFault(tVAddr Addr)
836 {
837         Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
838         Threads_Fault( 1 );
839         //Threads_Exit( 0, -1 );
840 }
841
842 /**
843  * \brief heavy mutex
844  */
845 void Mutex_Acquire(tMutex *Mutex)
846 {
847         tThread *us = Proc_GetCurThread();
848         tThread *prev;
849         
850         // Get protector
851         SHORTLOCK( &Mutex->Protector );
852         
853         //Log("Mutex_Acquire: (%p)", Mutex);
854         
855         // Check if the lock is already held
856         if( Mutex->Owner ) {
857                 SHORTLOCK( &glThreadListLock );
858                 // - Remove from active list
859                 us->Remaining = 0;
860                 prev = Threads_int_GetPrev(&gActiveThreads, us);
861                 prev->Next = us->Next;
862                 giNumActiveThreads --;
863                 us->Status = THREAD_STAT_SLEEPING;
864                 
865                 // - Add to waiting
866                 if(Mutex->LastWaiting) {
867                         Mutex->LastWaiting->Next = us;
868                         Mutex->LastWaiting = us;
869                 }
870                 else {
871                         Mutex->Waiting = us;
872                         Mutex->LastWaiting = us;
873                 }
874                 SHORTREL( &glThreadListLock );
875                 SHORTREL( &Mutex->Protector );
876                 while(us->Status == THREAD_STAT_SLEEPING)       HALT();
877                 // We're only woken when we get the lock
878         }
879         // Ooh, let's take it!
880         else {
881                 Mutex->Owner = us;
882                 SHORTREL( &Mutex->Protector );
883         }
884 }
885
886 /**
887  * \brief Release a held spinlock
888  */
889 void Mutex_Release(tMutex *Mutex)
890 {
891         SHORTLOCK( &Mutex->Protector );
892         //Log("Mutex_Release: (%p)", Mutex);
893         if( Mutex->Waiting ) {
894                 Mutex->Owner = Mutex->Waiting;  // Set owner
895                 Mutex->Waiting = Mutex->Waiting->Next;  // Next!
896                 // Wake new owner
897                 Mutex->Owner->Status = THREAD_STAT_ACTIVE;
898                 Threads_AddActive(Mutex->Owner);
899                 Log("Mutex %p Woke %p", Mutex, Mutex->Owner);
900         }
901         else {
902                 Mutex->Owner = NULL;
903         }
904         SHORTREL( &Mutex->Protector );
905 }
906
907 int Mutex_IsLocked(tMutex *Mutex)
908 {
909         return Mutex->Owner != NULL;
910 }
911
912 // === EXPORTS ===
913 EXPORT(Threads_GetUID);

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