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

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