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

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