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 Marks the thread as actively using this select
39 //! \brief Semaphore to atomically put the listener to sleep
40 tSemaphore SleepHandle; // TODO: Allow timeouts (by setting an alarm?)
44 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, int IsKernel);
45 int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull);
46 int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable);
47 int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState);
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);
56 int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, int IsKernel)
58 tVFS_SelectThread thread_info;
61 Semaphore_Init(&thread_info.SleepHandle, 0, 0, "VFS_Select()", "");
63 // Notes: The idea is to make sure we onlt enter wait (on the semaphore)
64 // if we are going to be woken up (either by an event at a later time,
65 // or by an event that happened while or before we were registering).
66 // Hence, register must happen _before_ we check the state flag
67 // (See VFS_int_Select_Register), that way either we pick up the flag,
68 // or the semaphore is incremeneted (or both, but never none)
70 // Register with nodes
71 ret = VFS_int_Select_Register(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
72 ret += VFS_int_Select_Register(&thread_info, MaxHandle, WriteHandles, 1, IsKernel);
73 ret += VFS_int_Select_Register(&thread_info, MaxHandle, ErrHandles, 2, IsKernel);
75 // If there were events waiting, de-register and return
78 ret = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
79 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 1, IsKernel);
80 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 2, IsKernel);
84 // TODO: Implement timeout
86 // Wait (only if there is no timeout, or it is greater than zero
87 if( !Timeout || *Timeout > 0 )
89 ret = Semaphore_Wait(&thread_info.SleepHandle, 0);
92 // Fill output (modify *Handles)
93 // - Also, de-register
94 ret = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel);
95 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 1, IsKernel);
96 ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 2, IsKernel);
102 * \return Number of files with an action
104 int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
106 int i, numFlagged = 0;
107 tVFS_SelectList **list;
108 int *flag, wantedFlagValue;
111 if( !Handles ) return 0;
113 for( i = 0; i < MaxHandle; i ++ )
117 // Is the descriptor set
118 if( !FD_ISSET(i, Handles) ) continue;
120 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
121 // Is the handle valid?
122 if( !handle || !handle->Node )
124 if( Type == 2 ) { // Bad FD counts as an error
133 // Get the type of the listen
137 list = &handle->Node->ReadThreads;
138 flag = &handle->Node->DataAvaliable;
140 maxAllowed = 1; // Max of 1 for read
143 list = &handle->Node->WriteThreads;
144 flag = &handle->Node->BufferFull;
146 maxAllowed = 1; // Max of 1 for write
149 list = &handle->Node->ErrorThreads;
150 flag = &handle->Node->ErrorOccurred;
152 maxAllowed = -1; // No max for error listeners
155 Log_Error("VFS", "VFS_int_Select_Deregister: BUG CHECK, Unknown Type %i", Type);
161 *list = calloc(1, sizeof(tVFS_SelectList));
165 if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
167 // Oops, error (or just no space)
171 // Check for the flag
172 if( !!*flag == !!wantedFlagValue )
179 * \return Number of files with an action
181 int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
183 int i, numFlagged = 0;
184 tVFS_SelectList **list;
185 int *flag, wantedFlagValue;
187 if( !Handles ) return 0;
189 for( i = 0; i < MaxHandle; i ++ )
193 // Is the descriptor set
194 if( !FD_ISSET(i, Handles) ) continue;
196 handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
197 // Is the handle valid?
198 if( !handle || !handle->Node )
200 if( Type == 2 ) { // Bad FD counts as an error
209 // Get the type of the listen
213 list = &handle->Node->ReadThreads;
214 flag = &handle->Node->DataAvaliable;
218 list = &handle->Node->WriteThreads;
219 flag = &handle->Node->BufferFull;
223 list = &handle->Node->ErrorThreads;
224 flag = &handle->Node->ErrorOccurred;
228 Log_Error("VFS", "VFS_int_Select_Deregister: BUG CHECK, Unknown Type %i", Type);
233 VFS_int_Select_RemThread(*list, Thread );
235 // Check for the flag
236 if( !!*flag == !!wantedFlagValue ) {
248 * \return Boolean failure
250 int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
253 tVFS_SelectListEnt *block, *prev;
255 // Lock to avoid concurrency issues
256 Mutex_Acquire(&List->Lock);
258 block = &List->FirstEnt;
260 // Look for free space
263 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
265 if( block->Threads[i] == NULL )
267 block->Threads[i] = Thread;
268 Mutex_Release(&List->Lock);
272 if( MaxAllowed && count >= MaxAllowed ) {
282 block = malloc( sizeof(tVFS_SelectListEnt) );
284 Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
285 Mutex_Release(&List->Lock);
289 block->Threads[0] = Thread;
290 for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
292 block->Threads[i] = NULL;
299 Mutex_Release(&List->Lock);
304 void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
307 tVFS_SelectListEnt *block, *prev;
309 // Lock to avoid concurrency issues
310 Mutex_Acquire(&List->Lock);
312 block = &List->FirstEnt;
314 // Look for the thread
317 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
319 if( block->Threads[i] == Thread )
321 block->Threads[i] = NULL;
323 // Check if this block is empty
324 if( block != &List->FirstEnt )
326 for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
327 if( block->Threads[i] )
330 if( i == NUM_THREADS_PER_ALLOC ) {
331 prev->Next = block->Next;
334 //TODO: If not empty, check if it can be merged downwards
337 Mutex_Release(&List->Lock);
346 // Not on list, is this an error?