Kernel - Converted most blocking states to use Threads_int_Sleep
[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          int    taken;
32         if( MaxToTake < 0 ) {
33                 Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)",
34                         MaxToTake, Sem, Sem->Name);
35                 MaxToTake = 0;
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                 taken = Threads_int_Sleep(THREAD_STAT_SEMAPHORESLEEP,
53                         Sem, MaxToTake,
54                         &Sem->Waiting, &Sem->LastWaiting, &Sem->Protector);
55                 
56                 // Get the lock again
57                 SHORTLOCK( &Sem->Protector );
58         }
59         
60         // While there is space, and there are thread waiting
61         // wake the first thread and give it what it wants (or what's left)
62         while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling )
63         {
64                  int    given;
65                 tThread *toWake = Sem->Signaling;
66                 
67                 Sem->Signaling = Sem->Signaling->Next;
68                 // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
69                 if( Sem->Signaling == NULL )
70                         Sem->LastSignaling = NULL;
71                 
72                 // Figure out how much to give
73                 if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
74                         given = toWake->RetStatus;
75                 else
76                         given = Sem->MaxValue - Sem->Value;
77                 Sem->Value -= given;
78                 
79                 
80                 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
81                 Log("%p (%i %s) woken by wait on %p %s:%s",
82                         toWake, toWake->TID, toWake->ThreadName,
83                         Sem, Sem->ModName, Sem->Name);
84                 #endif
85                 
86                 // Save the number we gave to the thread's status
87                 toWake->RetStatus = given;
88                 
89                 // Wake the sleeper
90                 if( toWake->Status != THREAD_STAT_ACTIVE )
91                         Threads_AddActive(toWake);
92         }
93         SHORTREL( &Sem->Protector );
94         
95         #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
96         Log("Semaphore %p %s:%s took %i by wait",
97                 Sem, Sem->ModName, Sem->Name, taken);
98         #endif
99
100         return taken;
101 }
102
103 //
104 // Add items to a semaphore
105 //
106 int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
107 {
108          int    given;
109          int    added;
110         
111         if( AmmountToAdd < 0 ) {
112                 Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
113                         AmmountToAdd, Sem, Sem->Name);
114         }
115         SHORTLOCK( &Sem->Protector );
116         
117         // Check if we have to block
118         if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
119         {
120                 #if 0
121                 Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name);
122                 Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue);
123                 #endif
124                 added = Threads_int_Sleep(THREAD_STAT_SEMAPHORESLEEP,
125                         Sem, AmmountToAdd,
126                         &Sem->Signaling, &Sem->LastSignaling, &Sem->Protector);
127                 
128                 // Get the lock again
129                 SHORTLOCK( &Sem->Protector );
130         }
131         // Non blocking
132         else
133         {
134                 // Figure out how much we need to take off
135                 if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
136                         added = Sem->MaxValue - Sem->Value;
137                 else
138                         added = AmmountToAdd;
139                 Sem->Value += added;
140         }
141         
142         // While there are items avaliable, and there are thread waiting
143         // wake the first thread and give it what it wants (or what's left)
144         while( Sem->Value && Sem->Waiting )
145         {
146                 tThread *toWake = Sem->Waiting;
147                 
148                 // Remove thread from list (double ended, so clear LastWaiting if needed)
149                 Sem->Waiting = Sem->Waiting->Next;
150                 if( Sem->Waiting == NULL )
151                         Sem->LastWaiting = NULL;
152                 
153                 // Figure out how much to give to woken thread
154                 // - Requested count is stored in ->RetStatus
155                 if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
156                         given = toWake->RetStatus;
157                 else
158                         given = Sem->Value;
159                 Sem->Value -= given;
160                 
161                 // Save the number we gave to the thread's status
162                 toWake->RetStatus = given;
163                 
164                 if(toWake->bInstrTrace)
165                         Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem);
166                 #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
167                 Log("%p (%i %s) woken by signal on %p %s:%s",
168                         toWake, toWake->TID, toWake->ThreadName,
169                         Sem, Sem->ModName, Sem->Name);
170                 #endif
171                 
172                 // Wake the sleeper
173                 if( toWake->Status != THREAD_STAT_ACTIVE )
174                         Threads_AddActive(toWake);
175                 else
176                         Warning("Thread %p (%i %s) is already awake",
177                                 toWake, toWake->TID, toWake->ThreadName);
178         }
179         SHORTREL( &Sem->Protector );
180         
181         return added;
182 }
183
184 void Semaphore_ForceWake(tThread *Thread)
185 {
186         if( !CPU_HAS_LOCK(&Thread->IsLocked) ) {
187                 Log_Error("Semaphore", "Force wake should be called with the thread lock held");
188                 return ;
189         }
190         if( Thread->Status != THREAD_STAT_SEMAPHORESLEEP ) {
191                 Log_Error("Semaphore", "_ForceWake called on non-semaphore thread");
192                 return ;
193         }
194
195         tSemaphore *sem = Thread->WaitPointer;
196         SHORTLOCK( &sem->Protector );
197         tThread *prev = NULL;
198         if( sem->Waiting == Thread )
199                 sem->Waiting = sem->Waiting->Next;
200         else
201         {
202                 for( prev = sem->Waiting; prev && prev->Next != Thread; prev = prev->Next )
203                         ;
204                 if( prev )
205                         prev->Next = Thread->Next;
206         }
207         if( sem->LastWaiting == Thread )
208                 sem->LastWaiting = prev;
209         SHORTREL( &sem->Protector );
210         Thread->RetStatus = 0;
211         Threads_AddActive(Thread);
212 }
213
214 //
215 // Get the current value of a semaphore
216 //
217 int Semaphore_GetValue(tSemaphore *Sem)
218 {
219         return Sem->Value;
220 }
221
222 // === EXPORTS ===
223 EXPORT(Semaphore_Init);
224 EXPORT(Semaphore_Wait);
225 EXPORT(Semaphore_Signal);

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