3 * - By thePowersGang (John Hodge)
6 * - Implements the select() system call (and supporting code)
13 #include <semaphore.h>
16 #define NUM_THREADS_PER_ALLOC 4
19 typedef struct sVFS_SelectThread tVFS_SelectThread;
20 typedef struct sVFS_SelectListEnt tVFS_SelectListEnt;
23 struct sVFS_SelectListEnt
25 tVFS_SelectListEnt *Next;
26 tVFS_SelectThread *Threads[NUM_THREADS_PER_ALLOC];
29 struct sVFS_SelectList
32 tVFS_SelectListEnt FirstEnt;
35 struct sVFS_SelectThread
37 //! \brief Semaphore to atomically put the listener to sleep
38 tSemaphore SleepHandle; // TODO: Allow timeouts (by setting an alarm?)
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, 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(enum eVFS_SelectTypes Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed);
48 int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel);
49 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes 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 void VFS_int_Select_SignalAll(tVFS_SelectList *List);
57 int VFS_SelectNode(tVFS_Node *Node, enum eVFS_SelectTypes Type, tTime *Timeout)
59 tVFS_SelectThread *thread_info;
60 tVFS_SelectList **list;
61 int *flag, wanted, maxAllowed;
63 ENTER("pNode iType pTimeout", Node, Type, Timeout);
65 if( VFS_int_Select_GetType(Type, Node, &list, &flag, &wanted, &maxAllowed) ) {
70 thread_info = malloc(sizeof(tVFS_SelectThread));
71 if(!thread_info) return -1;
73 Semaphore_Init(&thread_info->SleepHandle, 0, 0, "VFS_SelectNode()", "");
75 LOG("list=%p, flag=%p, wanted=%i, maxAllowed=%i", list, flag, wanted, maxAllowed);
79 *list = calloc(1, sizeof(tVFS_SelectList));
82 VFS_int_Select_AddThread(*list, thread_info, maxAllowed);
85 VFS_int_Select_RemThread(*list, thread_info);
91 if( !Timeout || *Timeout > 0 )
93 LOG("Semaphore_Wait()");
94 // TODO: Actual timeout
95 Semaphore_Wait(&thread_info->SleepHandle, 1);
98 LOG("VFS_int_Select_RemThread()");
99 VFS_int_Select_RemThread(*list, thread_info);
103 LEAVE('i', *flag == wanted);
104 return *flag == wanted;
107 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, BOOL IsKernel)
109 tVFS_SelectThread *thread_info;
112 thread_info = malloc(sizeof(tVFS_SelectThread));
113 if(!thread_info) return -1;
115 Semaphore_Init(&thread_info->SleepHandle, 0, -1, "VFS_Select()", "");
117 // Notes: The idea is to make sure we onlt enter wait (on the semaphore)
118 // if we are going to be woken up (either by an event at a later time,
119 // or by an event that happened while or before we were registering).
120 // Hence, register must happen _before_ we check the state flag
121 // (See VFS_int_Select_Register), that way either we pick up the flag,
122 // or the semaphore is incremeneted (or both, but never none)
124 // Register with nodes
125 ret = VFS_int_Select_Register(thread_info, MaxHandle, ReadHandles, 0, IsKernel);
126 ret += VFS_int_Select_Register(thread_info, MaxHandle, WriteHandles, 1, IsKernel);
127 ret += VFS_int_Select_Register(thread_info, MaxHandle, ErrHandles, 2, IsKernel);
129 // If there were events waiting, de-register and return
132 ret = VFS_int_Select_Deregister(thread_info, MaxHandle, ReadHandles, 0, IsKernel);
133 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, WriteHandles, 1, IsKernel);
134 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, ErrHandles, 2, IsKernel);
139 // TODO: Implement timeout
141 // Wait (only if there is no timeout, or it is greater than zero
142 if( !Timeout || *Timeout > 0 )
144 ret = Semaphore_Wait(&thread_info->SleepHandle, 1);
147 // Fill output (modify *Handles)
148 // - Also, de-register
149 ret = VFS_int_Select_Deregister(thread_info, MaxHandle, ReadHandles, 0, IsKernel);
150 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, WriteHandles, 1, IsKernel);
151 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, ErrHandles, 2, IsKernel);
156 // Mark a node as having data ready for reading
157 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
159 ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
160 Node->DataAvaliable = !!IsDataAvaliable;
161 if( Node->DataAvaliable )
162 VFS_int_Select_SignalAll(Node->ReadThreads);
167 // Mark a node as having a full buffer
168 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
170 ENTER("pNode bIsDataAvaliable", Node, IsBufferFull);
171 Node->BufferFull = !!IsBufferFull;
172 if( !Node->BufferFull )
173 VFS_int_Select_SignalAll(Node->WriteThreads);
178 // Mark a node as errored
179 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
181 ENTER("pNode bIsDataAvaliable", Node, IsErrorState);
182 Node->ErrorOccurred = !!IsErrorState;
183 if( Node->ErrorOccurred )
184 VFS_int_Select_SignalAll(Node->ErrorThreads);
190 int VFS_int_Select_GetType(enum eVFS_SelectTypes Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
192 // Get the type of the listen
196 if(List) *List = &Node->ReadThreads;
197 if(Flag) *Flag = &Node->DataAvaliable;
198 if(WantedFlag) *WantedFlag = 1;
199 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read
202 if(List) *List = &Node->WriteThreads;
203 if(Flag) *Flag = &Node->BufferFull;
204 if(WantedFlag) *WantedFlag = 0;
205 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write
208 if(List) *List = &Node->ErrorThreads;
209 if(Flag) *Flag = &Node->ErrorOccurred;
210 if(WantedFlag) *WantedFlag = 1;
211 if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners
214 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
221 * \return Number of files with an action
223 int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel)
225 int i, numFlagged = 0;
226 tVFS_SelectList **list;
227 int *flag, wantedFlagValue;
230 if( !Handles ) return 0;
232 ENTER("pThread iMaxHandle pHandles iType BIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
234 for( i = 0; i < MaxHandle; i ++ )
238 // Is the descriptor set
239 if( !FD_ISSET(i, Handles) ) continue;
241 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
242 // Is the handle valid?
243 if( !handle || !handle->Node )
245 if( Type == 2 ) { // Bad FD counts as an error
254 // Get the type of the listen
255 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
262 *list = calloc(1, sizeof(tVFS_SelectList));
266 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
268 // Oops, error (or just no space)
272 // Check for the flag
273 if( !!*flag == !!wantedFlagValue )
277 LEAVE('i', numFlagged);
282 * \return Number of files with an action
284 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel)
286 int i, numFlagged = 0;
287 tVFS_SelectList **list;
288 int *flag, wantedFlagValue;
290 if( !Handles ) return 0;
292 ENTER("pThread iMaxHandle pHandles iType BIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
294 for( i = 0; i < MaxHandle; i ++ )
298 // Is the descriptor set
299 if( !FD_ISSET(i, Handles) ) continue;
301 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
302 // Is the handle valid?
303 if( !handle || !handle->Node )
305 if( Type == 2 ) { // Bad FD counts as an error
314 // Get the type of the listen
316 // Get the type of the listen
317 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
323 VFS_int_Select_RemThread(*list, Thread );
325 // Check for the flag
326 if( !!*flag == !!wantedFlagValue ) {
334 LEAVE('i', numFlagged);
340 * \return Boolean failure
342 int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
345 tVFS_SelectListEnt *block, *prev;
347 ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
349 // Lock to avoid concurrency issues
350 Mutex_Acquire(&List->Lock);
352 block = &List->FirstEnt;
354 // Look for free space
357 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
359 if( block->Threads[i] == NULL )
361 block->Threads[i] = Thread;
362 Mutex_Release(&List->Lock);
367 if( MaxAllowed && count >= MaxAllowed ) {
380 block = malloc( sizeof(tVFS_SelectListEnt) );
382 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
383 Mutex_Release(&List->Lock);
387 block->Threads[0] = Thread;
388 for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
390 block->Threads[i] = NULL;
397 Mutex_Release(&List->Lock);
403 void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
406 tVFS_SelectListEnt *block, *prev;
408 ENTER("pList pThread", List, Thread);
410 // Lock to avoid concurrency issues
411 Mutex_Acquire(&List->Lock);
413 block = &List->FirstEnt;
415 // Look for the thread
418 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
420 if( block->Threads[i] == Thread )
422 block->Threads[i] = NULL;
424 // Check if this block is empty
425 if( block != &List->FirstEnt )
427 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
428 if( block->Threads[i] )
431 if( i == NUM_THREADS_PER_ALLOC ) {
432 LOG("Deleting block");
433 prev->Next = block->Next;
436 //TODO: If not empty, check if it can be merged downwards
439 Mutex_Release(&List->Lock);
449 // Not on list, is this an error?
451 Mutex_Release(&List->Lock);
458 * \brief Signal all threads on a list
460 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
463 tVFS_SelectListEnt *block;
465 ENTER("pList", List);
467 // Lock to avoid concurrency issues
468 Mutex_Acquire(&List->Lock);
470 block = &List->FirstEnt;
472 // Look for the thread
475 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
477 LOG("block->Threads[i] = %p", block->Threads[i]);
478 if( block->Threads[i] )
480 Semaphore_Signal( &block->Threads[i]->SleepHandle, 1 );
487 Mutex_Release(&List->Lock);