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

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