3 * - By thePowersGang (John Hodge)
6 * - Implements the select() system call (and supporting code)
8 * TODO: Implment timeouts (via an alarm event?)
9 * TODO: Remove malloc for read/write queues
16 #include <semaphore.h>
21 #define NUM_THREADS_PER_ALLOC 4
24 typedef struct sVFS_SelectListEnt tVFS_SelectListEnt;
27 struct sVFS_SelectListEnt
29 tVFS_SelectListEnt *Next;
30 tThread *Threads[NUM_THREADS_PER_ALLOC];
33 // NOTE: Typedef is in vfs.h
34 struct sVFS_SelectList
37 tVFS_SelectListEnt FirstEnt;
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);
56 int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *Name)
58 tThread *thisthread = Proc_GetCurThread();
61 ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name);
64 for( type = 0; type < 3; type ++ )
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) ) {
75 if( !*list ) *list = calloc(1, sizeof(tVFS_SelectList));
77 VFS_int_Select_AddThread(*list, thisthread, maxAllowed);
80 VFS_int_Select_RemThread(*list, thisthread);
86 // - Fast return for polling
87 if( Timeout && *Timeout == 0 ) return 0;
90 if( !Timeout || *Timeout > 0 )
92 LOG("Semaphore_Wait()");
93 // TODO: Actual timeout
94 Threads_WaitEvents( THREAD_EVENT_VFS );
99 for( type = 0; type < 3; type ++ )
101 tVFS_SelectList **list;
102 int *flag, wanted, maxAllowed;
103 if( !(TypeFlags & (1 << type)) ) continue;
104 VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed);
105 LOG("VFS_int_Select_RemThread()");
106 VFS_int_Select_RemThread(*list, thisthread);
107 ret = ret || *flag == wanted;
114 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel)
116 tThread *thisthread = Proc_GetCurThread();
119 ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel",
120 MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel);
122 // Notes: The idea is to make sure we only enter wait (Threads_WaitEvents)
123 // if we are going to be woken up (either by an event at a later time,
124 // or by an event that happened while or before we were registering).
125 // Hence, register must happen _before_ we check the state flag
126 // (See VFS_int_Select_Register), that way either we pick up the flag,
127 // or the semaphore is incremeneted (or both, but never none)
129 // Register with nodes
130 ret = VFS_int_Select_Register(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
131 ret += VFS_int_Select_Register(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
132 ret += VFS_int_Select_Register(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
134 LOG("Register ret = %i", ret);
136 // If there were events waiting, de-register and return
139 ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
140 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
141 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
146 // TODO: Implement timeout
147 LOG("Timeout = %p", Timeout);
149 // Wait (only if there is no timeout, or it is greater than zero
150 if( !Timeout || *Timeout > 0 )
153 // TODO: Allow extra events to be waited upon
154 Threads_WaitEvents( THREAD_EVENT_VFS|ExtraEvents );
157 // Fill output (modify *Handles)
158 // - Also, de-register
159 ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
160 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
161 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
166 // Mark a node as having data ready for reading
167 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
169 ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
170 Node->DataAvaliable = !!IsDataAvaliable;
171 if( Node->DataAvaliable )
172 VFS_int_Select_SignalAll(Node->ReadThreads);
177 // Mark a node as having a full buffer
178 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
180 ENTER("pNode bIsBufferFull", Node, IsBufferFull);
181 Node->BufferFull = !!IsBufferFull;
182 if( !Node->BufferFull )
183 VFS_int_Select_SignalAll(Node->WriteThreads);
188 // Mark a node as errored
189 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
191 ENTER("pNode bIsErrorState", Node, IsErrorState);
192 Node->ErrorOccurred = !!IsErrorState;
193 if( Node->ErrorOccurred )
194 VFS_int_Select_SignalAll(Node->ErrorThreads);
200 int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
202 // Get the type of the listen
206 if(List) *List = &Node->ReadThreads;
207 if(Flag) *Flag = &Node->DataAvaliable;
208 if(WantedFlag) *WantedFlag = 1;
209 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read
212 if(List) *List = &Node->WriteThreads;
213 if(Flag) *Flag = &Node->BufferFull;
214 if(WantedFlag) *WantedFlag = 0;
215 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write
218 if(List) *List = &Node->ErrorThreads;
219 if(Flag) *Flag = &Node->ErrorOccurred;
220 if(WantedFlag) *WantedFlag = 1;
221 if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners
224 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
231 * \return Number of files with an action
233 int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
235 int i, numFlagged = 0;
236 tVFS_SelectList **list;
237 int *flag, wantedFlagValue;
240 if( !Handles ) return 0;
242 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
244 for( i = 0; i < MaxHandle; i ++ )
248 // Is the descriptor set
249 if( !FD_ISSET(i, Handles) ) continue;
252 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
253 // Is the handle valid?
254 if( !handle || !handle->Node )
256 if( Type == 2 ) { // Bad FD counts as an error
265 // Get the type of the listen
266 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
273 *list = calloc(1, sizeof(tVFS_SelectList));
277 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
279 // Oops, error (or just no space)
283 // Check for the flag
284 if( !!*flag == !!wantedFlagValue )
288 LEAVE('i', numFlagged);
293 * \return Number of files with an action
295 int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
297 int i, numFlagged = 0;
298 tVFS_SelectList **list;
299 int *flag, wantedFlagValue;
301 if( !Handles ) return 0;
303 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
305 for( i = 0; i < MaxHandle; i ++ )
309 // Is the descriptor set
310 if( !FD_ISSET(i, Handles) ) continue;
313 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
314 // Is the handle valid?
315 if( !handle || !handle->Node )
317 if( Type == 2 ) { // Bad FD counts as an error
326 // Get the type of the listen
328 // Get the type of the listen
329 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
335 VFS_int_Select_RemThread(*list, Thread );
337 // Check for the flag
338 if( !!*flag == !!wantedFlagValue ) {
346 LEAVE('i', numFlagged);
352 * \return Boolean failure
354 int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed)
357 tVFS_SelectListEnt *block, *prev;
359 ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
361 // Lock to avoid concurrency issues
362 Mutex_Acquire(&List->Lock);
364 block = &List->FirstEnt;
366 // Look for free space
369 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
371 if( block->Threads[i] == NULL )
373 block->Threads[i] = Thread;
374 Mutex_Release(&List->Lock);
379 if( MaxAllowed && count >= MaxAllowed ) {
392 block = malloc( sizeof(tVFS_SelectListEnt) );
394 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
395 Mutex_Release(&List->Lock);
399 block->Threads[0] = Thread;
400 for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
402 block->Threads[i] = NULL;
409 Mutex_Release(&List->Lock);
415 void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread)
418 tVFS_SelectListEnt *block, *prev = NULL;
420 ENTER("pList pThread", List, Thread);
422 // Lock to avoid concurrency issues
423 Mutex_Acquire(&List->Lock);
425 block = &List->FirstEnt;
427 // Look for the thread
430 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
432 if( block->Threads[i] == Thread )
434 block->Threads[i] = NULL;
436 // Check if this block is empty
437 if( block != &List->FirstEnt )
439 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
440 if( block->Threads[i] )
443 if( i == NUM_THREADS_PER_ALLOC ) {
444 LOG("Deleting block");
445 prev->Next = block->Next;
448 //TODO: If not empty, check if it can be merged downwards
451 Mutex_Release(&List->Lock);
461 // Not on list, is this an error?
463 Mutex_Release(&List->Lock);
470 * \brief Signal all threads on a list
472 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
475 tVFS_SelectListEnt *block;
479 ENTER("pList", List);
481 // Lock to avoid concurrency issues
482 Mutex_Acquire(&List->Lock);
484 block = &List->FirstEnt;
486 // Look for the thread
489 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
491 if( block->Threads[i] )
493 LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]);
494 Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS );
501 Mutex_Release(&List->Lock);