3 * - By John Hodge (thePowersGang)
10 #include <semaphore.h>
11 #include <threads_int.h>
13 #define SEMAPHORE_DEBUG 0 // Debug semaphores
17 // Initialise a semaphore
19 void Semaphore_Init(tSemaphore *Sem, int Value, int MaxValue, const char *Module, const char *Name)
21 LOG("Init %p to %i/%i (%s:%s)", Sem, Value, MaxValue, Module, Name);
22 memset(Sem, 0, sizeof(tSemaphore));
24 Sem->ModName = Module;
26 Sem->MaxValue = MaxValue;
29 // Wait for items to be avaliable
31 int Semaphore_Wait(tSemaphore *Sem, int MaxToTake)
35 Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)",
36 MaxToTake, Sem, Sem->Name);
39 LOG("Waiting on %p for %i (%i/%i used atm) - (%s:%s)",
40 Sem, MaxToTake, Sem->Value, Sem->MaxValue, Sem->ModName, Sem->Name);
42 SHORTLOCK( &Sem->Protector );
43 LOG("Protector grabbed");
45 // Check if there's already items avaliable
49 if( MaxToTake && Sem->Value > MaxToTake )
57 taken = Threads_int_Sleep(THREAD_STAT_SEMAPHORESLEEP,
59 &Sem->Waiting, &Sem->LastWaiting, &Sem->Protector);
62 SHORTLOCK( &Sem->Protector );
65 // While there is space, and there are thread waiting
66 // wake the first thread and give it what it wants (or what's left)
67 while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling )
70 tThread *toWake = Sem->Signaling;
72 Sem->Signaling = Sem->Signaling->Next;
73 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
74 if( Sem->Signaling == NULL )
75 Sem->LastSignaling = NULL;
77 // Figure out how much to give
78 if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
79 given = toWake->RetStatus;
81 given = Sem->MaxValue - Sem->Value;
85 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
86 Log("%p (%i %s) woken by wait on %p %s:%s",
87 toWake, toWake->TID, toWake->ThreadName,
88 Sem, Sem->ModName, Sem->Name);
91 // Save the number we gave to the thread's status
92 toWake->RetStatus = given;
95 if( toWake->Status != THREAD_STAT_ACTIVE )
96 Threads_AddActive(toWake);
98 SHORTREL( &Sem->Protector );
100 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
101 Log("Semaphore %p %s:%s took %i by wait",
102 Sem, Sem->ModName, Sem->Name, taken);
109 // Add items to a semaphore
111 int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
116 if( AmmountToAdd < 0 ) {
117 Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
118 AmmountToAdd, Sem, Sem->Name);
120 SHORTLOCK( &Sem->Protector );
122 // Check if we have to block
123 if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
126 Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name);
127 Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue);
129 added = Threads_int_Sleep(THREAD_STAT_SEMAPHORESLEEP,
131 &Sem->Signaling, &Sem->LastSignaling, &Sem->Protector);
133 // Get the lock again
134 SHORTLOCK( &Sem->Protector );
139 // Figure out how much we need to take off
140 if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
141 added = Sem->MaxValue - Sem->Value;
143 added = AmmountToAdd;
147 // While there are items avaliable, and there are thread waiting
148 // wake the first thread and give it what it wants (or what's left)
149 while( Sem->Value && Sem->Waiting )
151 tThread *toWake = Sem->Waiting;
153 // Remove thread from list (double ended, so clear LastWaiting if needed)
154 Sem->Waiting = Sem->Waiting->Next;
155 if( Sem->Waiting == NULL )
156 Sem->LastWaiting = NULL;
158 // Figure out how much to give to woken thread
159 // - Requested count is stored in ->RetStatus
160 if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
161 given = toWake->RetStatus;
166 // Save the number we gave to the thread's status
167 toWake->RetStatus = given;
169 if(toWake->bInstrTrace)
170 Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem);
171 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
172 Log("%p (%i %s) woken by signal on %p %s:%s",
173 toWake, toWake->TID, toWake->ThreadName,
174 Sem, Sem->ModName, Sem->Name);
178 if( toWake->Status != THREAD_STAT_ACTIVE )
179 Threads_AddActive(toWake);
181 Warning("Thread %p (%i %s) is already awake",
182 toWake, toWake->TID, toWake->ThreadName);
184 SHORTREL( &Sem->Protector );
189 void Semaphore_ForceWake(tThread *Thread)
191 if( !CPU_HAS_LOCK(&Thread->IsLocked) ) {
192 Log_Error("Semaphore", "Force wake should be called with the thread lock held");
195 if( Thread->Status != THREAD_STAT_SEMAPHORESLEEP ) {
196 Log_Error("Semaphore", "_ForceWake called on non-semaphore thread");
200 tSemaphore *sem = Thread->WaitPointer;
201 SHORTLOCK( &sem->Protector );
202 tThread *prev = NULL;
203 if( sem->Waiting == Thread )
204 sem->Waiting = sem->Waiting->Next;
207 for( prev = sem->Waiting; prev && prev->Next != Thread; prev = prev->Next )
210 prev->Next = Thread->Next;
212 if( sem->LastWaiting == Thread )
213 sem->LastWaiting = prev;
214 SHORTREL( &sem->Protector );
215 Thread->RetStatus = 0;
216 Threads_AddActive(Thread);
220 // Get the current value of a semaphore
222 int Semaphore_GetValue(tSemaphore *Sem)
228 EXPORT(Semaphore_Init);
229 EXPORT(Semaphore_Wait);
230 EXPORT(Semaphore_Signal);