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

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