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

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