3f8193b841e652ff46738953d83768fe882cc10e
[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   1
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 Semaphore to atomically put the listener to sleep
38         tSemaphore      SleepHandle;    // TODO: Allow timeouts (by setting an alarm?)
39 };
40
41 // === PROTOTYPES ===
42 // int  VFS_SelectNode(tVFS_Node *Node, enum eVFS_SelectTypes Type, tTime *Timeout);
43 // int  VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, BOOL IsKernel);
44 // int  VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull);
45 // int  VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable);
46 // int  VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState);
47  int    VFS_int_Select_GetType(enum eVFS_SelectTypes Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed);
48  int    VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel);
49  int    VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes 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 void    VFS_int_Select_SignalAll(tVFS_SelectList *List);
53
54 // === GLOBALS ===
55
56 // === FUNCTIONS ===
57 int VFS_SelectNode(tVFS_Node *Node, enum eVFS_SelectTypes Type, tTime *Timeout)
58 {
59         tVFS_SelectThread       thread_info;
60         tVFS_SelectList **list;
61          int    *flag, wanted, maxAllowed;
62         
63         ENTER("pNode iType pTimeout", Node, Type, Timeout);
64         
65         Semaphore_Init(&thread_info.SleepHandle, 0, 0, "VFS_SelectNode()", "");
66         
67         if( VFS_int_Select_GetType(Type, Node, &list, &flag, &wanted, &maxAllowed) ) {
68                 LEAVE('i', -1);
69                 return -1;
70         }
71         
72         VFS_int_Select_AddThread(*list, &thread_info, maxAllowed);
73         if( *flag == wanted )
74         {
75                 VFS_int_Select_RemThread(*list, &thread_info);
76                 LEAVE('i', 1);
77                 return 1;
78         }
79         
80         if( !Timeout || *Timeout > 0 )
81         {
82                 // TODO: Actual timeout
83                 Semaphore_Wait(&thread_info.SleepHandle, 0);
84         }
85         
86         VFS_int_Select_RemThread(*list, &thread_info);
87         
88         LEAVE('i', *flag == wanted);
89         return *flag == wanted;
90 }
91
92 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, BOOL IsKernel)
93 {
94         tVFS_SelectThread       thread_info;
95          int    ret;
96         
97         Semaphore_Init(&thread_info.SleepHandle, 0, 0, "VFS_Select()", "");
98         
99         // Notes: The idea is to make sure we onlt enter wait (on the semaphore)
100         // if we are going to be woken up (either by an event at a later time,
101         // or by an event that happened while or before we were registering).
102         // Hence, register must happen _before_ we check the state flag
103         // (See VFS_int_Select_Register), that way either we pick up the flag,
104         // or the semaphore is incremeneted (or both, but never none)
105         
106         // Register with nodes
107         ret  = VFS_int_Select_Register(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
108         ret += VFS_int_Select_Register(&thread_info, MaxHandle, WriteHandles, 1, IsKernel);
109         ret += VFS_int_Select_Register(&thread_info, MaxHandle, ErrHandles, 2, IsKernel);
110         
111         // If there were events waiting, de-register and return
112         if( ret )
113         {
114                 ret  = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
115                 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, WriteHandles, 1, IsKernel);
116                 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ErrHandles, 2, IsKernel);
117                 return ret;
118         }
119         
120         // TODO: Implement timeout
121         
122         // Wait (only if there is no timeout, or it is greater than zero
123         if( !Timeout || *Timeout > 0 )
124         {
125                 ret = Semaphore_Wait(&thread_info.SleepHandle, 0);
126         }
127         
128         // Fill output (modify *Handles)
129         // - Also, de-register
130         ret  = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
131         ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, WriteHandles, 1, IsKernel);
132         ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ErrHandles, 2, IsKernel);
133         return ret;
134 }
135
136 // Mark a node as having data ready for reading
137 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
138 {
139         Node->DataAvaliable = !!IsDataAvaliable;
140         if( Node->DataAvaliable )
141                 VFS_int_Select_SignalAll(Node->ReadThreads);
142         return 0;
143 }
144
145 // Mark a node as having a full buffer
146 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
147 {
148         Node->BufferFull = !!IsBufferFull;
149         if( !Node->BufferFull )
150                 VFS_int_Select_SignalAll(Node->WriteThreads);
151         return 0;
152 }
153
154 // Mark a node as errored
155 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
156 {
157         Node->ErrorOccurred = !!IsErrorState;
158         if( Node->ErrorOccurred )
159                 VFS_int_Select_SignalAll(Node->ErrorThreads);
160         return 0;
161 }
162
163 // --- Internal ---
164 int VFS_int_Select_GetType(enum eVFS_SelectTypes Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
165 {
166         // Get the type of the listen
167         switch(Type)
168         {
169         case 0: // Read
170                 if(List)        *List = &Node->ReadThreads;
171                 if(Flag)        *Flag = &Node->DataAvaliable;
172                 if(WantedFlag)  *WantedFlag = 1;
173                 if(MaxAllowed)  *MaxAllowed = 1;        // Max of 1 for read
174                 break;
175         case 1: // Write
176                 if(List)        *List = &Node->WriteThreads;
177                 if(Flag)        *Flag = &Node->BufferFull;
178                 if(WantedFlag)  *WantedFlag = 0;
179                 if(MaxAllowed)  *MaxAllowed = 1;        // Max of 1 for write
180                 break;
181         case 2: // Error
182                 if(List)        *List = &Node->ErrorThreads;
183                 if(Flag)        *Flag = &Node->ErrorOccurred;
184                 if(WantedFlag)  *WantedFlag = 1;
185                 if(MaxAllowed)  *MaxAllowed = -1;       // No max for error listeners
186                 break;
187         default:
188                 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
189                 return 1;
190         }
191         return 0;
192 }
193
194 /**
195  * \return Number of files with an action
196  */
197 int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel)
198 {
199          int    i, numFlagged = 0;
200         tVFS_SelectList **list;
201          int    *flag, wantedFlagValue;
202          int    maxAllowed;
203         
204         if( !Handles )  return 0;
205         
206         for( i = 0; i < MaxHandle; i ++ )
207         {
208                 tVFS_Handle     *handle;
209                 
210                 // Is the descriptor set
211                 if( !FD_ISSET(i, Handles) )     continue;
212                 
213                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
214                 // Is the handle valid?
215                 if( !handle || !handle->Node )
216                 {
217                         if( Type == 2 ) {       // Bad FD counts as an error
218                                 numFlagged ++;
219                         }
220                         else {
221                                 FD_CLR(i, Handles);
222                         }
223                         continue;
224                 }
225         
226                 // Get the type of the listen
227                 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) )
228                         return 0;
229                 
230                 // Alloc if needed
231                 if( !*list ) {
232                         *list = calloc(1, sizeof(tVFS_SelectList));
233                 }
234                 
235                 // Register
236                 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
237                 {
238                         // Oops, error (or just no space)
239                         FD_CLR(i, Handles);
240                 }
241                 
242                 // Check for the flag
243                 if( !!*flag == !!wantedFlagValue )
244                         numFlagged ++;
245         }
246         
247         return numFlagged;
248 }
249 /**
250  * \return Number of files with an action
251  */
252 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel)
253 {
254          int    i, numFlagged = 0;
255         tVFS_SelectList **list;
256          int    *flag, wantedFlagValue;
257         
258         if( !Handles )  return 0;
259         
260         for( i = 0; i < MaxHandle; i ++ )
261         {
262                 tVFS_Handle     *handle;
263                 
264                 // Is the descriptor set
265                 if( !FD_ISSET(i, Handles) )     continue;
266                 
267                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
268                 // Is the handle valid?
269                 if( !handle || !handle->Node )
270                 {
271                         if( Type == 2 ) {       // Bad FD counts as an error
272                                 numFlagged ++;
273                         }
274                         else {
275                                 FD_CLR(i, Handles);
276                         }
277                         continue;
278                 }
279         
280                 // Get the type of the listen
281         
282                 // Get the type of the listen
283                 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) )
284                         return 0;
285                 
286                 // Remove
287                 VFS_int_Select_RemThread(*list, Thread );
288                 
289                 // Check for the flag
290                 if( !!*flag == !!wantedFlagValue ) {
291                         numFlagged ++;
292                 }
293                 else {
294                         FD_CLR(i, Handles);
295                 }
296         }
297         
298         return numFlagged;
299 }
300
301 /**
302  * \return Boolean failure
303  */
304 int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
305 {
306          int    i, count = 0;
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 free space
315         do
316         {
317                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
318                 {
319                         if( block->Threads[i] == NULL )
320                         {
321                                 block->Threads[i] = Thread;
322                                 Mutex_Release(&List->Lock);
323                                 return 0;
324                         }
325                         count ++;
326                         if( MaxAllowed && count >= MaxAllowed ) {
327                                 return 1;
328                         }
329                 }
330                 
331                 prev = block;
332                 block = block->Next;
333         } while(block);
334         
335         // Create new block
336         block = malloc( sizeof(tVFS_SelectListEnt) );
337         if( !block ) {
338                 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
339                 Mutex_Release(&List->Lock);
340                 return -1;
341         }
342         block->Next = NULL;
343         block->Threads[0] = Thread;
344         for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
345         {
346                 block->Threads[i] = NULL;
347         }
348         
349         // Add to list
350         prev->Next = block;
351         
352         // Release
353         Mutex_Release(&List->Lock);
354         
355         return 0;
356 }
357
358 void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
359 {
360          int    i;
361         tVFS_SelectListEnt      *block, *prev;
362         
363         // Lock to avoid concurrency issues
364         Mutex_Acquire(&List->Lock);
365         
366         block = &List->FirstEnt;
367         
368         // Look for the thread
369         do
370         {
371                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
372                 {
373                         if( block->Threads[i] == Thread )
374                         {
375                                 block->Threads[i] = NULL;
376                                 
377                                 // Check if this block is empty
378                                 if( block != &List->FirstEnt )
379                                 {
380                                         for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
381                                                 if( block->Threads[i] )
382                                                         break;
383                                         // If empty, free it
384                                         if( i == NUM_THREADS_PER_ALLOC ) {
385                                                 prev->Next = block->Next;
386                                                 free(block);
387                                         }
388                                         //TODO: If not empty, check if it can be merged downwards
389                                 }
390                                 
391                                 Mutex_Release(&List->Lock);
392                                 return ;
393                         }
394                 }
395                 
396                 prev = block;
397                 block = block->Next;
398         } while(block);
399         
400         // Not on list, is this an error?
401         
402         Mutex_Release(&List->Lock);
403 }
404
405 /**
406  * \brief Signal all threads on a list
407  */
408 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
409 {
410          int    i;
411         tVFS_SelectListEnt      *block, *prev;
412         
413         // Lock to avoid concurrency issues
414         Mutex_Acquire(&List->Lock);
415         
416         block = &List->FirstEnt;
417         
418         // Look for the thread
419         do
420         {
421                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
422                 {
423                         if( block->Threads[i]  )
424                         {
425                                 Semaphore_Signal( &block->Threads[i]->SleepHandle, 1 );
426                         }
427                 }
428                 
429                 prev = block;
430                 block = block->Next;
431         } while(block);
432         
433         Mutex_Release(&List->Lock);
434 }

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