4 * - Common Thread Control
10 #define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
11 #define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
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
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);
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_DelFromQueue(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 Threads_DumpActive(void);
55 void Mutex_Acquire(tMutex *Mutex);
56 void Mutex_Release(tMutex *Mutex);
57 int Mutex_IsLocked(tMutex *Mutex);
61 // Only used for the core kernel
62 tThread gThreadZero = {
63 Status: THREAD_STAT_ACTIVE, // Status
64 ThreadName: "ThreadZero", // Name
65 Quantum: DEFAULT_QUANTUM, // Default Quantum
66 Remaining: DEFAULT_QUANTUM, // Current Quantum
67 NumTickets: DEFAULT_TICKETS // Number of tickets
71 tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
72 // --- Current State ---
73 volatile int giNumActiveThreads = 0; // Number of threads on the active queue
74 volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads
75 volatile Uint giNextTID = 1; // Next TID to allocate
76 // --- Thread Lists ---
77 tThread *gAllThreads = NULL; // All allocated threads
78 tThread *gActiveThreads = NULL; // Currently Running Threads
79 tThread *gSleepingThreads = NULL; // Sleeping Threads
80 tThread *gDeleteThreads = NULL; // Threads to delete
81 int giNumCPUs = 1; // Number of CPUs
82 BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF)
86 * \fn void Threads_Init(void)
87 * \brief Initialse the thread list
89 void Threads_Init(void)
93 // Create Initial Task
94 gActiveThreads = &gThreadZero;
95 gAllThreads = &gThreadZero;
96 //giFreeTickets = gThreadZero.NumTickets; // Not needed, as ThreadZero is scheduled
97 giNumActiveThreads = 1;
103 * \fn void Threads_SetName(char *NewName)
104 * \brief Sets the current thread's name
105 * \param NewName New name for the thread
106 * \return Boolean Failure
108 int Threads_SetName(char *NewName)
110 tThread *cur = Proc_GetCurThread();
111 char *oldname = cur->ThreadName;
113 // NOTE: There is a possibility of non-thread safety here
114 // A thread could read the current name pointer before it is zeroed
116 cur->ThreadName = NULL;
118 if( IsHeap(oldname) ) free( oldname );
120 cur->ThreadName = strdup(NewName);
125 * \fn char *Threads_GetName(int ID)
126 * \brief Gets a thread's name
127 * \param ID Thread ID (-1 indicates current thread)
128 * \return Pointer to name
129 * \retval NULL Failure
131 char *Threads_GetName(tTID ID)
134 return Proc_GetCurThread()->ThreadName;
136 return Threads_GetThread(ID)->ThreadName;
140 * \fn void Threads_SetTickets(tThread *Thread, int Num)
141 * \brief Sets the 'priority' of a task
142 * \param Thread Thread to update ticket count (NULL means current thread)
143 * \param Num New ticket count (must be >= 0, clipped to \a MAX_TICKETS)
145 void Threads_SetTickets(tThread *Thread, int Num)
147 // Get current thread
148 if(Thread == NULL) Thread = Proc_GetCurThread();
151 if(Num > MAX_TICKETS) Num = MAX_TICKETS;
153 // If this isn't the current thread, we need to lock
154 if( Thread != Proc_GetCurThread() ) {
155 SHORTLOCK( &glThreadListLock );
156 giFreeTickets -= Thread->NumTickets - Num;
157 Thread->NumTickets = Num;
158 #if DEBUG_TRACE_TICKETS
159 Log("Threads_SetTickets: new giFreeTickets = %i", giFreeTickets);
161 SHORTREL( &glThreadListLock );
164 Thread->NumTickets = Num;
168 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
169 * \brief Clone the TCB of the current thread
170 * \param Err Error pointer
171 * \param Flags Flags for something... (What is this for?)
173 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
177 cur = Proc_GetCurThread();
179 // Allocate and duplicate
180 new = malloc(sizeof(tThread));
185 memcpy(new, cur, sizeof(tThread));
189 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
190 new->Status = THREAD_STAT_PREINIT;
194 new->TID = giNextTID++;
198 new->ThreadName = strdup(cur->ThreadName);
200 // Set Thread Group ID (PID)
202 new->TGID = new->TID;
204 new->TGID = cur->TGID;
206 // Messages are not inherited
207 new->Messages = NULL;
208 new->LastMessage = NULL;
211 new->Remaining = new->Quantum = cur->Quantum;
212 new->NumTickets = cur->NumTickets;
214 // Set Signal Handlers
215 new->CurFaultNum = 0;
216 new->FaultHandler = cur->FaultHandler;
218 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
220 switch(cCONFIG_TYPES[i])
223 new->Config[i] = cur->Config[i];
227 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
234 // Maintain a global list of threads
235 SHORTLOCK( &glThreadListLock );
236 new->GlobalPrev = NULL; // Protect against bugs
237 new->GlobalNext = gAllThreads;
239 SHORTREL( &glThreadListLock );
245 * \brief Get a configuration pointer from the Per-Thread data area
246 * \param ID Config slot ID
247 * \return Pointer at ID
249 Uint *Threads_GetCfgPtr(int ID)
251 if(ID < 0 || ID >= NUM_CFG_ENTRIES) {
252 Warning("Threads_GetCfgPtr: Index %i is out of bounds", ID);
256 return &Proc_GetCurThread()->Config[ID];
260 * \brief Wait for a task to change state
261 * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
262 * \param Status Thread return status
263 * \return TID of child that changed state
265 tTID Threads_WaitTID(int TID, int *Status)
269 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
273 // Any peer/child thread
275 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
281 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
287 tThread *t = Threads_GetThread(TID);
288 int initStatus = t->Status;
291 // Wait for the thread to die!
292 if(initStatus != THREAD_STAT_ZOMBIE) {
293 // TODO: Handle child also being suspended if wanted
294 while(t->Status != THREAD_STAT_ZOMBIE) {
296 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
297 Threads_GetTID(), t->TID, t->Status);
302 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
303 Threads_GetTID(), t->TID, t->Status);
307 case THREAD_STAT_ZOMBIE:
309 t->Status = THREAD_STAT_DEAD;
310 // TODO: Child return value?
311 if(Status) *Status = t->RetStatus;
312 // add to delete queue
313 Threads_AddToDelete( t );
316 if(Status) *Status = -1;
326 * \brief Gets a thread given its TID
327 * \param TID Thread ID
328 * \return Thread pointer
330 tThread *Threads_GetThread(Uint TID)
334 // Search global list
335 for(thread = gAllThreads;
337 thread = thread->GlobalNext)
339 if(thread->TID == TID)
343 Log("Unable to find TID %i on main list\n", TID);
349 * \brief Adds a thread to the delete queue
350 * \param Thread Thread to delete
352 void Threads_AddToDelete(tThread *Thread)
354 // Add to delete queue
355 // TODO: Is locking needed?
357 Thread->Next = gDeleteThreads;
358 gDeleteThreads = Thread;
361 gDeleteThreads = Thread;
366 * \brief Deletes an entry from a list
367 * \param List Pointer to the list head
368 * \param Thread Thread to find
371 tThread *Threads_int_DelFromQueue(tThread **List, tThread *Thread)
373 tThread *ret, *prev = NULL;
376 ret && ret != Thread;
377 prev = ret, ret = ret->Next
380 // Is the thread on the list
382 //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
387 *List = Thread->Next;
388 //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
391 prev->Next = Thread->Next;
392 //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
399 * \brief Exit the current process (or another?)
400 * \param TID Thread ID to kill
401 * \param Status Exit status
403 void Threads_Exit(int TID, int Status)
406 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
408 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
410 // Halt forever, just in case
415 * \fn void Threads_Kill(tThread *Thread, int Status)
416 * \brief Kill a thread
417 * \param Thread Thread to kill
418 * \param Status Status code to return to the parent
420 void Threads_Kill(tThread *Thread, int Status)
424 // TODO: Kill all children
428 // TODO: I should keep a .Parent pointer, and a .Children list
429 for(child = gActiveThreads;
433 if(child->PTID == Thread->TID)
434 Threads_Kill(child, -1);
439 ///\note Double lock is needed due to overlap of lock areas
441 // Lock thread (stop us recieving messages)
442 SHORTLOCK( &Thread->IsLocked );
444 // Clear Message Queue
445 while( Thread->Messages )
447 msg = Thread->Messages->Next;
448 free( Thread->Messages );
449 Thread->Messages = msg;
453 SHORTLOCK( &glThreadListLock );
455 // Delete from active list
456 if( !Threads_int_DelFromQueue( &gActiveThreads, Thread ) )
458 Warning("Proc_Exit - Current thread is not on the active queue");
459 SHORTREL( &glThreadListLock );
460 SHORTREL( &Thread->IsLocked );
464 // Ensure that we are not rescheduled
465 Thread->Remaining = 0; // Clear Remaining Quantum
466 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
468 // Update bookkeeping
469 giNumActiveThreads --;
470 if( Thread != Proc_GetCurThread() )
471 giFreeTickets -= Thread->NumTickets;
474 Thread->RetStatus = Status;
476 // Don't Zombie if we are being killed because our parent is
479 Thread->Status = THREAD_STAT_DEAD;
480 Threads_AddToDelete( Thread );
482 Thread->Status = THREAD_STAT_ZOMBIE;
484 Threads_Wake( Thread->Parent );
487 Log("Thread %i went *hurk* (%i)", Thread->TID, Thread->Status);
490 SHORTREL( &glThreadListLock );
491 SHORTREL( &Thread->IsLocked ); // TODO: We may not actually be released...
501 * \brief Yield remainder of the current thread's timeslice
503 void Threads_Yield(void)
505 tThread *thread = Proc_GetCurThread();
506 thread->Remaining = 0;
507 //while(thread->Remaining == 0)
512 * \fn void Threads_Sleep(void)
513 * \brief Take the current process off the run queue
515 void Threads_Sleep(void)
517 tThread *cur = Proc_GetCurThread();
520 SHORTLOCK( &glThreadListLock );
522 // Don't sleep if there is a message waiting
523 if( cur->Messages ) {
524 SHORTREL( &glThreadListLock );
528 // Remove us from running queue
530 // Mark thread as sleeping
531 cur->Status = THREAD_STAT_SLEEPING;
533 // Add to Sleeping List (at the top)
534 cur->Next = gSleepingThreads;
535 gSleepingThreads = cur;
538 #if DEBUG_TRACE_STATE
539 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
543 SHORTREL( &glThreadListLock );
545 while(cur->Status != THREAD_STAT_ACTIVE) HALT();
550 * \fn int Threads_Wake( tThread *Thread )
551 * \brief Wakes a sleeping/waiting thread up
552 * \param Thread Thread to wake
553 * \return Boolean Failure (Returns ERRNO)
554 * \warning This should ONLY be called with task switches disabled
556 int Threads_Wake(tThread *Thread)
561 switch(Thread->Status)
563 case THREAD_STAT_ACTIVE:
564 Log("Thread_Wake: Waking awake thread (%i)", Thread->TID);
567 case THREAD_STAT_SLEEPING:
568 SHORTLOCK( &glThreadListLock );
569 // Remove from sleeping queue
570 Threads_int_DelFromQueue(&gSleepingThreads, Thread);
572 Threads_AddActive( Thread );
574 #if DEBUG_TRACE_STATE
575 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
577 SHORTREL( &glThreadListLock );
580 case THREAD_STAT_WAITING:
581 Warning("Thread_Wake - Waiting threads are not currently supported");
584 case THREAD_STAT_DEAD:
585 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
589 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
595 * \brief Wake a thread given the TID
596 * \param TID Thread ID to wake
597 * \return Boolean Faulure (errno)
599 int Threads_WakeTID(tTID TID)
601 tThread *thread = Threads_GetThread(TID);
605 ret = Threads_Wake( thread );
606 //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
611 * \brief Adds a thread to the active queue
613 void Threads_AddActive(tThread *Thread)
615 SHORTLOCK( &glThreadListLock );
620 for( t = gActiveThreads; t; t = t->Next )
623 Panic("Threads_AddActive: Attempting a double add of TID %i (0x%x)",
624 Thread->TID, __builtin_return_address(0));
627 if(t->Status != THREAD_STAT_ACTIVE) {
628 Panic("Threads_AddActive: TID %i status != THREAD_STAT_ACTIVE",
636 Thread->Status = THREAD_STAT_ACTIVE;
638 // Add to active list
639 Thread->Next = gActiveThreads;
640 gActiveThreads = Thread;
642 // Update bookkeeping
643 giNumActiveThreads ++;
644 giFreeTickets += Thread->NumTickets;
646 #if DEBUG_TRACE_TICKETS
647 Log("Threads_AddActive: %p %i (%s) added, new giFreeTickets = %i",
648 Thread, Thread->TID, Thread->ThreadName, giFreeTickets);
650 SHORTREL( &glThreadListLock );
654 * \brief Removes the current thread from the active queue
655 * \warning This should ONLY be called with task switches disabled
656 * \return Current thread pointer
658 tThread *Threads_RemActive(void)
660 tThread *ret = Proc_GetCurThread();
662 SHORTLOCK( &glThreadListLock );
664 // Delete from active queue
665 if( !Threads_int_DelFromQueue(&gActiveThreads, ret) ) {
666 SHORTREL( &glThreadListLock );
673 giNumActiveThreads --;
674 // no need to decrement tickets, scheduler did it for us
676 #if DEBUG_TRACE_TICKETS
677 Log("Threads_RemActive: %p %i (%s) removed, giFreeTickets = %i",
678 ret, ret->TID, ret->ThreadName, giFreeTickets);
681 SHORTREL( &glThreadListLock );
687 * \fn void Threads_SetFaultHandler(Uint Handler)
688 * \brief Sets the signal handler for a signal
690 void Threads_SetFaultHandler(Uint Handler)
692 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
693 Proc_GetCurThread()->FaultHandler = Handler;
697 * \fn void Threads_Fault(int Num)
698 * \brief Calls a fault handler
700 void Threads_Fault(int Num)
702 tThread *thread = Proc_GetCurThread();
704 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
708 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
710 switch(thread->FaultHandler)
713 Threads_Kill(thread, -1);
716 case 1: // Dump Core?
717 Threads_Kill(thread, -1);
722 // Double Fault? Oh, F**k
723 if(thread->CurFaultNum != 0) {
724 Threads_Kill(thread, -1); // For now, just kill
728 thread->CurFaultNum = Num;
730 Proc_CallFaultHandler(thread);
733 // --- Process Structure Access Functions ---
734 tPID Threads_GetPID(void)
736 return Proc_GetCurThread()->TGID;
738 tTID Threads_GetTID(void)
740 return Proc_GetCurThread()->TID;
742 tUID Threads_GetUID(void)
744 return Proc_GetCurThread()->UID;
746 tGID Threads_GetGID(void)
748 return Proc_GetCurThread()->GID;
751 int Threads_SetUID(Uint *Errno, tUID ID)
753 tThread *t = Proc_GetCurThread();
758 Log_Debug("Threads", "TID %i's UID set to %i", t->TID, ID);
763 int Threads_SetGID(Uint *Errno, tGID ID)
765 tThread *t = Proc_GetCurThread();
770 Log_Debug("Threads", "TID %i's GID set to %i", t->TID, ID);
776 * \fn void Threads_Dump(void)
777 * \brief Dumps a list of currently running threads
779 void Threads_Dump(void)
783 Log("--- Thread Dump ---");
784 Log("Active Threads: (%i reported)", giNumActiveThreads);
785 for(thread=gActiveThreads;thread;thread=thread->Next)
787 Log(" %i (%i) - %s (CPU %i)",
788 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
789 if(thread->Status != THREAD_STAT_ACTIVE)
790 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
791 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
792 Log(" KStack 0x%x", thread->KernelStack);
796 for(thread=gAllThreads;thread;thread=thread->GlobalNext)
798 Log(" %i (%i) - %s (CPU %i)",
799 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
800 Log(" State %i", thread->Status);
801 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
802 Log(" KStack 0x%x", thread->KernelStack);
806 * \fn void Threads_Dump(void)
808 void Threads_DumpActive(void)
812 Log("Active Threads:");
813 for(thread=gActiveThreads;thread;thread=thread->Next)
815 Log(" %i (%i) - %s (CPU %i)",
816 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
817 if(thread->Status != THREAD_STAT_ACTIVE)
818 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
819 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
820 Log(" KStack 0x%x", thread->KernelStack);
825 * \brief Gets the next thread to run
826 * \param CPU Current CPU
827 * \param Last The thread the CPU was running
829 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
835 // If this CPU has the lock, we must let it complete
836 if( CPU_HAS_LOCK( &glThreadListLock ) )
839 // Don't change threads if the current CPU has switches disabled
840 if( gaThreads_NoTaskSwitch[CPU] )
845 SHORTLOCK( &glThreadListLock );
847 // Clear Delete Queue
848 // - I should probably put this in a worker thread to avoid calling free() in the scheduler
849 while(gDeleteThreads)
851 thread = gDeleteThreads->Next;
852 if( IS_LOCKED(&gDeleteThreads->IsLocked) ) { // Only free if structure is unused
854 gDeleteThreads->Status = THREAD_STAT_DEAD;
856 if( IsHeap(gDeleteThreads->ThreadName) )
857 free(gDeleteThreads->ThreadName);
858 // Remove from global list
859 if( gDeleteThreads == gAllThreads )
860 gAllThreads = gDeleteThreads->GlobalNext;
862 gDeleteThreads->GlobalPrev->GlobalNext = gDeleteThreads->GlobalNext;
863 free( gDeleteThreads );
865 gDeleteThreads = thread;
868 // No active threads, just take a nap
869 if(giNumActiveThreads == 0) {
870 SHORTREL( &glThreadListLock );
871 #if DEBUG_TRACE_TICKETS
872 Log("No active threads");
877 // Special case: 1 thread
878 if(giNumActiveThreads == 1) {
879 if( gActiveThreads->CurCPU == -1 )
880 gActiveThreads->CurCPU = CPU;
882 SHORTREL( &glThreadListLock );
884 if( gActiveThreads->CurCPU == CPU )
885 return gActiveThreads;
887 return NULL; // CPU has nothing to do
890 // Allow the old thread to be scheduled again
892 if( Last->Status == THREAD_STAT_ACTIVE ) {
893 giFreeTickets += Last->NumTickets;
894 #if DEBUG_TRACE_TICKETS
895 LogF(" CPU %i released %p (%i %s) into the pool (%i tickets in pool)\n",
896 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets);
899 #if DEBUG_TRACE_TICKETS
901 LogF(" CPU %i released %p (%i %s)->Status = %i (Released)\n",
902 CPU, Last, Last->TID, Last->ThreadName, Last->Status);
907 #if DEBUG_TRACE_TICKETS
908 //Threads_DumpActive();
913 for(thread = gActiveThreads; thread; thread = thread->Next) {
914 if(thread->CurCPU >= 0) continue;
915 if(thread->Status != THREAD_STAT_ACTIVE)
916 Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
917 thread, thread->TID, thread->ThreadName, thread->Status);
918 if(thread->Next == thread) {
919 Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
920 thread, thread->TID, thread->ThreadName, thread->Status);
922 number += thread->NumTickets;
924 if(number != giFreeTickets) {
925 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
926 giFreeTickets, number, CPU);
930 // No free tickets (all tasks delegated to cores)
931 if( giFreeTickets == 0 ) {
932 SHORTREL(&glThreadListLock);
936 // Get the ticket number
937 ticket = number = rand() % giFreeTickets;
939 // Find the next thread
940 for(thread=gActiveThreads;thread;thread=thread->Next)
942 if(thread->CurCPU >= 0) continue;
943 if(thread->NumTickets > number) break;
944 number -= thread->NumTickets;
950 for(thread=gActiveThreads;thread;thread=thread->Next) {
951 if(thread->CurCPU >= 0) continue;
952 number += thread->NumTickets;
954 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
955 giFreeTickets, number);
957 #if DEBUG_TRACE_TICKETS
958 LogF(" CPU%i giFreeTickets = %i\n", CPU, giFreeTickets);
961 // Make the new thread non-schedulable
962 giFreeTickets -= thread->NumTickets;
963 thread->CurCPU = CPU;
966 #if DEBUG_TRACE_TICKETS
967 LogF(" CPU%i giFreeTickets = %i, giving %p (%i %s CPU=%i)\n",
968 CPU, giFreeTickets, thread, thread->TID, thread->ThreadName, thread->CurCPU);
971 SHORTREL( &glThreadListLock );
977 * \fn void Threads_SegFault(tVAddr Addr)
978 * \brief Called when a Segment Fault occurs
980 void Threads_SegFault(tVAddr Addr)
982 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
984 //Threads_Exit( 0, -1 );
988 * \brief Acquire a heavy mutex
989 * \param Mutex Mutex to acquire
991 * This type of mutex checks if the mutex is avaliable, and acquires it
992 * if it is. Otherwise, the current thread is added to the mutex's wait
993 * queue and the thread suspends. When the holder of the mutex completes,
994 * the oldest thread (top thread) on the queue is given the lock and
997 void Mutex_Acquire(tMutex *Mutex)
999 tThread *us = Proc_GetCurThread();
1002 SHORTLOCK( &Mutex->Protector );
1004 //Log("Mutex_Acquire: (%p)", Mutex);
1006 // Check if the lock is already held
1007 if( Mutex->Owner ) {
1008 SHORTLOCK( &glThreadListLock );
1009 // - Remove from active list
1010 us = Threads_RemActive();
1011 // - Mark as sleeping
1012 us->Status = THREAD_STAT_OFFSLEEP;
1015 if(Mutex->LastWaiting) {
1016 Mutex->LastWaiting->Next = us;
1017 Mutex->LastWaiting = us;
1020 Mutex->Waiting = us;
1021 Mutex->LastWaiting = us;
1023 SHORTREL( &glThreadListLock );
1024 SHORTREL( &Mutex->Protector );
1025 while(us->Status == THREAD_STAT_OFFSLEEP) Threads_Yield();
1026 // We're only woken when we get the lock
1028 // Ooh, let's take it!
1031 SHORTREL( &Mutex->Protector );
1036 * \brief Release a held mutex
1037 * \param Mutex Mutex to release
1039 void Mutex_Release(tMutex *Mutex)
1041 SHORTLOCK( &Mutex->Protector );
1042 //Log("Mutex_Release: (%p)", Mutex);
1043 if( Mutex->Waiting ) {
1044 Mutex->Owner = Mutex->Waiting; // Set owner
1045 Mutex->Waiting = Mutex->Waiting->Next; // Next!
1046 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
1047 // 2010-10-02 21:50 - Comemerating the death of the longest single
1048 // blocker in the Acess2 history. REMEMBER TO
1049 // FUCKING MAINTAIN YOUR FUCKING LISTS DIPWIT
1050 if( Mutex->LastWaiting == Mutex->Owner )
1051 Mutex->LastWaiting = NULL;
1054 SHORTLOCK( &glThreadListLock );
1055 if( Mutex->Owner->Status != THREAD_STAT_ACTIVE )
1056 Threads_AddActive(Mutex->Owner);
1057 SHORTREL( &glThreadListLock );
1060 Mutex->Owner = NULL;
1062 SHORTREL( &Mutex->Protector );
1066 * \brief Is this mutex locked?
1067 * \param Mutex Mutex pointer
1069 int Mutex_IsLocked(tMutex *Mutex)
1071 return Mutex->Owner != NULL;
1075 EXPORT(Threads_GetUID);
1076 EXPORT(Threads_GetGID);
1077 EXPORT(Mutex_Acquire);
1078 EXPORT(Mutex_Release);
1079 EXPORT(Mutex_IsLocked);