Kernel - Some rational comments to select
[tpg/acess2.git] / Kernel / vfs / select.c
1 /*
2  * Acess2 VFS
3  * - By thePowersGang (John Hodge)
4  * 
5  * select.c
6  * - Implements the select() system call (and supporting code)
7  */
8 #define DEBUG   0
9 #include <acess.h>
10 #include "vfs.h"
11 #include "vfs_int.h"
12 #include "vfs_ext.h"
13 #include <semaphore.h>
14
15 // === CONSTANTS ===
16 #define NUM_THREADS_PER_ALLOC   4
17
18 // === TYPES ===
19 typedef struct sVFS_SelectThread        tVFS_SelectThread;
20 typedef struct sVFS_SelectListEnt       tVFS_SelectListEnt;
21
22 // === STRUCTURES ===
23 struct sVFS_SelectListEnt
24 {
25         tVFS_SelectListEnt      *Next;
26         tVFS_SelectThread       *Threads[NUM_THREADS_PER_ALLOC];
27 };
28
29 struct sVFS_SelectList
30 {
31         tMutex  Lock;
32         tVFS_SelectListEnt      FirstEnt;
33 };
34
35 struct sVFS_SelectThread
36 {
37         //! \brief Marks the thread as actively using this select
38          int    IsActive;
39         //! \brief Semaphore to atomically put the listener to sleep
40         tSemaphore      SleepHandle;    // TODO: Allow timeouts (by setting an alarm?)
41 };
42
43 // === PROTOTYPES ===
44  int    VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, int IsKernel);
45  int    VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull);
46  int    VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable);
47  int    VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState);
48  int    VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
49  int    VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
50  int    VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed);
51 void    VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread);
52
53 // === GLOBALS ===
54
55 // === FUNCTIONS ===
56 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, int IsKernel)
57 {
58         tVFS_SelectThread       thread_info;
59          int    ret;
60         
61         Semaphore_Init(&thread_info.SleepHandle, 0, 0, "VFS_Select()", "");
62         
63         // Notes: The idea is to make sure we onlt enter wait (on the semaphore)
64         // if we are going to be woken up (either by an event at a later time,
65         // or by an event that happened while or before we were registering).
66         // Hence, register must happen _before_ we check the state flag
67         // (See VFS_int_Select_Register), that way either we pick up the flag,
68         // or the semaphore is incremeneted (or both, but never none)
69         
70         // Register with nodes
71         ret  = VFS_int_Select_Register(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
72         ret += VFS_int_Select_Register(&thread_info, MaxHandle, WriteHandles, 1, IsKernel);
73         ret += VFS_int_Select_Register(&thread_info, MaxHandle, ErrHandles, 2, IsKernel);
74         
75         // If there were events waiting, de-register and return
76         if( ret )
77         {
78                 ret = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
79                 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 1, IsKernel);
80                 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 2, IsKernel);
81                 return ret;
82         }
83         
84         // TODO: Implement timeout
85         
86         // Wait (only if there is no timeout, or it is greater than zero
87         if( !Timeout || *Timeout > 0 )
88         {
89                 ret = Semaphore_Wait(&thread_info.SleepHandle, 0);
90         }
91         
92         // Fill output (modify *Handles)
93         // - Also, de-register
94         ret = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
95         ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 1, IsKernel);
96         ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 2, IsKernel);
97         return ret;
98 }
99
100 // --- Internal ---
101 /**
102  * \return Number of files with an action
103  */
104 int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
105 {
106          int    i, numFlagged = 0;
107         tVFS_SelectList **list;
108          int    *flag, wantedFlagValue;
109          int    maxAllowed;
110         
111         if( !Handles )  return 0;
112         
113         for( i = 0; i < MaxHandle; i ++ )
114         {
115                 tVFS_Handle     *handle;
116                 
117                 // Is the descriptor set
118                 if( !FD_ISSET(i, Handles) )     continue;
119                 
120                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
121                 // Is the handle valid?
122                 if( !handle || !handle->Node )
123                 {
124                         if( Type == 2 ) {       // Bad FD counts as an error
125                                 numFlagged ++;
126                         }
127                         else {
128                                 FD_CLR(i, Handles);
129                         }
130                         continue;
131                 }
132         
133                 // Get the type of the listen
134                 switch(Type)
135                 {
136                 case 0: // Read
137                         list = &handle->Node->ReadThreads;
138                         flag = &handle->Node->DataAvaliable;
139                         wantedFlagValue = 1;
140                         maxAllowed = 1; // Max of 1 for read
141                         break;
142                 case 1: // Write
143                         list = &handle->Node->WriteThreads;
144                         flag = &handle->Node->BufferFull;
145                         wantedFlagValue = 0;
146                         maxAllowed = 1; // Max of 1 for write
147                         break;
148                 case 2: // Error
149                         list = &handle->Node->ErrorThreads;
150                         flag = &handle->Node->ErrorOccurred;
151                         wantedFlagValue = 1;
152                         maxAllowed = -1;        // No max for error listeners
153                         break;
154                 default:
155                         Log_Error("VFS", "VFS_int_Select_Deregister: BUG CHECK, Unknown Type %i", Type);
156                         return 0;
157                 }
158                 
159                 // Alloc if needed
160                 if( !*list ) {
161                         *list = calloc(1, sizeof(tVFS_SelectList));
162                 }
163                 
164                 // Register
165                 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
166                 {
167                         // Oops, error (or just no space)
168                         FD_CLR(i, Handles);
169                 }
170                 
171                 // Check for the flag
172                 if( !!*flag == !!wantedFlagValue )
173                         numFlagged ++;
174         }
175         
176         return numFlagged;
177 }
178 /**
179  * \return Number of files with an action
180  */
181 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
182 {
183          int    i, numFlagged = 0;
184         tVFS_SelectList **list;
185          int    *flag, wantedFlagValue;
186         
187         if( !Handles )  return 0;
188         
189         for( i = 0; i < MaxHandle; i ++ )
190         {
191                 tVFS_Handle     *handle;
192                 
193                 // Is the descriptor set
194                 if( !FD_ISSET(i, Handles) )     continue;
195                 
196                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
197                 // Is the handle valid?
198                 if( !handle || !handle->Node )
199                 {
200                         if( Type == 2 ) {       // Bad FD counts as an error
201                                 numFlagged ++;
202                         }
203                         else {
204                                 FD_CLR(i, Handles);
205                         }
206                         continue;
207                 }
208         
209                 // Get the type of the listen
210                 switch(Type)
211                 {
212                 case 0: // Read
213                         list = &handle->Node->ReadThreads;
214                         flag = &handle->Node->DataAvaliable;
215                         wantedFlagValue = 1;
216                         break;
217                 case 1: // Write
218                         list = &handle->Node->WriteThreads;
219                         flag = &handle->Node->BufferFull;
220                         wantedFlagValue = 0;
221                         break;
222                 case 2: // Error
223                         list = &handle->Node->ErrorThreads;
224                         flag = &handle->Node->ErrorOccurred;
225                         wantedFlagValue = 1;
226                         break;
227                 default:
228                         Log_Error("VFS", "VFS_int_Select_Deregister: BUG CHECK, Unknown Type %i", Type);
229                         return 0;
230                 }
231                 
232                 // Remove
233                 VFS_int_Select_RemThread(*list, Thread );
234                 
235                 // Check for the flag
236                 if( !!*flag == !!wantedFlagValue ) {
237                         numFlagged ++;
238                 }
239                 else {
240                         FD_CLR(i, Handles);
241                 }
242         }
243         
244         return numFlagged;
245 }
246
247 /**
248  * \return Boolean failure
249  */
250 int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
251 {
252          int    i, count = 0;
253         tVFS_SelectListEnt      *block, *prev;
254         
255         // Lock to avoid concurrency issues
256         Mutex_Acquire(&List->Lock);
257         
258         block = &List->FirstEnt;
259         
260         // Look for free space
261         do
262         {
263                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
264                 {
265                         if( block->Threads[i] == NULL )
266                         {
267                                 block->Threads[i] = Thread;
268                                 Mutex_Release(&List->Lock);
269                                 return 0;
270                         }
271                         count ++;
272                         if( MaxAllowed && count >= MaxAllowed ) {
273                                 return 1;
274                         }
275                 }
276                 
277                 prev = block;
278                 block = block->Next;
279         } while(block);
280         
281         // Create new block
282         block = malloc( sizeof(tVFS_SelectListEnt) );
283         if( !block ) {
284                 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
285                 Mutex_Release(&List->Lock);
286                 return -1;
287         }
288         block->Next = NULL;
289         block->Threads[0] = Thread;
290         for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
291         {
292                 block->Threads[i] = NULL;
293         }
294         
295         // Add to list
296         prev->Next = block;
297         
298         // Release
299         Mutex_Release(&List->Lock);
300         
301         return 0;
302 }
303
304 void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
305 {
306          int    i;
307         tVFS_SelectListEnt      *block, *prev;
308         
309         // Lock to avoid concurrency issues
310         Mutex_Acquire(&List->Lock);
311         
312         block = &List->FirstEnt;
313         
314         // Look for the thread
315         do
316         {
317                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
318                 {
319                         if( block->Threads[i] == Thread )
320                         {
321                                 block->Threads[i] = NULL;
322                                 
323                                 // Check if this block is empty
324                                 if( block != &List->FirstEnt )
325                                 {
326                                         for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
327                                                 if( block->Threads[i] )
328                                                         break;
329                                         // If empty, free it
330                                         if( i == NUM_THREADS_PER_ALLOC ) {
331                                                 prev->Next = block->Next;
332                                                 free(block);
333                                         }
334                                         //TODO: If not empty, check if it can be merged downwards
335                                 }
336                                 
337                                 Mutex_Release(&List->Lock);
338                                 return ;
339                         }
340                 }
341                 
342                 prev = block;
343                 block = block->Next;
344         } while(block);
345         
346         // Not on list, is this an error?
347 }
348

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