Kernel - Implementing select()
[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 Marks the thread as actively using this select
38          int    IsActive;
39         //! \brief Semaphore to atomically put the listener to sleep
40         tSemaphore      SleepHandle;    // TODO: Allow timeouts (by setting an alarm?)
41 };
42
43 // === PROTOTYPES ===
44  int    VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, int IsKernel);
45  int    VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull);
46  int    VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable);
47  int    VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState);
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
53 // === GLOBALS ===
54
55 // === FUNCTIONS ===
56 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, int IsKernel)
57 {
58         tVFS_SelectThread       thread_info;
59          int    ret;
60         
61         // Register with nodes
62         ret  = VFS_int_Select_Register(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
63         ret += VFS_int_Select_Register(&thread_info, MaxHandle, WriteHandles, 1, IsKernel);
64         ret += VFS_int_Select_Register(&thread_info, MaxHandle, ErrHandles, 2, IsKernel);
65         
66         // If there were events waiting, de-register and return
67         if( ret )
68         {
69                 ret = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
70                 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 1, IsKernel);
71                 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 2, IsKernel);
72                 return ret;
73         }
74         
75         // TODO: Implement timeout
76         
77         // Wait (only if there is no timeout, or it is greater than zero
78         if( !Timeout || *Timeout > 0 )
79         {
80                 ret = Semaphore_Wait(&thread_info.SleepHandle, 0);
81         }
82         
83         // Fill output (modify *Handles)
84         // - Also, de-register
85         ret = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
86         ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 1, IsKernel);
87         ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 2, IsKernel);
88         return ret;
89 }
90
91 // --- Internal ---
92 /**
93  * \return Number of files with an action
94  */
95 int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
96 {
97          int    i, numFlagged = 0;
98         tVFS_SelectList **list;
99          int    *flag, wantedFlagValue;
100          int    maxAllowed;
101         
102         if( !Handles )  return 0;
103         
104         for( i = 0; i < MaxHandle; i ++ )
105         {
106                 tVFS_Handle     *handle;
107                 
108                 // Is the descriptor set
109                 if( !FD_ISSET(i, Handles) )     continue;
110                 
111                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
112                 // Is the handle valid?
113                 if( !handle || !handle->Node )
114                 {
115                         if( Type == 2 ) {       // Bad FD counts as an error
116                                 numFlagged ++;
117                         }
118                         else {
119                                 FD_CLR(i, Handles);
120                         }
121                         continue;
122                 }
123         
124                 // Get the type of the listen
125                 switch(Type)
126                 {
127                 case 0: // Read
128                         list = &handle->Node->ReadThreads;
129                         flag = &handle->Node->DataAvaliable;
130                         wantedFlagValue = 1;
131                         maxAllowed = 1; // Max of 1 for read
132                         break;
133                 case 1: // Write
134                         list = &handle->Node->WriteThreads;
135                         flag = &handle->Node->BufferFull;
136                         wantedFlagValue = 0;
137                         maxAllowed = 1; // Max of 1 for write
138                         break;
139                 case 2: // Error
140                         list = &handle->Node->ErrorThreads;
141                         flag = &handle->Node->ErrorOccurred;
142                         wantedFlagValue = 1;
143                         maxAllowed = -1;        // No max for error listeners
144                         break;
145                 default:
146                         Log_Error("VFS", "VFS_int_Select_Deregister: BUG CHECK, Unknown Type %i", Type);
147                         return 0;
148                 }
149                 
150                 // Alloc if needed
151                 if( !*list ) {
152                         *list = calloc(1, sizeof(tVFS_SelectList));
153                 }
154                 
155                 // Register
156                 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
157                 {
158                         // Oops, error (or just no space)
159                         FD_CLR(i, Handles);
160                 }
161                 
162                 // Check for the flag
163                 if( !!*flag == !!wantedFlagValue )
164                         numFlagged ++;
165         }
166         
167         return numFlagged;
168 }
169 /**
170  * \return Number of files with an action
171  */
172 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
173 {
174          int    i, numFlagged = 0;
175         tVFS_SelectList **list;
176          int    *flag, wantedFlagValue;
177         
178         if( !Handles )  return 0;
179         
180         for( i = 0; i < MaxHandle; i ++ )
181         {
182                 tVFS_Handle     *handle;
183                 
184                 // Is the descriptor set
185                 if( !FD_ISSET(i, Handles) )     continue;
186                 
187                 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
188                 // Is the handle valid?
189                 if( !handle || !handle->Node )
190                 {
191                         if( Type == 2 ) {       // Bad FD counts as an error
192                                 numFlagged ++;
193                         }
194                         else {
195                                 FD_CLR(i, Handles);
196                         }
197                         continue;
198                 }
199         
200                 // Get the type of the listen
201                 switch(Type)
202                 {
203                 case 0: // Read
204                         list = &handle->Node->ReadThreads;
205                         flag = &handle->Node->DataAvaliable;
206                         wantedFlagValue = 1;
207                         break;
208                 case 1: // Write
209                         list = &handle->Node->WriteThreads;
210                         flag = &handle->Node->BufferFull;
211                         wantedFlagValue = 0;
212                         break;
213                 case 2: // Error
214                         list = &handle->Node->ErrorThreads;
215                         flag = &handle->Node->ErrorOccurred;
216                         wantedFlagValue = 1;
217                         break;
218                 default:
219                         Log_Error("VFS", "VFS_int_Select_Deregister: BUG CHECK, Unknown Type %i", Type);
220                         return 0;
221                 }
222                 
223                 // Remove
224                 VFS_int_Select_RemThread(*list, Thread );
225                 
226                 // Check for the flag
227                 if( !!*flag == !!wantedFlagValue ) {
228                         numFlagged ++;
229                 }
230                 else {
231                         FD_CLR(i, Handles);
232                 }
233         }
234         
235         return numFlagged;
236 }
237
238 /**
239  * \return Boolean failure
240  */
241 int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
242 {
243          int    i, count = 0;
244         tVFS_SelectListEnt      *block, *prev;
245         
246         // Lock to avoid concurrency issues
247         Mutex_Acquire(&List->Lock);
248         
249         block = &List->FirstEnt;
250         
251         // Look for free space
252         do
253         {
254                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
255                 {
256                         if( block->Threads[i] == NULL )
257                         {
258                                 block->Threads[i] = Thread;
259                                 Mutex_Release(&List->Lock);
260                                 return 0;
261                         }
262                         count ++;
263                         if( MaxAllowed && count >= MaxAllowed ) {
264                                 return 1;
265                         }
266                 }
267                 
268                 prev = block;
269                 block = block->Next;
270         } while(block);
271         
272         // Create new block
273         block = malloc( sizeof(tVFS_SelectListEnt) );
274         if( !block ) {
275                 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
276                 Mutex_Release(&List->Lock);
277                 return -1;
278         }
279         block->Next = NULL;
280         block->Threads[0] = Thread;
281         for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
282         {
283                 block->Threads[i] = NULL;
284         }
285         
286         // Add to list
287         prev->Next = block;
288         
289         // Release
290         Mutex_Release(&List->Lock);
291         
292         return 0;
293 }
294
295 void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
296 {
297          int    i;
298         tVFS_SelectListEnt      *block, *prev;
299         
300         // Lock to avoid concurrency issues
301         Mutex_Acquire(&List->Lock);
302         
303         block = &List->FirstEnt;
304         
305         // Look for the thread
306         do
307         {
308                 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
309                 {
310                         if( block->Threads[i] == Thread )
311                         {
312                                 block->Threads[i] = NULL;
313                                 
314                                 // Check if this block is empty
315                                 if( block != &List->FirstEnt )
316                                 {
317                                         for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
318                                                 if( block->Threads[i] )
319                                                         break;
320                                         // If empty, free it
321                                         if( i == NUM_THREADS_PER_ALLOC ) {
322                                                 prev->Next = block->Next;
323                                                 free(block);
324                                         }
325                                         //TODO: If not empty, check if it can be merged downwards
326                                 }
327                                 
328                                 Mutex_Release(&List->Lock);
329                                 return ;
330                         }
331                 }
332                 
333                 prev = block;
334                 block = block->Next;
335         } while(block);
336         
337         // Not on list, is this an error?
338 }
339

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