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)
34 Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)",
35 MaxToTake, Sem, Sem->Name);
39 SHORTLOCK( &Sem->Protector );
41 // Check if there's already items avaliable
45 if( MaxToTake && Sem->Value > MaxToTake )
53 SHORTLOCK( &glThreadListLock );
55 // - Remove from active list
56 us = Threads_RemActive();
59 us->Status = THREAD_STAT_SEMAPHORESLEEP;
60 us->WaitPointer = Sem;
61 us->RetStatus = MaxToTake; // Use RetStatus as a temp variable
64 if(Sem->LastWaiting) {
65 Sem->LastWaiting->Next = us;
66 Sem->LastWaiting = us;
70 Sem->LastWaiting = us;
73 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
74 Log("%p (%i %s) waiting on semaphore %p %s:%s",
75 us, us->TID, us->ThreadName,
76 Sem, Sem->ModName, Sem->Name);
79 SHORTREL( &Sem->Protector ); // Release first to make sure it is released
80 SHORTREL( &glThreadListLock );
81 // Sleep until woken (either by getting what we need, or a timer event)
82 Threads_int_WaitForStatusEnd( THREAD_STAT_SEMAPHORESLEEP );
83 // We're only woken when there's something avaliable (or a signal arrives)
84 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
85 Log("Semaphore %p %s:%s woken from wait", Sem, Sem->ModName, Sem->Name);
87 us->WaitPointer = NULL;
89 taken = us->RetStatus;
92 SHORTLOCK( &Sem->Protector );
95 // While there is space, and there are thread waiting
96 // wake the first thread and give it what it wants (or what's left)
97 while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling )
100 tThread *toWake = Sem->Signaling;
102 Sem->Signaling = Sem->Signaling->Next;
103 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
104 if( Sem->Signaling == NULL )
105 Sem->LastSignaling = NULL;
107 // Figure out how much to give
108 if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
109 given = toWake->RetStatus;
111 given = Sem->MaxValue - Sem->Value;
115 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
116 Log("%p (%i %s) woken by wait on %p %s:%s",
117 toWake, toWake->TID, toWake->ThreadName,
118 Sem, Sem->ModName, Sem->Name);
121 // Save the number we gave to the thread's status
122 toWake->RetStatus = given;
125 if( toWake->Status != THREAD_STAT_ACTIVE )
126 Threads_AddActive(toWake);
128 SHORTREL( &Sem->Protector );
130 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
131 Log("Semaphore %p %s:%s took %i by wait",
132 Sem, Sem->ModName, Sem->Name, taken);
139 // Add items to a semaphore
141 int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
146 if( AmmountToAdd < 0 ) {
147 Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
148 AmmountToAdd, Sem, Sem->Name);
150 SHORTLOCK( &Sem->Protector );
152 // Check if we have to block
153 if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
157 Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name);
158 Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue);
161 SHORTLOCK( &glThreadListLock );
162 // - Remove from active list
163 us = Threads_RemActive();
165 // - Mark as sleeping
166 us->Status = THREAD_STAT_SEMAPHORESLEEP;
167 us->WaitPointer = Sem;
168 us->RetStatus = AmmountToAdd; // Use RetStatus as a temp variable
171 if(Sem->LastSignaling) {
172 Sem->LastSignaling->Next = us;
173 Sem->LastSignaling = us;
177 Sem->LastSignaling = us;
180 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
181 Log("%p (%i %s) signaling semaphore %p %s:%s",
182 us, us->TID, us->ThreadName,
183 Sem, Sem->ModName, Sem->Name);
186 SHORTREL( &glThreadListLock );
187 SHORTREL( &Sem->Protector );
188 Threads_int_WaitForStatusEnd(THREAD_STAT_SEMAPHORESLEEP);
189 // We're only woken when there's something avaliable
190 us->WaitPointer = NULL;
192 added = us->RetStatus;
194 // Get the lock again
195 SHORTLOCK( &Sem->Protector );
200 // Figure out how much we need to take off
201 if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
202 added = Sem->MaxValue - Sem->Value;
204 added = AmmountToAdd;
208 // While there are items avaliable, and there are thread waiting
209 // wake the first thread and give it what it wants (or what's left)
210 while( Sem->Value && Sem->Waiting )
212 tThread *toWake = Sem->Waiting;
214 // Remove thread from list (double ended, so clear LastWaiting if needed)
215 Sem->Waiting = Sem->Waiting->Next;
216 if( Sem->Waiting == NULL )
217 Sem->LastWaiting = NULL;
219 // Figure out how much to give to woken thread
220 // - Requested count is stored in ->RetStatus
221 if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
222 given = toWake->RetStatus;
227 // Save the number we gave to the thread's status
228 toWake->RetStatus = given;
230 if(toWake->bInstrTrace)
231 Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem);
232 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
233 Log("%p (%i %s) woken by signal on %p %s:%s",
234 toWake, toWake->TID, toWake->ThreadName,
235 Sem, Sem->ModName, Sem->Name);
239 if( toWake->Status != THREAD_STAT_ACTIVE )
240 Threads_AddActive(toWake);
242 Warning("Thread %p (%i %s) is already awake",
243 toWake, toWake->TID, toWake->ThreadName);
245 SHORTREL( &Sem->Protector );
250 void Semaphore_ForceWake(tThread *Thread)
252 if( !CPU_HAS_LOCK(&Thread->IsLocked) ) {
253 Log_Error("Semaphore", "Force wake should be called with the thread lock held");
256 if( Thread->Status != THREAD_STAT_SEMAPHORESLEEP ) {
257 Log_Error("Semaphore", "_ForceWake called on non-semaphore thread");
261 tSemaphore *sem = Thread->WaitPointer;
262 SHORTLOCK( &sem->Protector );
263 tThread *prev = NULL;
264 if( sem->Waiting == Thread )
265 sem->Waiting = sem->Waiting->Next;
268 for( prev = sem->Waiting; prev && prev->Next != Thread; prev = prev->Next )
271 prev->Next = Thread->Next;
273 if( sem->LastWaiting == Thread )
274 sem->LastWaiting = prev;
275 SHORTREL( &sem->Protector );
276 Thread->RetStatus = 0;
277 Threads_AddActive(Thread);
281 // Get the current value of a semaphore
283 int Semaphore_GetValue(tSemaphore *Sem)
289 EXPORT(Semaphore_Init);
290 EXPORT(Semaphore_Wait);
291 EXPORT(Semaphore_Signal);