Kernel/x86 - Add flag to set PIT to larges possible divisor
[tpg/acess2.git] / KernelLand / Kernel / semaphore.c
1 /*
2  * Acess2 Kernel
3  * - By John Hodge (thePowersGang)
4  *
5  * semaphore.c
6  * - Semaphores
7  */
8 #define DEBUG   0
9 #include <acess.h>
10 #include <semaphore.h>
11 #include <threads_int.h>
12
13 #define SEMAPHORE_DEBUG         0       // Debug semaphores
14
15 // === CODE ===
16 //
17 // Initialise a semaphore
18 //
19 void Semaphore_Init(tSemaphore *Sem, int Value, int MaxValue, const char *Module, const char *Name)
20 {
21         LOG("Init %p to %i/%i (%s:%s)", Sem, Value, MaxValue, Module, Name);
22         memset(Sem, 0, sizeof(tSemaphore));
23         Sem->Value = Value;
24         Sem->ModName = Module;
25         Sem->Name = Name;
26         Sem->MaxValue = MaxValue;
27 }
28 //
29 // Wait for items to be avaliable
30 //
31 int Semaphore_Wait(tSemaphore *Sem, int MaxToTake)
32 {
33          int    taken;
34         if( MaxToTake < 0 ) {
35                 Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)",
36                         MaxToTake, Sem, Sem->Name);
37                 MaxToTake = 0;
38         }
39         LOG("Waiting on %p for %i (%i/%i used atm) - (%s:%s)",
40                 Sem, MaxToTake, Sem->Value, Sem->MaxValue, Sem->ModName, Sem->Name);
41         
42         SHORTLOCK( &Sem->Protector );
43         LOG("Protector grabbed");
44         
45         // Check if there's already items avaliable
46         if( Sem->Value > 0 )
47         {
48                 // Take what we need
49                 if( MaxToTake && Sem->Value > MaxToTake )
50                         taken = MaxToTake;
51                 else
52                         taken = Sem->Value;
53                 Sem->Value -= taken;
54         }
55         else
56         {
57                 taken = Threads_int_Sleep(THREAD_STAT_SEMAPHORESLEEP,
58                         Sem, MaxToTake,
59                         &Sem->Waiting, &Sem->LastWaiting, &Sem->Protector);
60                 
61                 // Get the lock again
62                 SHORTLOCK( &Sem->Protector );
63         }
64         
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 )
68         {
69                  int    given;
70                 tThread *toWake = Sem->Signaling;
71                 
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;
76                 
77                 // Figure out how much to give
78                 if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
79                         given = toWake->RetStatus;
80                 else
81                         given = Sem->MaxValue - Sem->Value;
82                 Sem->Value -= given;
83                 
84                 
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);
89                 #endif
90                 
91                 // Save the number we gave to the thread's status
92                 toWake->RetStatus = given;
93                 
94                 // Wake the sleeper
95                 if( toWake->Status != THREAD_STAT_ACTIVE )
96                         Threads_AddActive(toWake);
97         }
98         SHORTREL( &Sem->Protector );
99         
100         #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
101         Log("Semaphore %p %s:%s took %i by wait",
102                 Sem, Sem->ModName, Sem->Name, taken);
103         #endif
104
105         return taken;
106 }
107
108 //
109 // Add items to a semaphore
110 //
111 int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
112 {
113          int    given;
114          int    added;
115         
116         if( AmmountToAdd < 0 ) {
117                 Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
118                         AmmountToAdd, Sem, Sem->Name);
119         }
120         SHORTLOCK( &Sem->Protector );
121         
122         // Check if we have to block
123         if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
124         {
125                 #if 0
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);
128                 #endif
129                 added = Threads_int_Sleep(THREAD_STAT_SEMAPHORESLEEP,
130                         Sem, AmmountToAdd,
131                         &Sem->Signaling, &Sem->LastSignaling, &Sem->Protector);
132                 
133                 // Get the lock again
134                 SHORTLOCK( &Sem->Protector );
135         }
136         // Non blocking
137         else
138         {
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;
142                 else
143                         added = AmmountToAdd;
144                 Sem->Value += added;
145         }
146         
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 )
150         {
151                 tThread *toWake = Sem->Waiting;
152                 
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;
157                 
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;
162                 else
163                         given = Sem->Value;
164                 Sem->Value -= given;
165                 
166                 // Save the number we gave to the thread's status
167                 toWake->RetStatus = given;
168                 
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);
175                 #endif
176                 
177                 // Wake the sleeper
178                 if( toWake->Status != THREAD_STAT_ACTIVE )
179                         Threads_AddActive(toWake);
180                 else
181                         Warning("Thread %p (%i %s) is already awake",
182                                 toWake, toWake->TID, toWake->ThreadName);
183         }
184         SHORTREL( &Sem->Protector );
185         
186         return added;
187 }
188
189 void Semaphore_ForceWake(tThread *Thread)
190 {
191         if( !CPU_HAS_LOCK(&Thread->IsLocked) ) {
192                 Log_Error("Semaphore", "Force wake should be called with the thread lock held");
193                 return ;
194         }
195         if( Thread->Status != THREAD_STAT_SEMAPHORESLEEP ) {
196                 Log_Error("Semaphore", "_ForceWake called on non-semaphore thread");
197                 return ;
198         }
199
200         tSemaphore *sem = Thread->WaitPointer;
201         SHORTLOCK( &sem->Protector );
202         tThread *prev = NULL;
203         if( sem->Waiting == Thread )
204                 sem->Waiting = sem->Waiting->Next;
205         else
206         {
207                 for( prev = sem->Waiting; prev && prev->Next != Thread; prev = prev->Next )
208                         ;
209                 if( prev )
210                         prev->Next = Thread->Next;
211         }
212         if( sem->LastWaiting == Thread )
213                 sem->LastWaiting = prev;
214         SHORTREL( &sem->Protector );
215         Thread->RetStatus = 0;
216         Threads_AddActive(Thread);
217 }
218
219 //
220 // Get the current value of a semaphore
221 //
222 int Semaphore_GetValue(tSemaphore *Sem)
223 {
224         return Sem->Value;
225 }
226
227 // === EXPORTS ===
228 EXPORT(Semaphore_Init);
229 EXPORT(Semaphore_Wait);
230 EXPORT(Semaphore_Signal);

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