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)
59 tVFS_SelectThread thread_info;
60 tVFS_SelectList **list;
61 int *flag, wanted, maxAllowed;
63 ENTER("pNode iType pTimeout", Node, Type, Timeout);
65 Semaphore_Init(&thread_info.SleepHandle, 0, 0, "VFS_SelectNode()", "");
67 if( VFS_int_Select_GetType(Type, Node, &list, &flag, &wanted, &maxAllowed) ) {
72 VFS_int_Select_AddThread(*list, &thread_info, maxAllowed);
75 VFS_int_Select_RemThread(*list, &thread_info);
80 if( !Timeout || *Timeout > 0 )
82 // TODO: Actual timeout
83 Semaphore_Wait(&thread_info.SleepHandle, 0);
86 VFS_int_Select_RemThread(*list, &thread_info);
88 LEAVE('i', *flag == wanted);
89 return *flag == wanted;
92 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, BOOL IsKernel)
94 tVFS_SelectThread thread_info;
97 Semaphore_Init(&thread_info.SleepHandle, 0, 0, "VFS_Select()", "");
99 // Notes: The idea is to make sure we onlt enter wait (on the semaphore)
100 // if we are going to be woken up (either by an event at a later time,
101 // or by an event that happened while or before we were registering).
102 // Hence, register must happen _before_ we check the state flag
103 // (See VFS_int_Select_Register), that way either we pick up the flag,
104 // or the semaphore is incremeneted (or both, but never none)
106 // Register with nodes
107 ret = VFS_int_Select_Register(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
108 ret += VFS_int_Select_Register(&thread_info, MaxHandle, WriteHandles, 1, IsKernel);
109 ret += VFS_int_Select_Register(&thread_info, MaxHandle, ErrHandles, 2, IsKernel);
111 // If there were events waiting, de-register and return
114 ret = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
115 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, WriteHandles, 1, IsKernel);
116 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ErrHandles, 2, IsKernel);
120 // TODO: Implement timeout
122 // Wait (only if there is no timeout, or it is greater than zero
123 if( !Timeout || *Timeout > 0 )
125 ret = Semaphore_Wait(&thread_info.SleepHandle, 0);
128 // Fill output (modify *Handles)
129 // - Also, de-register
130 ret = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
131 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, WriteHandles, 1, IsKernel);
132 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ErrHandles, 2, IsKernel);
136 // Mark a node as having data ready for reading
137 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
139 Node->DataAvaliable = !!IsDataAvaliable;
140 if( Node->DataAvaliable )
141 VFS_int_Select_SignalAll(Node->ReadThreads);
145 // Mark a node as having a full buffer
146 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
148 Node->BufferFull = !!IsBufferFull;
149 if( !Node->BufferFull )
150 VFS_int_Select_SignalAll(Node->WriteThreads);
154 // Mark a node as errored
155 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
157 Node->ErrorOccurred = !!IsErrorState;
158 if( Node->ErrorOccurred )
159 VFS_int_Select_SignalAll(Node->ErrorThreads);
164 int VFS_int_Select_GetType(enum eVFS_SelectTypes Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
166 // Get the type of the listen
170 if(List) *List = &Node->ReadThreads;
171 if(Flag) *Flag = &Node->DataAvaliable;
172 if(WantedFlag) *WantedFlag = 1;
173 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read
176 if(List) *List = &Node->WriteThreads;
177 if(Flag) *Flag = &Node->BufferFull;
178 if(WantedFlag) *WantedFlag = 0;
179 if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write
182 if(List) *List = &Node->ErrorThreads;
183 if(Flag) *Flag = &Node->ErrorOccurred;
184 if(WantedFlag) *WantedFlag = 1;
185 if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners
188 Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
195 * \return Number of files with an action
197 int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel)
199 int i, numFlagged = 0;
200 tVFS_SelectList **list;
201 int *flag, wantedFlagValue;
204 if( !Handles ) return 0;
206 for( i = 0; i < MaxHandle; i ++ )
210 // Is the descriptor set
211 if( !FD_ISSET(i, Handles) ) continue;
213 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
214 // Is the handle valid?
215 if( !handle || !handle->Node )
217 if( Type == 2 ) { // Bad FD counts as an error
226 // Get the type of the listen
227 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) )
232 *list = calloc(1, sizeof(tVFS_SelectList));
236 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
238 // Oops, error (or just no space)
242 // Check for the flag
243 if( !!*flag == !!wantedFlagValue )
250 * \return Number of files with an action
252 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel)
254 int i, numFlagged = 0;
255 tVFS_SelectList **list;
256 int *flag, wantedFlagValue;
258 if( !Handles ) return 0;
260 for( i = 0; i < MaxHandle; i ++ )
264 // Is the descriptor set
265 if( !FD_ISSET(i, Handles) ) continue;
267 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
268 // Is the handle valid?
269 if( !handle || !handle->Node )
271 if( Type == 2 ) { // Bad FD counts as an error
280 // Get the type of the listen
282 // Get the type of the listen
283 if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) )
287 VFS_int_Select_RemThread(*list, Thread );
289 // Check for the flag
290 if( !!*flag == !!wantedFlagValue ) {
302 * \return Boolean failure
304 int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
307 tVFS_SelectListEnt *block, *prev;
309 // Lock to avoid concurrency issues
310 Mutex_Acquire(&List->Lock);
312 block = &List->FirstEnt;
314 // Look for free space
317 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
319 if( block->Threads[i] == NULL )
321 block->Threads[i] = Thread;
322 Mutex_Release(&List->Lock);
326 if( MaxAllowed && count >= MaxAllowed ) {
336 block = malloc( sizeof(tVFS_SelectListEnt) );
338 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
339 Mutex_Release(&List->Lock);
343 block->Threads[0] = Thread;
344 for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
346 block->Threads[i] = NULL;
353 Mutex_Release(&List->Lock);
358 void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
361 tVFS_SelectListEnt *block, *prev;
363 // Lock to avoid concurrency issues
364 Mutex_Acquire(&List->Lock);
366 block = &List->FirstEnt;
368 // Look for the thread
371 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
373 if( block->Threads[i] == Thread )
375 block->Threads[i] = NULL;
377 // Check if this block is empty
378 if( block != &List->FirstEnt )
380 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
381 if( block->Threads[i] )
384 if( i == NUM_THREADS_PER_ALLOC ) {
385 prev->Next = block->Next;
388 //TODO: If not empty, check if it can be merged downwards
391 Mutex_Release(&List->Lock);
400 // Not on list, is this an error?
402 Mutex_Release(&List->Lock);
406 * \brief Signal all threads on a list
408 void VFS_int_Select_SignalAll(tVFS_SelectList *List)
411 tVFS_SelectListEnt *block, *prev;
413 // Lock to avoid concurrency issues
414 Mutex_Acquire(&List->Lock);
416 block = &List->FirstEnt;
418 // Look for the thread
421 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
423 if( block->Threads[i] )
425 Semaphore_Signal( &block->Threads[i]->SleepHandle, 1 );
433 Mutex_Release(&List->Lock);