3 * - By John Hodge (thePowersGang)
10 #include <threads_int.h>
12 #define SEMAPHORE_DEBUG 0 // Debug semaphores
16 // Initialise a semaphore
18 void Semaphore_Init(tSemaphore *Sem, int Value, int MaxValue, const char *Module, const char *Name)
20 memset(Sem, 0, sizeof(tSemaphore));
22 Sem->ModName = Module;
24 Sem->MaxValue = MaxValue;
27 // Wait for items to be avaliable
29 int Semaphore_Wait(tSemaphore *Sem, int MaxToTake)
33 Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)",
34 MaxToTake, Sem, Sem->Name);
38 SHORTLOCK( &Sem->Protector );
40 // Check if there's already items avaliable
44 if( MaxToTake && Sem->Value > MaxToTake )
52 taken = Threads_int_Sleep(THREAD_STAT_SEMAPHORESLEEP,
54 &Sem->Waiting, &Sem->LastWaiting, &Sem->Protector);
57 SHORTLOCK( &Sem->Protector );
60 // While there is space, and there are thread waiting
61 // wake the first thread and give it what it wants (or what's left)
62 while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling )
65 tThread *toWake = Sem->Signaling;
67 Sem->Signaling = Sem->Signaling->Next;
68 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
69 if( Sem->Signaling == NULL )
70 Sem->LastSignaling = NULL;
72 // Figure out how much to give
73 if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
74 given = toWake->RetStatus;
76 given = Sem->MaxValue - Sem->Value;
80 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
81 Log("%p (%i %s) woken by wait on %p %s:%s",
82 toWake, toWake->TID, toWake->ThreadName,
83 Sem, Sem->ModName, Sem->Name);
86 // Save the number we gave to the thread's status
87 toWake->RetStatus = given;
90 if( toWake->Status != THREAD_STAT_ACTIVE )
91 Threads_AddActive(toWake);
93 SHORTREL( &Sem->Protector );
95 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
96 Log("Semaphore %p %s:%s took %i by wait",
97 Sem, Sem->ModName, Sem->Name, taken);
104 // Add items to a semaphore
106 int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
111 if( AmmountToAdd < 0 ) {
112 Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
113 AmmountToAdd, Sem, Sem->Name);
115 SHORTLOCK( &Sem->Protector );
117 // Check if we have to block
118 if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
121 Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name);
122 Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue);
124 added = Threads_int_Sleep(THREAD_STAT_SEMAPHORESLEEP,
126 &Sem->Signaling, &Sem->LastSignaling, &Sem->Protector);
128 // Get the lock again
129 SHORTLOCK( &Sem->Protector );
134 // Figure out how much we need to take off
135 if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
136 added = Sem->MaxValue - Sem->Value;
138 added = AmmountToAdd;
142 // While there are items avaliable, and there are thread waiting
143 // wake the first thread and give it what it wants (or what's left)
144 while( Sem->Value && Sem->Waiting )
146 tThread *toWake = Sem->Waiting;
148 // Remove thread from list (double ended, so clear LastWaiting if needed)
149 Sem->Waiting = Sem->Waiting->Next;
150 if( Sem->Waiting == NULL )
151 Sem->LastWaiting = NULL;
153 // Figure out how much to give to woken thread
154 // - Requested count is stored in ->RetStatus
155 if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
156 given = toWake->RetStatus;
161 // Save the number we gave to the thread's status
162 toWake->RetStatus = given;
164 if(toWake->bInstrTrace)
165 Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem);
166 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
167 Log("%p (%i %s) woken by signal on %p %s:%s",
168 toWake, toWake->TID, toWake->ThreadName,
169 Sem, Sem->ModName, Sem->Name);
173 if( toWake->Status != THREAD_STAT_ACTIVE )
174 Threads_AddActive(toWake);
176 Warning("Thread %p (%i %s) is already awake",
177 toWake, toWake->TID, toWake->ThreadName);
179 SHORTREL( &Sem->Protector );
184 void Semaphore_ForceWake(tThread *Thread)
186 if( !CPU_HAS_LOCK(&Thread->IsLocked) ) {
187 Log_Error("Semaphore", "Force wake should be called with the thread lock held");
190 if( Thread->Status != THREAD_STAT_SEMAPHORESLEEP ) {
191 Log_Error("Semaphore", "_ForceWake called on non-semaphore thread");
195 tSemaphore *sem = Thread->WaitPointer;
196 SHORTLOCK( &sem->Protector );
197 tThread *prev = NULL;
198 if( sem->Waiting == Thread )
199 sem->Waiting = sem->Waiting->Next;
202 for( prev = sem->Waiting; prev && prev->Next != Thread; prev = prev->Next )
205 prev->Next = Thread->Next;
207 if( sem->LastWaiting == Thread )
208 sem->LastWaiting = prev;
209 SHORTREL( &sem->Protector );
210 Thread->RetStatus = 0;
211 Threads_AddActive(Thread);
215 // Get the current value of a semaphore
217 int Semaphore_GetValue(tSemaphore *Sem)
223 EXPORT(Semaphore_Init);
224 EXPORT(Semaphore_Wait);
225 EXPORT(Semaphore_Signal);