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

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