Kernel/threads - Cleaning out delete queue
[tpg/acess2.git] / 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         }
37         
38         SHORTLOCK( &Sem->Protector );
39         
40         // Check if there's already items avaliable
41         if( Sem->Value > 0 )
42         {
43                 // Take what we need
44                 if( MaxToTake && Sem->Value > MaxToTake )
45                         taken = MaxToTake;
46                 else
47                         taken = Sem->Value;
48                 Sem->Value -= taken;
49         }
50         else
51         {
52                 SHORTLOCK( &glThreadListLock );
53                 
54                 // - Remove from active list
55                 us = Threads_RemActive();
56                 us->Next = NULL;
57                 // - Mark as sleeping
58                 us->Status = THREAD_STAT_SEMAPHORESLEEP;
59                 us->WaitPointer = Sem;
60                 us->RetStatus = MaxToTake;      // Use RetStatus as a temp variable
61                 
62                 // - Add to waiting
63                 if(Sem->LastWaiting) {
64                         Sem->LastWaiting->Next = us;
65                         Sem->LastWaiting = us;
66                 }
67                 else {
68                         Sem->Waiting = us;
69                         Sem->LastWaiting = us;
70                 }
71                 
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);
76                 #endif
77                 
78                 SHORTREL( &Sem->Protector );    // Release first to make sure it is released
79                 SHORTREL( &glThreadListLock );
80                 while( us->Status == THREAD_STAT_SEMAPHORESLEEP )
81                 {
82                         Threads_Yield();
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);
86                 }
87                 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
88                 Log("Semaphore %p %s:%s woken", Sem, Sem->ModName, Sem->Name);
89                 #endif
90                 // We're only woken when there's something avaliable (or a signal arrives)
91                 us->WaitPointer = NULL;
92                 
93                 taken = us->RetStatus;
94                 
95                 // Get the lock again
96                 SHORTLOCK( &Sem->Protector );
97         }
98         
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 )
102         {
103                  int    given;
104                 tThread *toWake = Sem->Signaling;
105                 
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;
110                 
111                 // Figure out how much to give
112                 if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
113                         given = toWake->RetStatus;
114                 else
115                         given = Sem->MaxValue - Sem->Value;
116                 Sem->Value -= given;
117                 
118                 
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);
123                 #endif
124                 
125                 // Save the number we gave to the thread's status
126                 toWake->RetStatus = given;
127                 
128                 // Wake the sleeper
129                 SHORTLOCK( &glThreadListLock );
130                 if( toWake->Status != THREAD_STAT_ACTIVE )
131                         Threads_AddActive(toWake);
132                 SHORTREL( &glThreadListLock );
133         }
134         SHORTREL( &Sem->Protector );
135         
136         #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
137         Log("Semaphore %p %s:%s took %i by wait",
138                 Sem, Sem->ModName, Sem->Name, taken);
139         #endif
140
141         return taken;
142 }
143
144 //
145 // Add items to a semaphore
146 //
147 int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
148 {
149          int    given;
150          int    added;
151         
152         if( AmmountToAdd < 0 ) {
153                 Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
154                         AmmountToAdd, Sem, Sem->Name);
155         }
156         SHORTLOCK( &Sem->Protector );
157         
158         // Check if we have to block
159         if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
160         {
161                 tThread *us;
162                 #if 0
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);
165                 #endif
166                 
167                 SHORTLOCK( &glThreadListLock );
168                 // - Remove from active list
169                 us = Threads_RemActive();
170                 us->Next = NULL;
171                 // - Mark as sleeping
172                 us->Status = THREAD_STAT_SEMAPHORESLEEP;
173                 us->WaitPointer = Sem;
174                 us->RetStatus = AmmountToAdd;   // Use RetStatus as a temp variable
175                 
176                 // - Add to waiting
177                 if(Sem->LastSignaling) {
178                         Sem->LastSignaling->Next = us;
179                         Sem->LastSignaling = us;
180                 }
181                 else {
182                         Sem->Signaling = us;
183                         Sem->LastSignaling = us;
184                 }
185                 
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);
190                 #endif
191                 
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;
197                 
198                 added = us->RetStatus;
199                 
200                 // Get the lock again
201                 SHORTLOCK( &Sem->Protector );
202         }
203         // Non blocking
204         else
205         {
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;
209                 else
210                         added = AmmountToAdd;
211                 Sem->Value += added;
212         }
213         
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 )
217         {
218                 tThread *toWake = Sem->Waiting;
219                 
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;
224                 
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;
229                 else
230                         given = Sem->Value;
231                 Sem->Value -= given;
232                 
233                 // Save the number we gave to the thread's status
234                 toWake->RetStatus = given;
235                 
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);
242                 #endif
243                 
244                 // Wake the sleeper
245                 if( toWake->Status != THREAD_STAT_ACTIVE )
246                         Threads_AddActive(toWake);
247                 else
248                         Warning("Thread %p (%i %s) is already awake", toWake, toWake->TID, toWake->ThreadName);
249         }
250         SHORTREL( &Sem->Protector );
251         
252         return added;
253 }
254
255 //
256 // Get the current value of a semaphore
257 //
258 int Semaphore_GetValue(tSemaphore *Sem)
259 {
260         return Sem->Value;
261 }
262
263 // === EXPORTS ===
264 EXPORT(Semaphore_Init);
265 EXPORT(Semaphore_Wait);
266 EXPORT(Semaphore_Signal);

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