3 * - By thePowersGang (John Hodge)
6 * - Implements the select() system call (and supporting code)
8 * TODO: Remove malloc for read/write queues
15 #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 * const thisthread = Proc_GetCurThread();
61 ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name);
65 for( int type = 0; type < 3; type ++ )
67 tVFS_SelectList **list;
68 int *flag, wanted, maxAllowed;
69 if( !(TypeFlags & (1 << type)) ) continue;
70 if( VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed) ) {
76 if( !*list ) *list = calloc(1, sizeof(tVFS_SelectList));
78 VFS_int_Select_AddThread(*list, thisthread, maxAllowed);
88 // Skip wait, conditions already met
89 LOG("ret = %i, skipping wait", ret);
93 LOG("Semaphore_Wait()");
94 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_SIGNAL );
96 else if( *Timeout > 0 )
98 tTimer *t = Time_AllocateTimer(NULL, NULL);
100 Threads_ClearEvent( THREAD_EVENT_TIMER );
101 LOG("Timeout %lli ms", *Timeout);
102 Time_ScheduleTimer( t, *Timeout );
103 // Wait for the timer or a VFS event
104 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_TIMER|THREAD_EVENT_SIGNAL );
110 for( int type = 0; type < 3; type ++ )
112 tVFS_SelectList **list;
113 int *flag, wanted, maxAllowed;
114 if( !(TypeFlags & (1 << type)) ) continue;
115 VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed);
116 LOG("VFS_int_Select_RemThread() for %i", type);
118 VFS_int_Select_RemThread(*list, thisthread);
119 ret = ret || *flag == wanted;
122 Threads_ClearEvent( THREAD_EVENT_VFS );
123 Threads_ClearEvent( THREAD_EVENT_TIMER );
129 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel)
131 tThread *thisthread = Proc_GetCurThread();
134 ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout xExtraEvents bIsKernel",
135 MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, ExtraEvents, IsKernel);
137 // Notes: The idea is to make sure we only enter wait (Threads_WaitEvents)
138 // if we are going to be woken up (either by an event at a later time,
139 // or by an event that happened while or before we were registering).
140 // Hence, register must happen _before_ we check the state flag
141 // (See VFS_int_Select_Register), that way either we pick up the flag,
142 // or the semaphore is incremeneted (or both, but never none)
144 // Register with nodes
146 LOG(" - ReadHandles[0] = %x", ReadHandles->flags[0]);
147 ret = VFS_int_Select_Register(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
148 ret += VFS_int_Select_Register(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
149 ret += VFS_int_Select_Register(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
151 LOG("Register ret = %i", ret);
153 // If there were events waiting, de-register and return
156 LOG(" - ReadHandles[0] = %x", ReadHandles->flags[0]);
157 ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
158 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
159 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
160 LOG(" - ReadHandles[0] = %x", ReadHandles->flags[0]);
168 LOG("Waiting for VFS/SIGNAL events (Plus 0x%x)", ExtraEvents);
169 // TODO: Actual timeout
170 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_SIGNAL|ExtraEvents );
172 else if( *Timeout > 0 )
174 tTimer *t = Time_AllocateTimer(NULL, NULL);
176 Threads_ClearEvent( THREAD_EVENT_TIMER );
177 // TODO: Convert *Timeout?
178 LOG("Timeout %lli ms", *Timeout);
179 Time_ScheduleTimer( t, *Timeout );
180 // Wait for the timer or a VFS event
181 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_TIMER|ExtraEvents );
184 // Fill output (modify *Handles)
185 LOG("De-registering");
186 // - Also, de-register
187 ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
188 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
189 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
191 Threads_ClearEvent( THREAD_EVENT_VFS );
192 Threads_ClearEvent( THREAD_EVENT_TIMER );
198 // Mark a node as having data ready for reading
199 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
202 ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
203 Node->DataAvaliable = !!IsDataAvaliable;
204 if( Node->DataAvaliable )
205 VFS_int_Select_SignalAll(Node->ReadThreads);
210 // Mark a node as having a full buffer
211 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
214 ENTER("pNode bIsBufferFull", Node, IsBufferFull);
215 Node->BufferFull = !!IsBufferFull;
216 if( !Node->BufferFull )
217 VFS_int_Select_SignalAll(Node->WriteThreads);
222 // Mark a node as errored
223 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
226 ENTER("pNode bIsErrorState", Node, IsErrorState);
227 Node->ErrorOccurred = !!IsErrorState;
228 if( Node->ErrorOccurred )
229 VFS_int_Select_SignalAll(Node->ErrorThreads);
235 int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
237 // Get the type of the listen
241 if(List) *List = &Node->ReadThreads;
242 if(Flag) *Flag = &Node->DataAvaliable;
243 if(WantedFlag) *WantedFlag = 1;
244 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read
247 if(List) *List = &Node->WriteThreads;
248 if(Flag) *Flag = &Node->BufferFull;
249 if(WantedFlag) *WantedFlag = 0;
250 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write
253 if(List) *List = &Node->ErrorThreads;
254 if(Flag) *Flag = &Node->ErrorOccurred;
255 if(WantedFlag) *WantedFlag = 1;
256 if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners
259 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
266 * \return Number of files with an action
268 int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
270 int i, numFlagged = 0;
271 tVFS_SelectList **list;
272 int *flag, wantedFlagValue;
275 if( !Handles ) return 0;
277 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
279 for( i = 0; i < MaxHandle; i ++ )
283 // Is the descriptor set
284 if( !FD_ISSET(i, Handles) ) continue;
287 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
288 // Is the handle valid?
289 if( !handle || !handle->Node )
291 if( Type == 2 ) { // Bad FD counts as an error
300 // Get the type of the listen
301 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
308 *list = calloc(1, sizeof(tVFS_SelectList));
312 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
314 // Oops, error (or just no space)
318 // Check for the flag
319 if( !!*flag == !!wantedFlagValue ) {
320 LOG(" %i == want %i", !!*flag, !!wantedFlagValue);
325 LEAVE('i', numFlagged);
330 * \return Number of files with an action
332 int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
334 int i, numFlagged = 0;
335 tVFS_SelectList **list;
336 int *flag, wantedFlagValue;
338 if( !Handles ) return 0;
340 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
342 for( i = 0; i < MaxHandle; i ++ )
346 // Is the descriptor set
347 if( !FD_ISSET(i, Handles) ) continue;
350 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
351 // Is the handle valid?
352 if( !handle || !handle->Node )
354 if( Type == 2 ) { // Bad FD counts as an error
363 // Get the type of the listen
364 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
370 VFS_int_Select_RemThread(*list, Thread );
372 // Check for the flag
373 if( !!*flag == !!wantedFlagValue ) {
375 LOG(" %i == want %i", !!*flag, !!wantedFlagValue);
383 LEAVE('i', numFlagged);
389 * \return Boolean failure
391 int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed)
394 tVFS_SelectListEnt *block, *prev;
396 ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
398 // Lock to avoid concurrency issues
399 Mutex_Acquire(&List->Lock);
401 block = &List->FirstEnt;
403 // Look for free space
406 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
408 if( block->Threads[i] == NULL )
410 block->Threads[i] = Thread;
411 Mutex_Release(&List->Lock);
416 if( MaxAllowed && count >= MaxAllowed ) {
417 Mutex_Release(&List->Lock);
430 block = malloc( sizeof(tVFS_SelectListEnt) );
432 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
433 Mutex_Release(&List->Lock);
437 block->Threads[0] = Thread;
438 for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
440 block->Threads[i] = NULL;
447 Mutex_Release(&List->Lock);
453 void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread)
455 tVFS_SelectListEnt *block, *prev = NULL;
457 ENTER("pList pThread", List, Thread);
459 // Lock to avoid concurrency issues
460 Mutex_Acquire(&List->Lock);
462 block = &List->FirstEnt;
464 // Look for the thread
467 for( int i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
469 if( block->Threads[i] == Thread )
471 block->Threads[i] = NULL;
473 // Check if this block is empty
474 if( block != &List->FirstEnt )
476 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
477 if( block->Threads[i] )
480 if( i == NUM_THREADS_PER_ALLOC ) {
481 LOG("Deleting block");
482 prev->Next = block->Next;
485 //TODO: If not empty, check if it can be merged downwards
488 Mutex_Release(&List->Lock);
498 // Not on list, is this an error?
500 Mutex_Release(&List->Lock);
507 * \brief Signal all threads on a list
509 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
512 tVFS_SelectListEnt *block;
516 ENTER("pList", List);
518 // Lock to avoid concurrency issues
519 Mutex_Acquire(&List->Lock);
521 block = &List->FirstEnt;
523 // Look for the thread
526 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
528 if( block->Threads[i] )
530 LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]);
531 Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS );
538 Mutex_Release(&List->Lock);