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(enum eVFS_SelectTypes 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, enum eVFS_SelectTypes Type, BOOL IsKernel);
49 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes 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, enum eVFS_SelectTypes Type, tTime *Timeout, const char *Name)
59 tVFS_SelectThread *thread_info;
60 tVFS_SelectList **list;
61 int *flag, wanted, maxAllowed;
63 ENTER("pNode iType pTimeout", Node, Type, Timeout);
65 if( VFS_int_Select_GetType(Type, Node, &list, &flag, &wanted, &maxAllowed) ) {
70 thread_info = malloc(sizeof(tVFS_SelectThread));
71 if(!thread_info) return -1;
73 Semaphore_Init(&thread_info->SleepHandle, 0, 0, "VFS_SelectNode()", Name);
75 LOG("list=%p, flag=%p, wanted=%i, maxAllowed=%i", list, flag, wanted, maxAllowed);
79 *list = calloc(1, sizeof(tVFS_SelectList));
82 VFS_int_Select_AddThread(*list, thread_info, maxAllowed);
85 VFS_int_Select_RemThread(*list, thread_info);
91 if( !Timeout || *Timeout > 0 )
93 LOG("Semaphore_Wait()");
94 // TODO: Actual timeout
95 Semaphore_Wait(&thread_info->SleepHandle, 1);
98 LOG("VFS_int_Select_RemThread()");
99 VFS_int_Select_RemThread(*list, thread_info);
103 LEAVE('i', *flag == wanted);
104 return *flag == wanted;
107 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, BOOL IsKernel)
109 tVFS_SelectThread *thread_info;
112 thread_info = malloc(sizeof(tVFS_SelectThread));
113 if(!thread_info) return -1;
115 ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel",
116 MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel);
118 Semaphore_Init(&thread_info->SleepHandle, 0, 0, "VFS_Select()", "");
120 // Notes: The idea is to make sure we only enter wait (on the semaphore)
121 // if we are going to be woken up (either by an event at a later time,
122 // or by an event that happened while or before we were registering).
123 // Hence, register must happen _before_ we check the state flag
124 // (See VFS_int_Select_Register), that way either we pick up the flag,
125 // or the semaphore is incremeneted (or both, but never none)
127 // Register with nodes
128 ret = VFS_int_Select_Register(thread_info, MaxHandle, ReadHandles, 0, IsKernel);
129 ret += VFS_int_Select_Register(thread_info, MaxHandle, WriteHandles, 1, IsKernel);
130 ret += VFS_int_Select_Register(thread_info, MaxHandle, ErrHandles, 2, IsKernel);
132 LOG("Register ret = %i", ret);
134 // If there were events waiting, de-register and return
137 ret = VFS_int_Select_Deregister(thread_info, MaxHandle, ReadHandles, 0, IsKernel);
138 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, WriteHandles, 1, IsKernel);
139 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, ErrHandles, 2, IsKernel);
145 // TODO: Implement timeout
147 // Wait (only if there is no timeout, or it is greater than zero
148 if( !Timeout || *Timeout > 0 )
150 ret = Semaphore_Wait(&thread_info->SleepHandle, 1);
151 // TODO: Do something with ret
154 // Fill output (modify *Handles)
155 // - Also, de-register
156 ret = VFS_int_Select_Deregister(thread_info, MaxHandle, ReadHandles, 0, IsKernel);
157 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, WriteHandles, 1, IsKernel);
158 ret += VFS_int_Select_Deregister(thread_info, MaxHandle, ErrHandles, 2, IsKernel);
164 // Mark a node as having data ready for reading
165 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
167 ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
168 Node->DataAvaliable = !!IsDataAvaliable;
169 if( Node->DataAvaliable )
170 VFS_int_Select_SignalAll(Node->ReadThreads);
175 // Mark a node as having a full buffer
176 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
178 ENTER("pNode bIsDataAvaliable", Node, IsBufferFull);
179 Node->BufferFull = !!IsBufferFull;
180 if( !Node->BufferFull )
181 VFS_int_Select_SignalAll(Node->WriteThreads);
186 // Mark a node as errored
187 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
189 ENTER("pNode bIsDataAvaliable", Node, IsErrorState);
190 Node->ErrorOccurred = !!IsErrorState;
191 if( Node->ErrorOccurred )
192 VFS_int_Select_SignalAll(Node->ErrorThreads);
198 int VFS_int_Select_GetType(enum eVFS_SelectTypes Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
200 // Get the type of the listen
204 if(List) *List = &Node->ReadThreads;
205 if(Flag) *Flag = &Node->DataAvaliable;
206 if(WantedFlag) *WantedFlag = 1;
207 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read
210 if(List) *List = &Node->WriteThreads;
211 if(Flag) *Flag = &Node->BufferFull;
212 if(WantedFlag) *WantedFlag = 0;
213 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write
216 if(List) *List = &Node->ErrorThreads;
217 if(Flag) *Flag = &Node->ErrorOccurred;
218 if(WantedFlag) *WantedFlag = 1;
219 if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners
222 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
229 * \return Number of files with an action
231 int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel)
233 int i, numFlagged = 0;
234 tVFS_SelectList **list;
235 int *flag, wantedFlagValue;
238 if( !Handles ) return 0;
240 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
242 for( i = 0; i < MaxHandle; i ++ )
246 // Is the descriptor set
247 if( !FD_ISSET(i, Handles) ) continue;
250 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
251 // Is the handle valid?
252 if( !handle || !handle->Node )
254 if( Type == 2 ) { // Bad FD counts as an error
263 // Get the type of the listen
264 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
271 *list = calloc(1, sizeof(tVFS_SelectList));
275 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
277 // Oops, error (or just no space)
281 // Check for the flag
282 if( !!*flag == !!wantedFlagValue )
286 LEAVE('i', numFlagged);
291 * \return Number of files with an action
293 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel)
295 int i, numFlagged = 0;
296 tVFS_SelectList **list;
297 int *flag, wantedFlagValue;
299 if( !Handles ) return 0;
301 ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
303 for( i = 0; i < MaxHandle; i ++ )
307 // Is the descriptor set
308 if( !FD_ISSET(i, Handles) ) continue;
311 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
312 // Is the handle valid?
313 if( !handle || !handle->Node )
315 if( Type == 2 ) { // Bad FD counts as an error
324 // Get the type of the listen
326 // Get the type of the listen
327 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
333 VFS_int_Select_RemThread(*list, Thread );
335 // Check for the flag
336 if( !!*flag == !!wantedFlagValue ) {
344 LEAVE('i', numFlagged);
350 * \return Boolean failure
352 int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
355 tVFS_SelectListEnt *block, *prev;
357 ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
359 // Lock to avoid concurrency issues
360 Mutex_Acquire(&List->Lock);
362 block = &List->FirstEnt;
364 // Look for free space
367 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
369 if( block->Threads[i] == NULL )
371 block->Threads[i] = Thread;
372 Mutex_Release(&List->Lock);
377 if( MaxAllowed && count >= MaxAllowed ) {
390 block = malloc( sizeof(tVFS_SelectListEnt) );
392 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
393 Mutex_Release(&List->Lock);
397 block->Threads[0] = Thread;
398 for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
400 block->Threads[i] = NULL;
407 Mutex_Release(&List->Lock);
413 void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
416 tVFS_SelectListEnt *block, *prev;
418 ENTER("pList pThread", List, Thread);
420 // Lock to avoid concurrency issues
421 Mutex_Acquire(&List->Lock);
423 block = &List->FirstEnt;
425 // Look for the thread
428 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
430 if( block->Threads[i] == Thread )
432 block->Threads[i] = NULL;
434 // Check if this block is empty
435 if( block != &List->FirstEnt )
437 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
438 if( block->Threads[i] )
441 if( i == NUM_THREADS_PER_ALLOC ) {
442 LOG("Deleting block");
443 prev->Next = block->Next;
446 //TODO: If not empty, check if it can be merged downwards
449 Mutex_Release(&List->Lock);
459 // Not on list, is this an error?
461 Mutex_Release(&List->Lock);
468 * \brief Signal all threads on a list
470 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
473 tVFS_SelectListEnt *block;
477 ENTER("pList", List);
479 // Lock to avoid concurrency issues
480 Mutex_Acquire(&List->Lock);
482 block = &List->FirstEnt;
484 // Look for the thread
487 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
489 LOG("block->Threads[i] = %p", block->Threads[i]);
490 if( block->Threads[i] )
492 Semaphore_Signal( &block->Threads[i]->SleepHandle, 1 );
499 Mutex_Release(&List->Lock);