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);
38 SHORTLOCK( &Sem->Protector );
40 // Check if there's already items avaliable
44 if( MaxToTake && Sem->Value > MaxToTake )
52 SHORTLOCK( &glThreadListLock );
54 // - Remove from active list
55 us = Threads_RemActive();
58 us->Status = THREAD_STAT_SEMAPHORESLEEP;
59 us->WaitPointer = Sem;
60 us->RetStatus = MaxToTake; // Use RetStatus as a temp variable
63 if(Sem->LastWaiting) {
64 Sem->LastWaiting->Next = us;
65 Sem->LastWaiting = us;
69 Sem->LastWaiting = us;
72 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
73 Log("%p (%i %s) waiting on semaphore %p %s:%s",
74 us, us->TID, us->ThreadName,
75 Sem, Sem->ModName, Sem->Name);
78 SHORTREL( &Sem->Protector ); // Release first to make sure it is released
79 SHORTREL( &glThreadListLock );
80 while( us->Status == THREAD_STAT_SEMAPHORESLEEP )
83 if(us->Status == THREAD_STAT_SEMAPHORESLEEP)
84 Log_Warning("Threads", "Semaphore %p %s:%s re-schedulued while asleep",
85 Sem, Sem->ModName, Sem->Name);
87 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
88 Log("Semaphore %p %s:%s woken", Sem, Sem->ModName, Sem->Name);
90 // We're only woken when there's something avaliable (or a signal arrives)
91 us->WaitPointer = NULL;
93 taken = us->RetStatus;
96 SHORTLOCK( &Sem->Protector );
99 // While there is space, and there are thread waiting
100 // wake the first thread and give it what it wants (or what's left)
101 while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling )
104 tThread *toWake = Sem->Signaling;
106 Sem->Signaling = Sem->Signaling->Next;
107 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
108 if( Sem->Signaling == NULL )
109 Sem->LastSignaling = NULL;
111 // Figure out how much to give
112 if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
113 given = toWake->RetStatus;
115 given = Sem->MaxValue - Sem->Value;
119 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
120 Log("%p (%i %s) woken by wait on %p %s:%s",
121 toWake, toWake->TID, toWake->ThreadName,
122 Sem, Sem->ModName, Sem->Name);
125 // Save the number we gave to the thread's status
126 toWake->RetStatus = given;
129 SHORTLOCK( &glThreadListLock );
130 if( toWake->Status != THREAD_STAT_ACTIVE )
131 Threads_AddActive(toWake);
132 SHORTREL( &glThreadListLock );
134 SHORTREL( &Sem->Protector );
136 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
137 Log("Semaphore %p %s:%s took %i by wait",
138 Sem, Sem->ModName, Sem->Name, taken);
145 // Add items to a semaphore
147 int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
152 if( AmmountToAdd < 0 ) {
153 Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
154 AmmountToAdd, Sem, Sem->Name);
156 SHORTLOCK( &Sem->Protector );
158 // Check if we have to block
159 if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
163 Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name);
164 Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue);
167 SHORTLOCK( &glThreadListLock );
168 // - Remove from active list
169 us = Threads_RemActive();
171 // - Mark as sleeping
172 us->Status = THREAD_STAT_SEMAPHORESLEEP;
173 us->WaitPointer = Sem;
174 us->RetStatus = AmmountToAdd; // Use RetStatus as a temp variable
177 if(Sem->LastSignaling) {
178 Sem->LastSignaling->Next = us;
179 Sem->LastSignaling = us;
183 Sem->LastSignaling = us;
186 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
187 Log("%p (%i %s) signaling semaphore %p %s:%s",
188 us, us->TID, us->ThreadName,
189 Sem, Sem->ModName, Sem->Name);
192 SHORTREL( &glThreadListLock );
193 SHORTREL( &Sem->Protector );
194 while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
195 // We're only woken when there's something avaliable
196 us->WaitPointer = NULL;
198 added = us->RetStatus;
200 // Get the lock again
201 SHORTLOCK( &Sem->Protector );
206 // Figure out how much we need to take off
207 if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
208 added = Sem->MaxValue - Sem->Value;
210 added = AmmountToAdd;
214 // While there are items avaliable, and there are thread waiting
215 // wake the first thread and give it what it wants (or what's left)
216 while( Sem->Value && Sem->Waiting )
218 tThread *toWake = Sem->Waiting;
220 // Remove thread from list (double ended, so clear LastWaiting if needed)
221 Sem->Waiting = Sem->Waiting->Next;
222 if( Sem->Waiting == NULL )
223 Sem->LastWaiting = NULL;
225 // Figure out how much to give to woken thread
226 // - Requested count is stored in ->RetStatus
227 if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
228 given = toWake->RetStatus;
233 // Save the number we gave to the thread's status
234 toWake->RetStatus = given;
236 if(toWake->bInstrTrace)
237 Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem);
238 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
239 Log("%p (%i %s) woken by signal on %p %s:%s",
240 toWake, toWake->TID, toWake->ThreadName,
241 Sem, Sem->ModName, Sem->Name);
245 if( toWake->Status != THREAD_STAT_ACTIVE )
246 Threads_AddActive(toWake);
248 Warning("Thread %p (%i %s) is already awake", toWake, toWake->TID, toWake->ThreadName);
250 SHORTREL( &Sem->Protector );
256 // Get the current value of a semaphore
258 int Semaphore_GetValue(tSemaphore *Sem)
264 EXPORT(Semaphore_Init);
265 EXPORT(Semaphore_Wait);
266 EXPORT(Semaphore_Signal);