Reduced chattiness of threading, x86 IRQs and BochsGA
[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                 while( us->Status == THREAD_STAT_SEMAPHORESLEEP )
82                 {
83                         Threads_Yield();
84                         if(us->Status == THREAD_STAT_SEMAPHORESLEEP)
85                                 Log_Warning("Threads", "Semaphore %p %s:%s re-schedulued while asleep",
86                                         Sem, Sem->ModName, Sem->Name);
87                 }
88                 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
89                 Log("Semaphore %p %s:%s woken", Sem, Sem->ModName, Sem->Name);
90                 #endif
91                 // We're only woken when there's something avaliable (or a signal arrives)
92                 us->WaitPointer = NULL;
93                 
94                 taken = us->RetStatus;
95                 
96                 // Get the lock again
97                 SHORTLOCK( &Sem->Protector );
98         }
99         
100         // While there is space, and there are thread waiting
101         // wake the first thread and give it what it wants (or what's left)
102         while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling )
103         {
104                  int    given;
105                 tThread *toWake = Sem->Signaling;
106                 
107                 Sem->Signaling = Sem->Signaling->Next;
108                 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
109                 if( Sem->Signaling == NULL )
110                         Sem->LastSignaling = NULL;
111                 
112                 // Figure out how much to give
113                 if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
114                         given = toWake->RetStatus;
115                 else
116                         given = Sem->MaxValue - Sem->Value;
117                 Sem->Value -= given;
118                 
119                 
120                 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
121                 Log("%p (%i %s) woken by wait on %p %s:%s",
122                         toWake, toWake->TID, toWake->ThreadName,
123                         Sem, Sem->ModName, Sem->Name);
124                 #endif
125                 
126                 // Save the number we gave to the thread's status
127                 toWake->RetStatus = given;
128                 
129                 // Wake the sleeper
130                 SHORTLOCK( &glThreadListLock );
131                 if( toWake->Status != THREAD_STAT_ACTIVE )
132                         Threads_AddActive(toWake);
133                 SHORTREL( &glThreadListLock );
134         }
135         SHORTREL( &Sem->Protector );
136         
137         #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
138         Log("Semaphore %p %s:%s took %i by wait",
139                 Sem, Sem->ModName, Sem->Name, taken);
140         #endif
141
142         return taken;
143 }
144
145 //
146 // Add items to a semaphore
147 //
148 int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
149 {
150          int    given;
151          int    added;
152         
153         if( AmmountToAdd < 0 ) {
154                 Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
155                         AmmountToAdd, Sem, Sem->Name);
156         }
157         SHORTLOCK( &Sem->Protector );
158         
159         // Check if we have to block
160         if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
161         {
162                 tThread *us;
163                 #if 0
164                 Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name);
165                 Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue);
166                 #endif
167                 
168                 SHORTLOCK( &glThreadListLock );
169                 // - Remove from active list
170                 us = Threads_RemActive();
171                 us->Next = NULL;
172                 // - Mark as sleeping
173                 us->Status = THREAD_STAT_SEMAPHORESLEEP;
174                 us->WaitPointer = Sem;
175                 us->RetStatus = AmmountToAdd;   // Use RetStatus as a temp variable
176                 
177                 // - Add to waiting
178                 if(Sem->LastSignaling) {
179                         Sem->LastSignaling->Next = us;
180                         Sem->LastSignaling = us;
181                 }
182                 else {
183                         Sem->Signaling = us;
184                         Sem->LastSignaling = us;
185                 }
186                 
187                 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
188                 Log("%p (%i %s) signaling semaphore %p %s:%s",
189                         us, us->TID, us->ThreadName,
190                         Sem, Sem->ModName, Sem->Name);
191                 #endif
192                 
193                 SHORTREL( &glThreadListLock );  
194                 SHORTREL( &Sem->Protector );
195                 while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
196                 // We're only woken when there's something avaliable
197                 us->WaitPointer = NULL;
198                 
199                 added = us->RetStatus;
200                 
201                 // Get the lock again
202                 SHORTLOCK( &Sem->Protector );
203         }
204         // Non blocking
205         else
206         {
207                 // Figure out how much we need to take off
208                 if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
209                         added = Sem->MaxValue - Sem->Value;
210                 else
211                         added = AmmountToAdd;
212                 Sem->Value += added;
213         }
214         
215         // While there are items avaliable, and there are thread waiting
216         // wake the first thread and give it what it wants (or what's left)
217         while( Sem->Value && Sem->Waiting )
218         {
219                 tThread *toWake = Sem->Waiting;
220                 
221                 // Remove thread from list (double ended, so clear LastWaiting if needed)
222                 Sem->Waiting = Sem->Waiting->Next;
223                 if( Sem->Waiting == NULL )
224                         Sem->LastWaiting = NULL;
225                 
226                 // Figure out how much to give to woken thread
227                 // - Requested count is stored in ->RetStatus
228                 if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
229                         given = toWake->RetStatus;
230                 else
231                         given = Sem->Value;
232                 Sem->Value -= given;
233                 
234                 // Save the number we gave to the thread's status
235                 toWake->RetStatus = given;
236                 
237                 if(toWake->bInstrTrace)
238                         Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem);
239                 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
240                 Log("%p (%i %s) woken by signal on %p %s:%s",
241                         toWake, toWake->TID, toWake->ThreadName,
242                         Sem, Sem->ModName, Sem->Name);
243                 #endif
244                 
245                 // Wake the sleeper
246                 if( toWake->Status != THREAD_STAT_ACTIVE )
247                         Threads_AddActive(toWake);
248                 else
249                         Warning("Thread %p (%i %s) is already awake", toWake, toWake->TID, toWake->ThreadName);
250         }
251         SHORTREL( &Sem->Protector );
252         
253         return added;
254 }
255
256 //
257 // Get the current value of a semaphore
258 //
259 int Semaphore_GetValue(tSemaphore *Sem)
260 {
261         return Sem->Value;
262 }
263
264 // === EXPORTS ===
265 EXPORT(Semaphore_Init);
266 EXPORT(Semaphore_Wait);
267 EXPORT(Semaphore_Signal);

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