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(int 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, int Type, BOOL IsKernel);
49 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int 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, int TypeFlags, tTime *Timeout, const char *Name)
59 tVFS_SelectThread *thread_info;
62 ENTER("pNode iTypeFlags pTimeout", Node, TypeFlags, Timeout);
64 thread_info = malloc(sizeof(tVFS_SelectThread));
65 if(!thread_info) LEAVE_RET('i', -1);
67 Semaphore_Init(&thread_info->SleepHandle, 0, 0, "VFS_SelectNode()", Name);
70 for( type = 0; type < 3; type ++ )
72 tVFS_SelectList **list;
73 int *flag, wanted, maxAllowed;
74 if( !(TypeFlags & (1 << type)) ) continue;
75 if( VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed) ) {
82 if( !*list ) *list = calloc(1, sizeof(tVFS_SelectList));
84 VFS_int_Select_AddThread(*list, thread_info, maxAllowed);
87 VFS_int_Select_RemThread(*list, thread_info);
94 // - Fast return for polling
95 if( Timeout && *Timeout == 0 ) return 0;
98 if( !Timeout || *Timeout > 0 )
100 LOG("Semaphore_Wait()");
101 // TODO: Actual timeout
102 Semaphore_Wait(&thread_info->SleepHandle, 1);
107 for( type = 0; type < 3; type ++ )
109 tVFS_SelectList **list;
110 int *flag, wanted, maxAllowed;
111 if( !(TypeFlags & (1 << type)) ) continue;
112 VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed);
113 LOG("VFS_int_Select_RemThread()");
114 VFS_int_Select_RemThread(*list, thread_info);
115 ret = ret || *flag == wanted;
124 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, BOOL IsKernel)
126 tVFS_SelectThread *thread_info;
129 thread_info = malloc(sizeof(tVFS_SelectThread));
130 if(!thread_info) return -1;
132 ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel",
133 MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel);
135 Semaphore_Init(&thread_info->SleepHandle, 0, 0, "VFS_Select()", "");
137 // Notes: The idea is to make sure we only enter wait (on the semaphore)
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
145 ret = VFS_int_Select_Register(thread_info, MaxHandle, ReadHandles, 0, IsKernel);
146 ret += VFS_int_Select_Register(thread_info, MaxHandle, WriteHandles, 1, IsKernel);
147 ret += VFS_int_Select_Register(thread_info, MaxHandle, ErrHandles, 2, IsKernel);
149 LOG("Register ret = %i", ret);
151 // If there were events waiting, de-register and return
154 ret = VFS_int_Select_Deregister(thread_info, MaxHandle, ReadHandles, 0, IsKernel);
155 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, WriteHandles, 1, IsKernel);
156 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, ErrHandles, 2, IsKernel);
162 // TODO: Implement timeout
163 LOG("Timeout = %p", Timeout);
165 // Wait (only if there is no timeout, or it is greater than zero
166 if( !Timeout || *Timeout > 0 )
168 ret = Semaphore_Wait(&thread_info->SleepHandle, 1);
169 // TODO: Do something with ret?
172 // Fill output (modify *Handles)
173 // - Also, de-register
174 ret = VFS_int_Select_Deregister(thread_info, MaxHandle, ReadHandles, 0, IsKernel);
175 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, WriteHandles, 1, IsKernel);
176 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, ErrHandles, 2, IsKernel);
182 // Mark a node as having data ready for reading
183 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
185 ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
186 Node->DataAvaliable = !!IsDataAvaliable;
187 if( Node->DataAvaliable )
188 VFS_int_Select_SignalAll(Node->ReadThreads);
193 // Mark a node as having a full buffer
194 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
196 ENTER("pNode bIsDataAvaliable", Node, IsBufferFull);
197 Node->BufferFull = !!IsBufferFull;
198 if( !Node->BufferFull )
199 VFS_int_Select_SignalAll(Node->WriteThreads);
204 // Mark a node as errored
205 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
207 ENTER("pNode bIsDataAvaliable", Node, IsErrorState);
208 Node->ErrorOccurred = !!IsErrorState;
209 if( Node->ErrorOccurred )
210 VFS_int_Select_SignalAll(Node->ErrorThreads);
216 int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
218 // Get the type of the listen
222 if(List) *List = &Node->ReadThreads;
223 if(Flag) *Flag = &Node->DataAvaliable;
224 if(WantedFlag) *WantedFlag = 1;
225 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read
228 if(List) *List = &Node->WriteThreads;
229 if(Flag) *Flag = &Node->BufferFull;
230 if(WantedFlag) *WantedFlag = 0;
231 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write
234 if(List) *List = &Node->ErrorThreads;
235 if(Flag) *Flag = &Node->ErrorOccurred;
236 if(WantedFlag) *WantedFlag = 1;
237 if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners
240 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
247 * \return Number of files with an action
249 int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
251 int i, numFlagged = 0;
252 tVFS_SelectList **list;
253 int *flag, wantedFlagValue;
256 if( !Handles ) return 0;
258 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
260 for( i = 0; i < MaxHandle; i ++ )
264 // Is the descriptor set
265 if( !FD_ISSET(i, Handles) ) continue;
268 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
269 // Is the handle valid?
270 if( !handle || !handle->Node )
272 if( Type == 2 ) { // Bad FD counts as an error
281 // Get the type of the listen
282 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
289 *list = calloc(1, sizeof(tVFS_SelectList));
293 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
295 // Oops, error (or just no space)
299 // Check for the flag
300 if( !!*flag == !!wantedFlagValue )
304 LEAVE('i', numFlagged);
309 * \return Number of files with an action
311 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
313 int i, numFlagged = 0;
314 tVFS_SelectList **list;
315 int *flag, wantedFlagValue;
317 if( !Handles ) return 0;
319 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
321 for( i = 0; i < MaxHandle; i ++ )
325 // Is the descriptor set
326 if( !FD_ISSET(i, Handles) ) continue;
329 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
330 // Is the handle valid?
331 if( !handle || !handle->Node )
333 if( Type == 2 ) { // Bad FD counts as an error
342 // Get the type of the listen
344 // Get the type of the listen
345 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
351 VFS_int_Select_RemThread(*list, Thread );
353 // Check for the flag
354 if( !!*flag == !!wantedFlagValue ) {
362 LEAVE('i', numFlagged);
368 * \return Boolean failure
370 int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
373 tVFS_SelectListEnt *block, *prev;
375 ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
377 // Lock to avoid concurrency issues
378 Mutex_Acquire(&List->Lock);
380 block = &List->FirstEnt;
382 // Look for free space
385 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
387 if( block->Threads[i] == NULL )
389 block->Threads[i] = Thread;
390 Mutex_Release(&List->Lock);
395 if( MaxAllowed && count >= MaxAllowed ) {
408 block = malloc( sizeof(tVFS_SelectListEnt) );
410 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
411 Mutex_Release(&List->Lock);
415 block->Threads[0] = Thread;
416 for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
418 block->Threads[i] = NULL;
425 Mutex_Release(&List->Lock);
431 void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
434 tVFS_SelectListEnt *block, *prev = NULL;
436 ENTER("pList pThread", List, Thread);
438 // Lock to avoid concurrency issues
439 Mutex_Acquire(&List->Lock);
441 block = &List->FirstEnt;
443 // Look for the thread
446 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
448 if( block->Threads[i] == Thread )
450 block->Threads[i] = NULL;
452 // Check if this block is empty
453 if( block != &List->FirstEnt )
455 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
456 if( block->Threads[i] )
459 if( i == NUM_THREADS_PER_ALLOC ) {
460 LOG("Deleting block");
461 prev->Next = block->Next;
464 //TODO: If not empty, check if it can be merged downwards
467 Mutex_Release(&List->Lock);
477 // Not on list, is this an error?
479 Mutex_Release(&List->Lock);
486 * \brief Signal all threads on a list
488 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
491 tVFS_SelectListEnt *block;
495 ENTER("pList", List);
497 // Lock to avoid concurrency issues
498 Mutex_Acquire(&List->Lock);
500 block = &List->FirstEnt;
502 // Look for the thread
505 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
507 if( block->Threads[i] )
509 LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]);
510 Semaphore_Signal( &block->Threads[i]->SleepHandle, 1 );
517 Mutex_Release(&List->Lock);