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_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);
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
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
84 * \fn void Threads_Init(void)
85 * \brief Initialse the thread list
87 void Threads_Init(void)
91 // Create Initial Task
92 gActiveThreads = &gThreadZero;
93 gAllThreads = &gThreadZero;
94 //giFreeTickets = gThreadZero.NumTickets; // Not needed, as ThreadZero is scheduled
95 giNumActiveThreads = 1;
101 * \fn void Threads_SetName(char *NewName)
102 * \brief Sets the current thread's name
103 * \param NewName New name for the thread
104 * \return Boolean Failure
106 int Threads_SetName(char *NewName)
108 tThread *cur = Proc_GetCurThread();
109 char *oldname = cur->ThreadName;
111 cur->ThreadName = NULL;
113 if( IsHeap(oldname) ) free( oldname );
115 cur->ThreadName = strdup(NewName);
120 * \fn char *Threads_GetName(int ID)
121 * \brief Gets a thread's name
122 * \param ID Thread ID (-1 indicates current thread)
123 * \return Pointer to name
124 * \retval NULL Failure
126 char *Threads_GetName(tTID ID)
129 return Proc_GetCurThread()->ThreadName;
131 // TODO: Find a thread and get its name
136 * \fn void Threads_SetTickets(tThread *Thread, int Num)
137 * \brief Sets the 'priority' of a task
138 * \param Thread Thread to update ticket count (NULL means current thread)
139 * \param Num New ticket count (must be >= 0, clipped to \a MAX_TICKETS)
141 void Threads_SetTickets(tThread *Thread, int Num)
143 // Get current thread
144 if(Thread == NULL) Thread = Proc_GetCurThread();
147 if(Num > MAX_TICKETS) Num = MAX_TICKETS;
149 // If this isn't the current thread, we need to lock
150 if( Thread != Proc_GetCurThread() ) {
151 SHORTLOCK( &glThreadListLock );
152 giFreeTickets -= Thread->NumTickets - Num;
153 Thread->NumTickets = Num;
154 #if DEBUG_TRACE_TICKETS
155 Log("Threads_SetTickets: new giFreeTickets = %i", giFreeTickets);
157 SHORTREL( &glThreadListLock );
160 Thread->NumTickets = Num;
164 * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
165 * \brief Clone the TCB of the current thread
166 * \param Err Error pointer
167 * \param Flags Flags for something... (What is this for?)
169 tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
173 cur = Proc_GetCurThread();
175 new = malloc(sizeof(tThread));
180 memcpy(new, cur, sizeof(tThread));
184 memset( &new->IsLocked, 0, sizeof(new->IsLocked));
185 new->Status = THREAD_STAT_ACTIVE;
189 new->TID = giNextTID++;
193 new->ThreadName = strdup(cur->ThreadName);
195 // Set Thread Group ID (PID)
197 new->TGID = new->TID;
199 new->TGID = cur->TGID;
201 // Messages are not inherited
202 new->Messages = NULL;
203 new->LastMessage = NULL;
206 new->Remaining = new->Quantum = cur->Quantum;
207 new->NumTickets = cur->NumTickets;
209 // Set Signal Handlers
210 new->CurFaultNum = 0;
211 new->FaultHandler = cur->FaultHandler;
213 for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
215 switch(cCONFIG_TYPES[i])
218 new->Config[i] = cur->Config[i];
222 new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
229 // Maintain a global list of threads
230 SHORTLOCK( &glThreadListLock );
231 new->GlobalPrev = NULL; // Protect against bugs
232 new->GlobalNext = gAllThreads;
234 SHORTREL( &glThreadListLock );
240 * \fn Uint *Threads_GetCfgPtr(int Id)
241 * \brief Get a configuration pointer from the Per-Thread data area
242 * \param ID Config slot ID
243 * \return Pointer at ID
245 Uint *Threads_GetCfgPtr(int ID)
247 if(ID < 0 || ID >= NUM_CFG_ENTRIES) {
248 Warning("Threads_GetCfgPtr: Index %i is out of bounds", ID);
252 return &Proc_GetCurThread()->Config[ID];
256 * \fn tTID Threads_WaitTID(int TID, int *Status)
257 * \brief Wait for a task to change state
258 * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
259 * \param Status Thread return status
260 * \return TID of child that changed state
262 tTID Threads_WaitTID(int TID, int *Status)
266 Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
270 // Any peer/child thread
272 Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
278 Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
284 tThread *t = Threads_GetThread(TID);
285 int initStatus = t->Status;
288 if(initStatus != THREAD_STAT_ZOMBIE) {
289 // TODO: Handle child also being suspended if wanted
290 while(t->Status != THREAD_STAT_ZOMBIE) {
292 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
293 Threads_GetTID(), t->TID, t->Status);
297 Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
298 Threads_GetTID(), t->TID, t->Status);
302 case THREAD_STAT_ZOMBIE:
304 t->Status = THREAD_STAT_DEAD;
305 // TODO: Child return value?
306 if(Status) *Status = t->RetStatus;
307 // add to delete queue
308 Threads_AddToDelete( t );
311 if(Status) *Status = -1;
321 * \fn tThread *Threads_GetThread(Uint TID)
322 * \brief Gets a thread given its TID
323 * \param TID Thread ID
324 * \return Thread pointer
326 tThread *Threads_GetThread(Uint TID)
330 // Search global list
331 for(thread = gAllThreads;
333 thread = thread->GlobalNext)
335 if(thread->TID == TID)
339 Log("Unable to find TID %i on main list\n", TID);
345 * \fn void Threads_AddToDelete(tThread *Thread)
346 * \brief Adds a thread to the delete queue
347 * \param Thread Thread to delete
349 void Threads_AddToDelete(tThread *Thread)
351 // Add to delete queue
353 Thread->Next = gDeleteThreads;
354 gDeleteThreads = Thread;
357 gDeleteThreads = Thread;
362 * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
363 * \brief Gets the previous entry in a thead linked list
364 * \param List Pointer to the list head
365 * \param Thread Thread to find
366 * \return Thread before \a Thread on \a List
368 tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
372 if(*List == Thread) {
373 return (tThread*)List;
378 ret->Next && ret->Next != Thread;
381 // Error if the thread is not on the list
382 if(!ret->Next || ret->Next != Thread) {
390 * \fn void Threads_Exit(int TID, int Status)
391 * \brief Exit the current process (or another?)
392 * \param TID Thread ID to kill
393 * \param Status Exit status
395 void Threads_Exit(int TID, int Status)
398 Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
400 Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
401 // Halt forever, just in case
407 * \fn void Threads_Kill(tThread *Thread, int Status)
408 * \brief Kill a thread
409 * \param Thread Thread to kill
410 * \param Status Status code to return to the parent
412 void Threads_Kill(tThread *Thread, int Status)
417 // TODO: Kill all children
421 for(child = gActiveThreads;
425 if(child->PTID == Thread->TID)
426 Threads_Kill(child, -1);
431 ///\note Double lock is needed due to overlap of lock areas
433 // Lock thread (stop us recieving messages)
434 SHORTLOCK( &Thread->IsLocked );
437 SHORTLOCK( &glThreadListLock );
439 // Get previous thread on list
440 prev = Threads_int_GetPrev( &gActiveThreads, Thread );
442 Warning("Proc_Exit - Current thread is not on the active queue");
443 SHORTREL( &glThreadListLock );
444 SHORTREL( &Thread->IsLocked );
448 // Clear Message Queue
449 while( Thread->Messages )
451 msg = Thread->Messages->Next;
452 free( Thread->Messages );
453 Thread->Messages = msg;
456 Thread->Remaining = 0; // Clear Remaining Quantum
457 Thread->Quantum = 0; // Clear Quantum to indicate dead thread
458 prev->Next = Thread->Next; // Remove from active
460 giNumActiveThreads --;
461 if( Thread != Proc_GetCurThread() )
462 giFreeTickets -= Thread->NumTickets;
465 Thread->RetStatus = Status;
467 // Don't Zombie if we are being killed as part of a tree
470 Thread->Status = THREAD_STAT_DEAD;
471 Threads_AddToDelete( Thread );
473 Thread->Status = THREAD_STAT_ZOMBIE;
475 Threads_Wake( Thread->Parent );
478 Log("Thread %i went *hurk* (%i)", Thread->TID, Thread->Status);
481 SHORTREL( &Thread->IsLocked );
482 SHORTREL( &glThreadListLock );
484 if(Status != -1) HALT();
488 * \fn void Threads_Yield(void)
489 * \brief Yield remainder of the current thread's timeslice
491 void Threads_Yield(void)
493 Proc_GetCurThread()->Remaining = 0;
498 * \fn void Threads_Sleep(void)
499 * \brief Take the current process off the run queue
501 void Threads_Sleep(void)
503 tThread *cur = Proc_GetCurThread();
506 SHORTLOCK( &glThreadListLock );
508 // Don't sleep if there is a message waiting
509 if( cur->Messages ) {
510 SHORTREL( &glThreadListLock );
514 // Remove us from running queue
517 // Add to Sleeping List (at the top)
518 cur->Next = gSleepingThreads;
519 gSleepingThreads = cur;
521 // Mark thread as sleeping
522 cur->Status = THREAD_STAT_SLEEPING;
524 #if DEBUG_TRACE_STATE
525 Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
529 SHORTREL( &glThreadListLock );
531 while(cur->Status != THREAD_STAT_ACTIVE) HALT();
536 * \fn int Threads_Wake( tThread *Thread )
537 * \brief Wakes a sleeping/waiting thread up
538 * \param Thread Thread to wake
539 * \return Boolean Failure (Returns ERRNO)
540 * \warning This should ONLY be called with task switches disabled
542 int Threads_Wake(tThread *Thread)
549 switch(Thread->Status)
551 case THREAD_STAT_ACTIVE:
552 Log("Thread_Wake: Waking awake thread (%i)", Thread->TID);
555 case THREAD_STAT_SLEEPING:
556 // Remove from sleeping queue
557 prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
558 prev->Next = Thread->Next;
560 // Add to active queue
561 Thread->Next = gActiveThreads;
562 gActiveThreads = Thread;
564 // Update bookkeeping
565 giNumActiveThreads ++;
566 giFreeTickets += Thread->NumTickets;
567 #if DEBUG_TRACE_TICKETS
568 Log("Threads_Wake: new giFreeTickets = %i", giFreeTickets);
572 Thread->Status = THREAD_STAT_ACTIVE;
573 #if DEBUG_TRACE_STATE
574 Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
578 case THREAD_STAT_WAITING:
579 Warning("Thread_Wake - Waiting threads are not currently supported");
582 case THREAD_STAT_DEAD:
583 Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID);
587 Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status);
593 * \brief Wake a thread given the TID
594 * \param TID Thread ID to wake
595 * \return Boolean Faulure (errno)
597 int Threads_WakeTID(tTID TID)
599 tThread *thread = Threads_GetThread(TID);
603 SHORTLOCK( &glThreadListLock );
604 ret = Threads_Wake( thread );
605 SHORTREL( &glThreadListLock );
606 //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
611 * \fn void Threads_AddActive(tThread *Thread)
612 * \brief Adds a thread to the active queue
614 void Threads_AddActive(tThread *Thread)
616 SHORTLOCK( &glThreadListLock );
617 // Add to active list
618 Thread->Next = gActiveThreads;
619 gActiveThreads = Thread;
620 // Update bookkeeping
621 giNumActiveThreads ++;
622 giFreeTickets += Thread->NumTickets;
623 #if DEBUG_TRACE_TICKETS
624 Log("Threads_AddActive: new giFreeTickets = %i", giFreeTickets);
626 SHORTREL( &glThreadListLock );
630 * \brief Removes the current thread from the active queue
631 * \warning This should ONLY be called with task switches disabled
632 * \return Current thread pointer
634 tThread *Threads_RemActive(void)
636 tThread *ret = Proc_GetCurThread();
637 tThread *prev = Threads_int_GetPrev(&gActiveThreads, ret);
638 if(!prev) return NULL;
641 prev->Next = ret->Next;
642 giNumActiveThreads --;
647 * \fn void Threads_SetFaultHandler(Uint Handler)
648 * \brief Sets the signal handler for a signal
650 void Threads_SetFaultHandler(Uint Handler)
652 //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
653 Proc_GetCurThread()->FaultHandler = Handler;
657 * \fn void Threads_Fault(int Num)
658 * \brief Calls a fault handler
660 void Threads_Fault(int Num)
662 tThread *thread = Proc_GetCurThread();
664 Log_Log("Threads", "Threads_Fault: thread = %p", thread);
668 Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
670 switch(thread->FaultHandler)
673 Threads_Kill(thread, -1);
676 case 1: // Dump Core?
677 Threads_Kill(thread, -1);
682 // Double Fault? Oh, F**k
683 if(thread->CurFaultNum != 0) {
684 Threads_Kill(thread, -1); // For now, just kill
688 thread->CurFaultNum = Num;
690 Proc_CallFaultHandler(thread);
693 // --- Process Structure Access Functions ---
694 tPID Threads_GetPID(void)
696 return Proc_GetCurThread()->TGID;
698 tTID Threads_GetTID(void)
700 return Proc_GetCurThread()->TID;
702 tUID Threads_GetUID(void)
704 return Proc_GetCurThread()->UID;
706 tGID Threads_GetGID(void)
708 return Proc_GetCurThread()->GID;
711 int Threads_SetUID(Uint *Errno, tUID ID)
713 tThread *t = Proc_GetCurThread();
718 Log_Debug("Threads", "TID %i's UID set to %i", t->TID, ID);
723 int Threads_SetGID(Uint *Errno, tGID ID)
725 tThread *t = Proc_GetCurThread();
730 Log_Debug("Threads", "TID %i's GID set to %i", t->TID, ID);
736 * \fn void Threads_Dump(void)
737 * \brief Dums a list of currently running threads
739 void Threads_Dump(void)
743 Log("Active Threads:");
744 for(thread=gActiveThreads;thread;thread=thread->Next)
746 Log(" %i (%i) - %s (CPU %i)",
747 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
748 if(thread->Status != THREAD_STAT_ACTIVE)
749 Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
750 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
751 Log(" KStack 0x%x", thread->KernelStack);
755 for(thread=gAllThreads;thread;thread=thread->GlobalNext)
757 Log(" %i (%i) - %s (CPU %i)",
758 thread->TID, thread->TGID, thread->ThreadName, thread->CurCPU);
759 Log(" State %i", thread->Status);
760 Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum);
761 Log(" KStack 0x%x", thread->KernelStack);
766 * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
767 * \brief Gets the next thread to run
768 * \param CPU Current CPU
769 * \param Last The thread the CPU was running
771 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
778 SHORTLOCK( &glThreadListLock );
780 // Clear Delete Queue
781 // - I should probably put this in a worker thread to avoid calling free() in the scheduler
782 while(gDeleteThreads)
784 thread = gDeleteThreads->Next;
785 if( IS_LOCKED(&gDeleteThreads->IsLocked) ) { // Only free if structure is unused
787 gDeleteThreads->Status = THREAD_STAT_DEAD;
789 if( IsHeap(gDeleteThreads->ThreadName) )
790 free(gDeleteThreads->ThreadName);
791 // Remove from global list
792 if( gDeleteThreads == gAllThreads )
793 gAllThreads = gDeleteThreads->GlobalNext;
795 gDeleteThreads->GlobalPrev->GlobalNext = gDeleteThreads->GlobalNext;
796 free( gDeleteThreads );
798 gDeleteThreads = thread;
801 // No active threads, just take a nap
802 if(giNumActiveThreads == 0) {
803 SHORTREL( &glThreadListLock );
804 #if DEBUG_TRACE_TICKETS
805 Log("No active threads");
810 // Special case: 1 thread
811 if(giNumActiveThreads == 1) {
812 if( gActiveThreads->CurCPU == -1 )
813 gActiveThreads->CurCPU = CPU;
814 SHORTREL( &glThreadListLock );
815 if( gActiveThreads->CurCPU == CPU )
816 return gActiveThreads;
817 return NULL; // CPU has nothing to do
820 // Allow the old thread to be scheduled again
822 if( Last->Status == THREAD_STAT_ACTIVE ) {
823 giFreeTickets += Last->NumTickets;
824 #if DEBUG_TRACE_TICKETS
825 LogF(" CPU %i released %p (%i %s) into the pool (%i tickets in pool)\n",
826 CPU, Last, Last->TID, Last->ThreadName, giFreeTickets);
829 #if DEBUG_TRACE_TICKETS
831 LogF(" %p (%s)->Status = %i (Released)\n", Last, Last->ThreadName, Last->Status);
838 for(thread = gActiveThreads; thread; thread = thread->Next) {
839 if(thread->CurCPU >= 0) continue;
840 number += thread->NumTickets;
842 if(number != giFreeTickets) {
843 Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
844 giFreeTickets, number, CPU);
848 // No free tickets (all tasks delegated to cores)
849 if( giFreeTickets == 0 ) {
850 SHORTREL(&glThreadListLock);
854 // Get the ticket number
855 ticket = number = rand() % giFreeTickets;
857 // Find the next thread
858 for(thread=gActiveThreads;thread;thread=thread->Next)
860 if(thread->CurCPU >= 0) continue;
861 if(thread->NumTickets > number) break;
862 number -= thread->NumTickets;
868 for(thread=gActiveThreads;thread;thread=thread->Next) {
869 if(thread->CurCPU >= 0) continue;
870 number += thread->NumTickets;
872 Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
873 giFreeTickets, number);
875 #if DEBUG_TRACE_TICKETS
876 LogF(" CPU%i giFreeTickets = %i\n", CPU, giFreeTickets);
879 // Make the new thread non-schedulable
880 giFreeTickets -= thread->NumTickets;
881 thread->CurCPU = CPU;
884 #if DEBUG_TRACE_TICKETS
885 LogF(" CPU%i giFreeTickets = %i, giving %p (%i %s CPU=%i)\n",
886 CPU, giFreeTickets, thread, thread->TID, thread->ThreadName, thread->CurCPU);
889 SHORTREL( &glThreadListLock );
895 * \fn void Threads_SegFault(tVAddr Addr)
896 * \brief Called when a Segment Fault occurs
898 void Threads_SegFault(tVAddr Addr)
900 Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
902 //Threads_Exit( 0, -1 );
906 * \brief Acquire a heavy mutex
907 * \param Mutex Mutex to acquire
909 * This type of mutex checks if the mutex is avaliable, and acquires it
910 * if it is. Otherwise, the current thread is added to the mutex's wait
911 * queue and the thread suspends. When the holder of the mutex completes,
912 * the oldest thread (top thread) on the queue is given the lock and
915 void Mutex_Acquire(tMutex *Mutex)
917 tThread *us = Proc_GetCurThread();
920 SHORTLOCK( &Mutex->Protector );
922 //Log("Mutex_Acquire: (%p)", Mutex);
924 // Check if the lock is already held
926 SHORTLOCK( &glThreadListLock );
927 // - Remove from active list
929 // - Mark as sleeping
930 us->Status = THREAD_STAT_OFFSLEEP;
933 if(Mutex->LastWaiting) {
934 Mutex->LastWaiting->Next = us;
935 Mutex->LastWaiting = us;
939 Mutex->LastWaiting = us;
941 SHORTREL( &glThreadListLock );
942 SHORTREL( &Mutex->Protector );
943 while(us->Status == THREAD_STAT_OFFSLEEP) Threads_Yield();
944 // We're only woken when we get the lock
946 // Ooh, let's take it!
949 SHORTREL( &Mutex->Protector );
954 * \brief Release a held mutex
955 * \param Mutex Mutex to release
957 void Mutex_Release(tMutex *Mutex)
959 SHORTLOCK( &Mutex->Protector );
960 //Log("Mutex_Release: (%p)", Mutex);
961 if( Mutex->Waiting ) {
962 Mutex->Owner = Mutex->Waiting; // Set owner
963 Mutex->Waiting = Mutex->Waiting->Next; // Next!
965 Mutex->Owner->Status = THREAD_STAT_ACTIVE;
966 Threads_AddActive(Mutex->Owner);
967 //Log("Mutex %p Woke %p", Mutex, Mutex->Owner);
972 SHORTREL( &Mutex->Protector );
976 * \brief Is this mutex locked?
977 * \param Mutex Mutex pointer
979 int Mutex_IsLocked(tMutex *Mutex)
981 return Mutex->Owner != NULL;
985 EXPORT(Threads_GetUID);
986 EXPORT(Mutex_Acquire);
987 EXPORT(Mutex_Release);
988 EXPORT(Mutex_IsLocked);