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

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