85c7d62f1268fe04bcba26bb61a81b08edf99684
[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         }
152         
153         // Fill output (modify *Handles)
154         // - Also, de-register
155         ret  = VFS_int_Select_Deregister(thread_info, MaxHandle, ReadHandles, 0, IsKernel);
156         ret += VFS_int_Select_Deregister(thread_info, MaxHandle, WriteHandles, 1, IsKernel);
157         ret += VFS_int_Select_Deregister(thread_info, MaxHandle, ErrHandles, 2, IsKernel);
158         free(thread_info);
159         LEAVE('i', ret);
160         return ret;
161 }
162
163 // Mark a node as having data ready for reading
164 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
165 {
166         ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
167         Node->DataAvaliable = !!IsDataAvaliable;
168         if( Node->DataAvaliable )
169                 VFS_int_Select_SignalAll(Node->ReadThreads);
170         LEAVE('i', 0);
171         return 0;
172 }
173
174 // Mark a node as having a full buffer
175 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
176 {
177         ENTER("pNode bIsDataAvaliable", Node, IsBufferFull);
178         Node->BufferFull = !!IsBufferFull;
179         if( !Node->BufferFull )
180                 VFS_int_Select_SignalAll(Node->WriteThreads);
181         LEAVE('i', 0);
182         return 0;
183 }
184
185 // Mark a node as errored
186 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
187 {
188         ENTER("pNode bIsDataAvaliable", Node, IsErrorState);
189         Node->ErrorOccurred = !!IsErrorState;
190         if( Node->ErrorOccurred )
191                 VFS_int_Select_SignalAll(Node->ErrorThreads);
192         LEAVE('i', 0);
193         return 0;
194 }
195
196 // --- Internal ---
197 int VFS_int_Select_GetType(enum eVFS_SelectTypes Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
198 {
199         // Get the type of the listen
200         switch(Type)
201         {
202         case 0: // Read
203                 if(List)        *List = &Node->ReadThreads;
204                 if(Flag)        *Flag = &Node->DataAvaliable;
205                 if(WantedFlag)  *WantedFlag = 1;
206                 if(MaxAllowed)  *MaxAllowed = 1;        // Max of 1 for read
207                 break;
208         case 1: // Write
209                 if(List)        *List = &Node->WriteThreads;
210                 if(Flag)        *Flag = &Node->BufferFull;
211                 if(WantedFlag)  *WantedFlag = 0;
212                 if(MaxAllowed)  *MaxAllowed = 1;        // Max of 1 for write
213                 break;
214         case 2: // Error
215                 if(List)        *List = &Node->ErrorThreads;
216                 if(Flag)        *Flag = &Node->ErrorOccurred;
217                 if(WantedFlag)  *WantedFlag = 1;
218                 if(MaxAllowed)  *MaxAllowed = -1;       // No max for error listeners
219                 break;
220         default:
221                 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
222                 return 1;
223         }
224         return 0;
225 }
226
227 /**
228  * \return Number of files with an action
229  */
230 int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel)
231 {
232          int    i, numFlagged = 0;
233         tVFS_SelectList **list;
234          int    *flag, wantedFlagValue;
235          int    maxAllowed;
236         
237         if( !Handles )  return 0;
238         
239         ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
240         
241         for( i = 0; i < MaxHandle; i ++ )
242         {
243                 tVFS_Handle     *handle;
244                 
245                 // Is the descriptor set
246                 if( !FD_ISSET(i, Handles) )     continue;
247                 LOG("FD #%i", i);
248                 
249                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
250                 // Is the handle valid?
251                 if( !handle || !handle->Node )
252                 {
253                         if( Type == 2 ) {       // Bad FD counts as an error
254                                 numFlagged ++;
255                         }
256                         else {
257                                 FD_CLR(i, Handles);
258                         }
259                         continue;
260                 }
261         
262                 // Get the type of the listen
263                 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
264                         LEAVE('i', 0);
265                         return 0;
266                 }
267                 
268                 // Alloc if needed
269                 if( !*list ) {
270                         *list = calloc(1, sizeof(tVFS_SelectList));
271                 }
272                 
273                 // Register
274                 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
275                 {
276                         // Oops, error (or just no space)
277                         FD_CLR(i, Handles);
278                 }
279                 
280                 // Check for the flag
281                 if( !!*flag == !!wantedFlagValue )
282                         numFlagged ++;
283         }
284         
285         LEAVE('i', numFlagged);
286         
287         return numFlagged;
288 }
289 /**
290  * \return Number of files with an action
291  */
292 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel)
293 {
294          int    i, numFlagged = 0;
295         tVFS_SelectList **list;
296          int    *flag, wantedFlagValue;
297         
298         if( !Handles )  return 0;
299         
300         ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
301         
302         for( i = 0; i < MaxHandle; i ++ )
303         {
304                 tVFS_Handle     *handle;
305                 
306                 // Is the descriptor set
307                 if( !FD_ISSET(i, Handles) )     continue;
308                 LOG("FD #%i", i);
309                 
310                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
311                 // Is the handle valid?
312                 if( !handle || !handle->Node )
313                 {
314                         if( Type == 2 ) {       // Bad FD counts as an error
315                                 numFlagged ++;
316                         }
317                         else {
318                                 FD_CLR(i, Handles);
319                         }
320                         continue;
321                 }
322         
323                 // Get the type of the listen
324         
325                 // Get the type of the listen
326                 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
327                         LEAVE('i', 0);
328                         return 0;
329                 }
330                 
331                 // Remove
332                 VFS_int_Select_RemThread(*list, Thread );
333                 
334                 // Check for the flag
335                 if( !!*flag == !!wantedFlagValue ) {
336                         numFlagged ++;
337                 }
338                 else {
339                         FD_CLR(i, Handles);
340                 }
341         }
342         
343         LEAVE('i', numFlagged);
344         
345         return numFlagged;
346 }
347
348 /**
349  * \return Boolean failure
350  */
351 int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
352 {
353          int    i, count = 0;
354         tVFS_SelectListEnt      *block, *prev;
355         
356         ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
357         
358         // Lock to avoid concurrency issues
359         Mutex_Acquire(&List->Lock);
360         
361         block = &List->FirstEnt;
362         
363         // Look for free space
364         do
365         {
366                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
367                 {
368                         if( block->Threads[i] == NULL )
369                         {
370                                 block->Threads[i] = Thread;
371                                 Mutex_Release(&List->Lock);
372                                 LEAVE('i', 0);
373                                 return 0;
374                         }
375                         count ++;
376                         if( MaxAllowed && count >= MaxAllowed ) {
377                                 LEAVE('i', 1);
378                                 return 1;
379                         }
380                 }
381                 
382                 prev = block;
383                 block = block->Next;
384         } while(block);
385         
386         LOG("New block");
387         
388         // Create new block
389         block = malloc( sizeof(tVFS_SelectListEnt) );
390         if( !block ) {
391                 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
392                 Mutex_Release(&List->Lock);
393                 return -1;
394         }
395         block->Next = NULL;
396         block->Threads[0] = Thread;
397         for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
398         {
399                 block->Threads[i] = NULL;
400         }
401         
402         // Add to list
403         prev->Next = block;
404         
405         // Release
406         Mutex_Release(&List->Lock);
407         
408         LEAVE('i', 0);
409         return 0;
410 }
411
412 void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
413 {
414          int    i;
415         tVFS_SelectListEnt      *block, *prev;
416         
417         ENTER("pList pThread", List, Thread);
418         
419         // Lock to avoid concurrency issues
420         Mutex_Acquire(&List->Lock);
421         
422         block = &List->FirstEnt;
423         
424         // Look for the thread
425         do
426         {
427                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
428                 {
429                         if( block->Threads[i] == Thread )
430                         {
431                                 block->Threads[i] = NULL;
432                                 
433                                 // Check if this block is empty
434                                 if( block != &List->FirstEnt )
435                                 {
436                                         for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
437                                                 if( block->Threads[i] )
438                                                         break;
439                                         // If empty, free it
440                                         if( i == NUM_THREADS_PER_ALLOC ) {
441                                                 LOG("Deleting block");
442                                                 prev->Next = block->Next;
443                                                 free(block);
444                                         }
445                                         //TODO: If not empty, check if it can be merged downwards
446                                 }
447                                 
448                                 Mutex_Release(&List->Lock);
449                                 LEAVE('-');
450                                 return ;
451                         }
452                 }
453                 
454                 prev = block;
455                 block = block->Next;
456         } while(block);
457         
458         // Not on list, is this an error?
459         
460         Mutex_Release(&List->Lock);
461         
462         LOG("Not on list");
463         LEAVE('-');
464 }
465
466 /**
467  * \brief Signal all threads on a list
468  */
469 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
470 {
471          int    i;
472         tVFS_SelectListEnt      *block;
473         
474         if( !List )     return ;
475         
476         ENTER("pList", List);
477         
478         // Lock to avoid concurrency issues
479         Mutex_Acquire(&List->Lock);
480         
481         block = &List->FirstEnt;
482         
483         // Look for the thread
484         do
485         {
486                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
487                 {
488                         LOG("block->Threads[i] = %p", block->Threads[i]);
489                         if( block->Threads[i]  )
490                         {
491                                 Semaphore_Signal( &block->Threads[i]->SleepHandle, 1 );
492                         }
493                 }
494                 
495                 block = block->Next;
496         } while(block);
497         
498         Mutex_Release(&List->Lock);
499         
500         LEAVE('-');
501 }

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