660bca89611731009e210aa53f59500b4d80efa4
[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: Remove malloc for read/write queues
9  */
10 #define DEBUG   0
11 #include <acess.h>
12 #include "vfs.h"
13 #include "vfs_int.h"
14 #include "vfs_ext.h"
15 #include <semaphore.h>
16 #include <threads.h>
17 #include <events.h>
18 #include <timers.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         // Wait for things      
87         if( !Timeout )
88         {
89                 LOG("Semaphore_Wait()");
90                 // TODO: Actual timeout
91                 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_SIGNAL );
92         }
93         else if( *Timeout > 0 )
94         {
95                 tTimer *t = Time_AllocateTimer(NULL, NULL);
96                 // Clear timer event
97                 Threads_ClearEvent( THREAD_EVENT_TIMER );
98                 // TODO: Convert *Timeout?
99                 LOG("Timeout %lli ms", *Timeout);
100                 Time_ScheduleTimer( t, *Timeout );
101                 // Wait for the timer or a VFS event
102                 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_TIMER|THREAD_EVENT_SIGNAL );
103                 Time_FreeTimer(t);
104         }
105         
106         // Get return value
107         ret = 0;
108         for( type = 0; type < 3; type ++ )
109         {
110                 tVFS_SelectList **list;
111                  int    *flag, wanted, maxAllowed;
112                 if( !(TypeFlags & (1 << type)) )        continue;
113                 VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed);
114                 LOG("VFS_int_Select_RemThread()");
115                 ASSERT(*list);
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("Waiting for VFS/SIGNAL events (Plus 0x%x)", ExtraEvents);
167                 // TODO: Actual timeout
168                 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_SIGNAL|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         LOG("De-registering");
184         // - Also, de-register
185         ret  = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
186         ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
187         ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
188         
189         Threads_ClearEvent( THREAD_EVENT_VFS );
190         Threads_ClearEvent( THREAD_EVENT_TIMER );
191         
192         LEAVE('i', ret);
193         return ret;
194 }
195
196 // Mark a node as having data ready for reading
197 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
198 {
199         ASSERTR(Node, 1);
200         ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
201         Node->DataAvaliable = !!IsDataAvaliable;
202         if( Node->DataAvaliable )
203                 VFS_int_Select_SignalAll(Node->ReadThreads);
204         LEAVE('i', 0);
205         return 0;
206 }
207
208 // Mark a node as having a full buffer
209 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
210 {
211         ASSERTR(Node, 1);
212         ENTER("pNode bIsBufferFull", Node, IsBufferFull);
213         Node->BufferFull = !!IsBufferFull;
214         if( !Node->BufferFull )
215                 VFS_int_Select_SignalAll(Node->WriteThreads);
216         LEAVE('i', 0);
217         return 0;
218 }
219
220 // Mark a node as errored
221 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
222 {
223         ASSERTR(Node, 1);
224         ENTER("pNode bIsErrorState", Node, IsErrorState);
225         Node->ErrorOccurred = !!IsErrorState;
226         if( Node->ErrorOccurred )
227                 VFS_int_Select_SignalAll(Node->ErrorThreads);
228         LEAVE('i', 0);
229         return 0;
230 }
231
232 // --- Internal ---
233 int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
234 {
235         // Get the type of the listen
236         switch(Type)
237         {
238         case 0: // Read
239                 if(List)        *List = &Node->ReadThreads;
240                 if(Flag)        *Flag = &Node->DataAvaliable;
241                 if(WantedFlag)  *WantedFlag = 1;
242                 if(MaxAllowed)  *MaxAllowed = 1;        // Max of 1 for read
243                 break;
244         case 1: // Write
245                 if(List)        *List = &Node->WriteThreads;
246                 if(Flag)        *Flag = &Node->BufferFull;
247                 if(WantedFlag)  *WantedFlag = 0;
248                 if(MaxAllowed)  *MaxAllowed = 1;        // Max of 1 for write
249                 break;
250         case 2: // Error
251                 if(List)        *List = &Node->ErrorThreads;
252                 if(Flag)        *Flag = &Node->ErrorOccurred;
253                 if(WantedFlag)  *WantedFlag = 1;
254                 if(MaxAllowed)  *MaxAllowed = -1;       // No max for error listeners
255                 break;
256         default:
257                 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
258                 return 1;
259         }
260         return 0;
261 }
262
263 /**
264  * \return Number of files with an action
265  */
266 int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
267 {
268          int    i, numFlagged = 0;
269         tVFS_SelectList **list;
270          int    *flag, wantedFlagValue;
271          int    maxAllowed;
272         
273         if( !Handles )  return 0;
274         
275         ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
276         
277         for( i = 0; i < MaxHandle; i ++ )
278         {
279                 tVFS_Handle     *handle;
280                 
281                 // Is the descriptor set
282                 if( !FD_ISSET(i, Handles) )     continue;
283                 LOG("FD #%i", i);
284                 
285                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
286                 // Is the handle valid?
287                 if( !handle || !handle->Node )
288                 {
289                         if( Type == 2 ) {       // Bad FD counts as an error
290                                 numFlagged ++;
291                         }
292                         else {
293                                 FD_CLR(i, Handles);
294                         }
295                         continue;
296                 }
297         
298                 // Get the type of the listen
299                 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
300                         LEAVE('i', 0);
301                         return 0;
302                 }
303                 
304                 // Alloc if needed
305                 if( !*list ) {
306                         *list = calloc(1, sizeof(tVFS_SelectList));
307                 }
308                 
309                 // Register
310                 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
311                 {
312                         // Oops, error (or just no space)
313                         FD_CLR(i, Handles);
314                 }
315                 
316                 // Check for the flag
317                 if( !!*flag == !!wantedFlagValue ) {
318                         LOG(" %i == want %i", !!*flag, !!wantedFlagValue);
319                         numFlagged ++;
320                 }
321         }
322         
323         LEAVE('i', numFlagged);
324         
325         return numFlagged;
326 }
327 /**
328  * \return Number of files with an action
329  */
330 int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
331 {
332          int    i, numFlagged = 0;
333         tVFS_SelectList **list;
334          int    *flag, wantedFlagValue;
335         
336         if( !Handles )  return 0;
337         
338         ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
339         
340         for( i = 0; i < MaxHandle; i ++ )
341         {
342                 tVFS_Handle     *handle;
343                 
344                 // Is the descriptor set
345                 if( !FD_ISSET(i, Handles) )     continue;
346                 LOG("FD #%i", i);
347                 
348                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
349                 // Is the handle valid?
350                 if( !handle || !handle->Node )
351                 {
352                         if( Type == 2 ) {       // Bad FD counts as an error
353                                 numFlagged ++;
354                         }
355                         else {
356                                 FD_CLR(i, Handles);
357                         }
358                         continue;
359                 }
360         
361                 // Get the type of the listen
362                 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
363                         LEAVE('i', 0);
364                         return 0;
365                 }
366                 
367                 // Remove
368                 VFS_int_Select_RemThread(*list, Thread );
369                 
370                 // Check for the flag
371                 if( !!*flag == !!wantedFlagValue ) {
372                         numFlagged ++;
373                         LOG(" %i == want %i", !!*flag, !!wantedFlagValue);
374                         FD_SET(i, Handles);
375                 }
376                 else {
377                         FD_CLR(i, Handles);
378                 }
379         }
380         
381         LEAVE('i', numFlagged);
382         
383         return numFlagged;
384 }
385
386 /**
387  * \return Boolean failure
388  */
389 int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed)
390 {
391          int    i, count = 0;
392         tVFS_SelectListEnt      *block, *prev;
393         
394         ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
395         
396         // Lock to avoid concurrency issues
397         Mutex_Acquire(&List->Lock);
398         
399         block = &List->FirstEnt;
400         
401         // Look for free space
402         do
403         {
404                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
405                 {
406                         if( block->Threads[i] == NULL )
407                         {
408                                 block->Threads[i] = Thread;
409                                 Mutex_Release(&List->Lock);
410                                 LEAVE('i', 0);
411                                 return 0;
412                         }
413                         count ++;
414                         if( MaxAllowed && count >= MaxAllowed ) {
415                                 Mutex_Release(&List->Lock);
416                                 LEAVE('i', 1);
417                                 return 1;
418                         }
419                 }
420                 
421                 prev = block;
422                 block = block->Next;
423         } while(block);
424         
425         LOG("New block");
426         
427         // Create new block
428         block = malloc( sizeof(tVFS_SelectListEnt) );
429         if( !block ) {
430                 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
431                 Mutex_Release(&List->Lock);
432                 return -1;
433         }
434         block->Next = NULL;
435         block->Threads[0] = Thread;
436         for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
437         {
438                 block->Threads[i] = NULL;
439         }
440         
441         // Add to list
442         prev->Next = block;
443         
444         // Release
445         Mutex_Release(&List->Lock);
446         
447         LEAVE('i', 0);
448         return 0;
449 }
450
451 void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread)
452 {
453         tVFS_SelectListEnt      *block, *prev = NULL;
454         
455         ENTER("pList pThread", List, Thread);
456         
457         // Lock to avoid concurrency issues
458         Mutex_Acquire(&List->Lock);
459         
460         block = &List->FirstEnt;
461         
462         // Look for the thread
463         do
464         {
465                 for( int i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
466                 {
467                         if( block->Threads[i] == Thread )
468                         {
469                                 block->Threads[i] = NULL;
470                                 
471                                 // Check if this block is empty
472                                 if( block != &List->FirstEnt )
473                                 {
474                                         for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
475                                                 if( block->Threads[i] )
476                                                         break;
477                                         // If empty, free it
478                                         if( i == NUM_THREADS_PER_ALLOC ) {
479                                                 LOG("Deleting block");
480                                                 prev->Next = block->Next;
481                                                 free(block);
482                                         }
483                                         //TODO: If not empty, check if it can be merged downwards
484                                 }
485                                 
486                                 Mutex_Release(&List->Lock);
487                                 LEAVE('-');
488                                 return ;
489                         }
490                 }
491                 
492                 prev = block;
493                 block = block->Next;
494         } while(block);
495         
496         // Not on list, is this an error?
497         
498         Mutex_Release(&List->Lock);
499         
500         LOG("Not on list");
501         LEAVE('-');
502 }
503
504 /**
505  * \brief Signal all threads on a list
506  */
507 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
508 {
509          int    i;
510         tVFS_SelectListEnt      *block;
511         
512         if( !List )     return ;
513         
514         ENTER("pList", List);
515         
516         // Lock to avoid concurrency issues
517         Mutex_Acquire(&List->Lock);
518         
519         block = &List->FirstEnt;
520         
521         // Look for the thread
522         do
523         {
524                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
525                 {
526                         if( block->Threads[i]  )
527                         {
528                                 LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]);
529                                 Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS );
530                         }
531                 }
532                 
533                 block = block->Next;
534         } while(block);
535         
536         Mutex_Release(&List->Lock);
537         
538         LEAVE('-');
539 }

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