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>
22 #define NUM_THREADS_PER_ALLOC 4
25 typedef struct sVFS_SelectListEnt tVFS_SelectListEnt;
28 struct sVFS_SelectListEnt
30 tVFS_SelectListEnt *Next;
31 tThread *Threads[NUM_THREADS_PER_ALLOC];
34 // NOTE: Typedef is in vfs.h
35 struct sVFS_SelectList
38 tVFS_SelectListEnt FirstEnt;
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, Uint32 ExtraEvents, 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(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed);
48 int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
49 int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
50 int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed);
51 void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread);
52 void VFS_int_Select_SignalAll(tVFS_SelectList *List);
57 int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *Name)
59 tThread *thisthread = Proc_GetCurThread();
62 ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name);
65 for( 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);
81 VFS_int_Select_RemThread(*list, thisthread);
90 LOG("Semaphore_Wait()");
91 // TODO: Actual timeout
92 Threads_WaitEvents( THREAD_EVENT_VFS );
94 else if( *Timeout > 0 )
96 tTimer *t = Time_AllocateTimer(NULL, NULL);
98 Threads_ClearEvent( THREAD_EVENT_TIMER );
99 // TODO: Convert *Timeout?
100 LOG("Timeout %lli ms", *Timeout);
101 Time_ScheduleTimer( t, *Timeout );
102 // Wait for the timer or a VFS event
103 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_TIMER );
109 for( type = 0; type < 3; type ++ )
111 tVFS_SelectList **list;
112 int *flag, wanted, maxAllowed;
113 if( !(TypeFlags & (1 << type)) ) continue;
114 VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed);
115 LOG("VFS_int_Select_RemThread()");
116 VFS_int_Select_RemThread(*list, thisthread);
117 ret = ret || *flag == wanted;
120 Threads_ClearEvent( THREAD_EVENT_VFS );
121 Threads_ClearEvent( THREAD_EVENT_TIMER );
127 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel)
129 tThread *thisthread = Proc_GetCurThread();
132 ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel",
133 MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel);
135 // Notes: The idea is to make sure we only enter wait (Threads_WaitEvents)
136 // if we are going to be woken up (either by an event at a later time,
137 // or by an event that happened while or before we were registering).
138 // Hence, register must happen _before_ we check the state flag
139 // (See VFS_int_Select_Register), that way either we pick up the flag,
140 // or the semaphore is incremeneted (or both, but never none)
142 // Register with nodes
143 ret = VFS_int_Select_Register(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
144 ret += VFS_int_Select_Register(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
145 ret += VFS_int_Select_Register(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
147 LOG("Register ret = %i", ret);
149 // If there were events waiting, de-register and return
152 ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
153 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
154 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
162 LOG("Semaphore_Wait()");
163 // TODO: Actual timeout
164 Threads_WaitEvents( THREAD_EVENT_VFS|ExtraEvents );
166 else if( *Timeout > 0 )
168 tTimer *t = Time_AllocateTimer(NULL, NULL);
170 Threads_ClearEvent( THREAD_EVENT_TIMER );
171 // TODO: Convert *Timeout?
172 LOG("Timeout %lli ms", *Timeout);
173 Time_ScheduleTimer( t, *Timeout );
174 // Wait for the timer or a VFS event
175 Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_TIMER|ExtraEvents );
178 // Fill output (modify *Handles)
179 // - Also, de-register
180 ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
181 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
182 ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
184 Threads_ClearEvent( THREAD_EVENT_VFS );
185 Threads_ClearEvent( THREAD_EVENT_TIMER );
191 // Mark a node as having data ready for reading
192 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
194 ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
195 Node->DataAvaliable = !!IsDataAvaliable;
196 if( Node->DataAvaliable )
197 VFS_int_Select_SignalAll(Node->ReadThreads);
202 // Mark a node as having a full buffer
203 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
205 ENTER("pNode bIsBufferFull", Node, IsBufferFull);
206 Node->BufferFull = !!IsBufferFull;
207 if( !Node->BufferFull )
208 VFS_int_Select_SignalAll(Node->WriteThreads);
213 // Mark a node as errored
214 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
216 ENTER("pNode bIsErrorState", Node, IsErrorState);
217 Node->ErrorOccurred = !!IsErrorState;
218 if( Node->ErrorOccurred )
219 VFS_int_Select_SignalAll(Node->ErrorThreads);
225 int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
227 // Get the type of the listen
231 if(List) *List = &Node->ReadThreads;
232 if(Flag) *Flag = &Node->DataAvaliable;
233 if(WantedFlag) *WantedFlag = 1;
234 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read
237 if(List) *List = &Node->WriteThreads;
238 if(Flag) *Flag = &Node->BufferFull;
239 if(WantedFlag) *WantedFlag = 0;
240 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write
243 if(List) *List = &Node->ErrorThreads;
244 if(Flag) *Flag = &Node->ErrorOccurred;
245 if(WantedFlag) *WantedFlag = 1;
246 if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners
249 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
256 * \return Number of files with an action
258 int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
260 int i, numFlagged = 0;
261 tVFS_SelectList **list;
262 int *flag, wantedFlagValue;
265 if( !Handles ) return 0;
267 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
269 for( i = 0; i < MaxHandle; i ++ )
273 // Is the descriptor set
274 if( !FD_ISSET(i, Handles) ) continue;
277 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
278 // Is the handle valid?
279 if( !handle || !handle->Node )
281 if( Type == 2 ) { // Bad FD counts as an error
290 // Get the type of the listen
291 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
298 *list = calloc(1, sizeof(tVFS_SelectList));
302 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
304 // Oops, error (or just no space)
308 // Check for the flag
309 if( !!*flag == !!wantedFlagValue )
313 LEAVE('i', numFlagged);
318 * \return Number of files with an action
320 int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
322 int i, numFlagged = 0;
323 tVFS_SelectList **list;
324 int *flag, wantedFlagValue;
326 if( !Handles ) return 0;
328 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
330 for( i = 0; i < MaxHandle; i ++ )
334 // Is the descriptor set
335 if( !FD_ISSET(i, Handles) ) continue;
338 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
339 // Is the handle valid?
340 if( !handle || !handle->Node )
342 if( Type == 2 ) { // Bad FD counts as an error
351 // Get the type of the listen
353 // Get the type of the listen
354 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
360 VFS_int_Select_RemThread(*list, Thread );
362 // Check for the flag
363 if( !!*flag == !!wantedFlagValue ) {
371 LEAVE('i', numFlagged);
377 * \return Boolean failure
379 int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed)
382 tVFS_SelectListEnt *block, *prev;
384 ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
386 // Lock to avoid concurrency issues
387 Mutex_Acquire(&List->Lock);
389 block = &List->FirstEnt;
391 // Look for free space
394 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
396 if( block->Threads[i] == NULL )
398 block->Threads[i] = Thread;
399 Mutex_Release(&List->Lock);
404 if( MaxAllowed && count >= MaxAllowed ) {
417 block = malloc( sizeof(tVFS_SelectListEnt) );
419 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
420 Mutex_Release(&List->Lock);
424 block->Threads[0] = Thread;
425 for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
427 block->Threads[i] = NULL;
434 Mutex_Release(&List->Lock);
440 void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread)
443 tVFS_SelectListEnt *block, *prev = NULL;
445 ENTER("pList pThread", List, Thread);
447 // Lock to avoid concurrency issues
448 Mutex_Acquire(&List->Lock);
450 block = &List->FirstEnt;
452 // Look for the thread
455 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
457 if( block->Threads[i] == Thread )
459 block->Threads[i] = NULL;
461 // Check if this block is empty
462 if( block != &List->FirstEnt )
464 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
465 if( block->Threads[i] )
468 if( i == NUM_THREADS_PER_ALLOC ) {
469 LOG("Deleting block");
470 prev->Next = block->Next;
473 //TODO: If not empty, check if it can be merged downwards
476 Mutex_Release(&List->Lock);
486 // Not on list, is this an error?
488 Mutex_Release(&List->Lock);
495 * \brief Signal all threads on a list
497 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
500 tVFS_SelectListEnt *block;
504 ENTER("pList", List);
506 // Lock to avoid concurrency issues
507 Mutex_Acquire(&List->Lock);
509 block = &List->FirstEnt;
511 // Look for the thread
514 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
516 if( block->Threads[i] )
518 LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]);
519 Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS );
526 Mutex_Release(&List->Lock);