Kernel - Removed while() loops in locks with Threads_int_WaitForStatusEnd
[tpg/acess2.git] / KernelLand / Kernel / semaphore.c
1 /*
2  * Acess2 Kernel
3  * - By John Hodge (thePowersGang)
4  *
5  * semaphore.c
6  * - Semaphores
7  */
8 #include <acess.h>
9 #include <semaphore.h>
10 #include <threads_int.h>
11
12 #define SEMAPHORE_DEBUG         0       // Debug semaphores
13
14 // === CODE ===
15 //
16 // Initialise a semaphore
17 //
18 void Semaphore_Init(tSemaphore *Sem, int Value, int MaxValue, const char *Module, const char *Name)
19 {
20         memset(Sem, 0, sizeof(tSemaphore));
21         Sem->Value = Value;
22         Sem->ModName = Module;
23         Sem->Name = Name;
24         Sem->MaxValue = MaxValue;
25 }
26 //
27 // Wait for items to be avaliable
28 //
29 int Semaphore_Wait(tSemaphore *Sem, int MaxToTake)
30 {
31         tThread *us;
32          int    taken;
33         if( MaxToTake < 0 ) {
34                 Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)",
35                         MaxToTake, Sem, Sem->Name);
36                 MaxToTake = 0;
37         }
38         
39         SHORTLOCK( &Sem->Protector );
40         
41         // Check if there's already items avaliable
42         if( Sem->Value > 0 )
43         {
44                 // Take what we need
45                 if( MaxToTake && Sem->Value > MaxToTake )
46                         taken = MaxToTake;
47                 else
48                         taken = Sem->Value;
49                 Sem->Value -= taken;
50         }
51         else
52         {
53                 SHORTLOCK( &glThreadListLock );
54                 
55                 // - Remove from active list
56                 us = Threads_RemActive();
57                 us->Next = NULL;
58                 // - Mark as sleeping
59                 us->Status = THREAD_STAT_SEMAPHORESLEEP;
60                 us->WaitPointer = Sem;
61                 us->RetStatus = MaxToTake;      // Use RetStatus as a temp variable
62                 
63                 // - Add to waiting
64                 if(Sem->LastWaiting) {
65                         Sem->LastWaiting->Next = us;
66                         Sem->LastWaiting = us;
67                 }
68                 else {
69                         Sem->Waiting = us;
70                         Sem->LastWaiting = us;
71                 }
72                 
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);
77                 #endif
78                 
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", Sem, Sem->ModName, Sem->Name);
86                 #endif
87                 us->WaitPointer = NULL;
88                 
89                 taken = us->RetStatus;
90                 
91                 // Get the lock again
92                 SHORTLOCK( &Sem->Protector );
93         }
94         
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 )
98         {
99                  int    given;
100                 tThread *toWake = Sem->Signaling;
101                 
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;
106                 
107                 // Figure out how much to give
108                 if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
109                         given = toWake->RetStatus;
110                 else
111                         given = Sem->MaxValue - Sem->Value;
112                 Sem->Value -= given;
113                 
114                 
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);
119                 #endif
120                 
121                 // Save the number we gave to the thread's status
122                 toWake->RetStatus = given;
123                 
124                 // Wake the sleeper
125                 if( toWake->Status != THREAD_STAT_ACTIVE )
126                         Threads_AddActive(toWake);
127         }
128         SHORTREL( &Sem->Protector );
129         
130         #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
131         Log("Semaphore %p %s:%s took %i by wait",
132                 Sem, Sem->ModName, Sem->Name, taken);
133         #endif
134
135         return taken;
136 }
137
138 //
139 // Add items to a semaphore
140 //
141 int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
142 {
143          int    given;
144          int    added;
145         
146         if( AmmountToAdd < 0 ) {
147                 Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
148                         AmmountToAdd, Sem, Sem->Name);
149         }
150         SHORTLOCK( &Sem->Protector );
151         
152         // Check if we have to block
153         if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
154         {
155                 tThread *us;
156                 #if 0
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);
159                 #endif
160                 
161                 SHORTLOCK( &glThreadListLock );
162                 // - Remove from active list
163                 us = Threads_RemActive();
164                 us->Next = NULL;
165                 // - Mark as sleeping
166                 us->Status = THREAD_STAT_SEMAPHORESLEEP;
167                 us->WaitPointer = Sem;
168                 us->RetStatus = AmmountToAdd;   // Use RetStatus as a temp variable
169                 
170                 // - Add to waiting
171                 if(Sem->LastSignaling) {
172                         Sem->LastSignaling->Next = us;
173                         Sem->LastSignaling = us;
174                 }
175                 else {
176                         Sem->Signaling = us;
177                         Sem->LastSignaling = us;
178                 }
179                 
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);
184                 #endif
185                 
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;
191                 
192                 added = us->RetStatus;
193                 
194                 // Get the lock again
195                 SHORTLOCK( &Sem->Protector );
196         }
197         // Non blocking
198         else
199         {
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;
203                 else
204                         added = AmmountToAdd;
205                 Sem->Value += added;
206         }
207         
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 )
211         {
212                 tThread *toWake = Sem->Waiting;
213                 
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;
218                 
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;
223                 else
224                         given = Sem->Value;
225                 Sem->Value -= given;
226                 
227                 // Save the number we gave to the thread's status
228                 toWake->RetStatus = given;
229                 
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);
236                 #endif
237                 
238                 // Wake the sleeper
239                 if( toWake->Status != THREAD_STAT_ACTIVE )
240                         Threads_AddActive(toWake);
241                 else
242                         Warning("Thread %p (%i %s) is already awake",
243                                 toWake, toWake->TID, toWake->ThreadName);
244         }
245         SHORTREL( &Sem->Protector );
246         
247         return added;
248 }
249
250 void Semaphore_ForceWake(tThread *Thread)
251 {
252         if( !CPU_HAS_LOCK(&Thread->IsLocked) ) {
253                 Log_Error("Semaphore", "Force wake should be called with the thread lock held");
254                 return ;
255         }
256         if( Thread->Status != THREAD_STAT_SEMAPHORESLEEP ) {
257                 Log_Error("Semaphore", "_ForceWake called on non-semaphore thread");
258                 return ;
259         }
260
261         tSemaphore *sem = Thread->WaitPointer;
262         SHORTLOCK( &sem->Protector );
263         tThread *prev = NULL;
264         if( sem->Waiting == Thread )
265                 sem->Waiting = sem->Waiting->Next;
266         else
267         {
268                 for( prev = sem->Waiting; prev && prev->Next != Thread; prev = prev->Next )
269                         ;
270                 if( prev )
271                         prev->Next = Thread->Next;
272         }
273         if( sem->LastWaiting == Thread )
274                 sem->LastWaiting = prev;
275         SHORTREL( &sem->Protector );
276         Thread->RetStatus = 0;
277         Threads_AddActive(Thread);
278 }
279
280 //
281 // Get the current value of a semaphore
282 //
283 int Semaphore_GetValue(tSemaphore *Sem)
284 {
285         return Sem->Value;
286 }
287
288 // === EXPORTS ===
289 EXPORT(Semaphore_Init);
290 EXPORT(Semaphore_Wait);
291 EXPORT(Semaphore_Signal);

UCC git Repository :: git.ucc.asn.au