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 extern tThread *Proc_GetCurThread(void);
27 typedef struct sVFS_SelectListEnt tVFS_SelectListEnt;
30 struct sVFS_SelectListEnt
32 tVFS_SelectListEnt *Next;
33 tThread *Threads[NUM_THREADS_PER_ALLOC];
36 // NOTE: Typedef is in vfs.h
37 struct sVFS_SelectList
40 tVFS_SelectListEnt FirstEnt;
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);
59 int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *Name)
61 tThread *thisthread = Proc_GetCurThread();
64 ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name);
67 for( type = 0; type < 3; type ++ )
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) ) {
78 if( !*list ) *list = calloc(1, sizeof(tVFS_SelectList));
80 VFS_int_Select_AddThread(*list, thisthread, maxAllowed);
83 VFS_int_Select_RemThread(*list, thisthread);
89 // - Fast return for polling
90 if( Timeout && *Timeout == 0 ) return 0;
93 if( !Timeout || *Timeout > 0 )
95 LOG("Semaphore_Wait()");
96 // TODO: Actual timeout
97 Threads_WaitEvents( THREAD_EVENT_VFS );
102 for( type = 0; type < 3; type ++ )
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;
117 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel)
119 tThread *thisthread = Proc_GetCurThread();
122 ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel",
123 MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel);
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)
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);
137 LOG("Register ret = %i", ret);
139 // If there were events waiting, de-register and return
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);
149 // TODO: Implement timeout
150 LOG("Timeout = %p", Timeout);
152 // Wait (only if there is no timeout, or it is greater than zero
153 if( !Timeout || *Timeout > 0 )
156 // TODO: Allow extra events to be waited upon
157 Threads_WaitEvents( THREAD_EVENT_VFS|ExtraEvents );
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);
169 // Mark a node as having data ready for reading
170 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
172 ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
173 Node->DataAvaliable = !!IsDataAvaliable;
174 if( Node->DataAvaliable )
175 VFS_int_Select_SignalAll(Node->ReadThreads);
180 // Mark a node as having a full buffer
181 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
183 ENTER("pNode bIsBufferFull", Node, IsBufferFull);
184 Node->BufferFull = !!IsBufferFull;
185 if( !Node->BufferFull )
186 VFS_int_Select_SignalAll(Node->WriteThreads);
191 // Mark a node as errored
192 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
194 ENTER("pNode bIsErrorState", Node, IsErrorState);
195 Node->ErrorOccurred = !!IsErrorState;
196 if( Node->ErrorOccurred )
197 VFS_int_Select_SignalAll(Node->ErrorThreads);
203 int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
205 // Get the type of the listen
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
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
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
227 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
234 * \return Number of files with an action
236 int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
238 int i, numFlagged = 0;
239 tVFS_SelectList **list;
240 int *flag, wantedFlagValue;
243 if( !Handles ) return 0;
245 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
247 for( i = 0; i < MaxHandle; i ++ )
251 // Is the descriptor set
252 if( !FD_ISSET(i, Handles) ) continue;
255 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
256 // Is the handle valid?
257 if( !handle || !handle->Node )
259 if( Type == 2 ) { // Bad FD counts as an error
268 // Get the type of the listen
269 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
276 *list = calloc(1, sizeof(tVFS_SelectList));
280 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
282 // Oops, error (or just no space)
286 // Check for the flag
287 if( !!*flag == !!wantedFlagValue )
291 LEAVE('i', numFlagged);
296 * \return Number of files with an action
298 int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
300 int i, numFlagged = 0;
301 tVFS_SelectList **list;
302 int *flag, wantedFlagValue;
304 if( !Handles ) return 0;
306 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
308 for( i = 0; i < MaxHandle; i ++ )
312 // Is the descriptor set
313 if( !FD_ISSET(i, Handles) ) continue;
316 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
317 // Is the handle valid?
318 if( !handle || !handle->Node )
320 if( Type == 2 ) { // Bad FD counts as an error
329 // Get the type of the listen
331 // Get the type of the listen
332 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
338 VFS_int_Select_RemThread(*list, Thread );
340 // Check for the flag
341 if( !!*flag == !!wantedFlagValue ) {
349 LEAVE('i', numFlagged);
355 * \return Boolean failure
357 int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed)
360 tVFS_SelectListEnt *block, *prev;
362 ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
364 // Lock to avoid concurrency issues
365 Mutex_Acquire(&List->Lock);
367 block = &List->FirstEnt;
369 // Look for free space
372 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
374 if( block->Threads[i] == NULL )
376 block->Threads[i] = Thread;
377 Mutex_Release(&List->Lock);
382 if( MaxAllowed && count >= MaxAllowed ) {
395 block = malloc( sizeof(tVFS_SelectListEnt) );
397 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
398 Mutex_Release(&List->Lock);
402 block->Threads[0] = Thread;
403 for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
405 block->Threads[i] = NULL;
412 Mutex_Release(&List->Lock);
418 void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread)
421 tVFS_SelectListEnt *block, *prev = NULL;
423 ENTER("pList pThread", List, Thread);
425 // Lock to avoid concurrency issues
426 Mutex_Acquire(&List->Lock);
428 block = &List->FirstEnt;
430 // Look for the thread
433 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
435 if( block->Threads[i] == Thread )
437 block->Threads[i] = NULL;
439 // Check if this block is empty
440 if( block != &List->FirstEnt )
442 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
443 if( block->Threads[i] )
446 if( i == NUM_THREADS_PER_ALLOC ) {
447 LOG("Deleting block");
448 prev->Next = block->Next;
451 //TODO: If not empty, check if it can be merged downwards
454 Mutex_Release(&List->Lock);
464 // Not on list, is this an error?
466 Mutex_Release(&List->Lock);
473 * \brief Signal all threads on a list
475 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
478 tVFS_SelectListEnt *block;
482 ENTER("pList", List);
484 // Lock to avoid concurrency issues
485 Mutex_Acquire(&List->Lock);
487 block = &List->FirstEnt;
489 // Look for the thread
492 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
494 if( block->Threads[i] )
496 LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]);
497 Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS );
504 Mutex_Release(&List->Lock);