X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=Kernel%2Fvfs%2Fselect.c;h=a092b07ca60f1e9d1bc606d47ca12b9554caeb8d;hb=abe6c6cf7fac39102e20cd28687b24c67f4952f8;hp=46d6f39f06d6f3f74a57f90e6d9adb72e353d1af;hpb=e573d30162c179edc6de7b7c8364a617d9f25f77;p=tpg%2Facess2.git diff --git a/Kernel/vfs/select.c b/Kernel/vfs/select.c index 46d6f39f..a092b07c 100644 --- a/Kernel/vfs/select.c +++ b/Kernel/vfs/select.c @@ -34,41 +34,111 @@ struct sVFS_SelectList struct sVFS_SelectThread { - //! \brief Marks the thread as actively using this select - int IsActive; //! \brief Semaphore to atomically put the listener to sleep tSemaphore SleepHandle; // TODO: Allow timeouts (by setting an alarm?) }; // === PROTOTYPES === - int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, int IsKernel); - int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull); - int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable); - int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState); - int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel); - int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel); +// int VFS_SelectNode(tVFS_Node *Node, enum eVFS_SelectTypes Type, tTime *Timeout); +// int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, BOOL IsKernel); +// int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull); +// int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable); +// int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState); + int VFS_int_Select_GetType(enum eVFS_SelectTypes Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed); + int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel); + int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel); int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed); void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread); +void VFS_int_Select_SignalAll(tVFS_SelectList *List); // === GLOBALS === // === FUNCTIONS === -int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, int IsKernel) +int VFS_SelectNode(tVFS_Node *Node, enum eVFS_SelectTypes Type, tTime *Timeout, const char *Name) { - tVFS_SelectThread thread_info; + tVFS_SelectThread *thread_info; + tVFS_SelectList **list; + int *flag, wanted, maxAllowed; + + ENTER("pNode iType pTimeout", Node, Type, Timeout); + + if( VFS_int_Select_GetType(Type, Node, &list, &flag, &wanted, &maxAllowed) ) { + LEAVE('i', -1); + return -1; + } + + thread_info = malloc(sizeof(tVFS_SelectThread)); + if(!thread_info) LEAVE_RET('i', -1); + + Semaphore_Init(&thread_info->SleepHandle, 0, 0, "VFS_SelectNode()", Name); + + LOG("list=%p, flag=%p, wanted=%i, maxAllowed=%i", list, flag, wanted, maxAllowed); + + // Alloc if needed + if( !*list ) { + *list = calloc(1, sizeof(tVFS_SelectList)); + } + + VFS_int_Select_AddThread(*list, thread_info, maxAllowed); + if( *flag == wanted ) + { + VFS_int_Select_RemThread(*list, thread_info); + free(thread_info); + LEAVE('i', 1); + return 1; + } + + if( !Timeout || *Timeout > 0 ) + { + LOG("Semaphore_Wait()"); + // TODO: Actual timeout + Semaphore_Wait(&thread_info->SleepHandle, 1); + } + + LOG("VFS_int_Select_RemThread()"); + VFS_int_Select_RemThread(*list, thread_info); + + free(thread_info); + + LEAVE('i', *flag == wanted); + return *flag == wanted; +} + +int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, BOOL IsKernel) +{ + tVFS_SelectThread *thread_info; int ret; + thread_info = malloc(sizeof(tVFS_SelectThread)); + if(!thread_info) return -1; + + ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel", + MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel); + + Semaphore_Init(&thread_info->SleepHandle, 0, 0, "VFS_Select()", ""); + + // Notes: The idea is to make sure we only enter wait (on the semaphore) + // if we are going to be woken up (either by an event at a later time, + // or by an event that happened while or before we were registering). + // Hence, register must happen _before_ we check the state flag + // (See VFS_int_Select_Register), that way either we pick up the flag, + // or the semaphore is incremeneted (or both, but never none) + // Register with nodes - ret = VFS_int_Select_Register(&thread_info, MaxHandle, ReadHandles, 0, IsKernel); - ret += VFS_int_Select_Register(&thread_info, MaxHandle, WriteHandles, 1, IsKernel); - ret += VFS_int_Select_Register(&thread_info, MaxHandle, ErrHandles, 2, IsKernel); + ret = VFS_int_Select_Register(thread_info, MaxHandle, ReadHandles, 0, IsKernel); + ret += VFS_int_Select_Register(thread_info, MaxHandle, WriteHandles, 1, IsKernel); + ret += VFS_int_Select_Register(thread_info, MaxHandle, ErrHandles, 2, IsKernel); + + LOG("Register ret = %i", ret); // If there were events waiting, de-register and return if( ret ) { - ret = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel); - ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 1, IsKernel); - ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 2, IsKernel); + ret = VFS_int_Select_Deregister(thread_info, MaxHandle, ReadHandles, 0, IsKernel); + ret += VFS_int_Select_Deregister(thread_info, MaxHandle, WriteHandles, 1, IsKernel); + ret += VFS_int_Select_Deregister(thread_info, MaxHandle, ErrHandles, 2, IsKernel); + free(thread_info); + LEAVE('i', ret); return ret; } @@ -77,22 +147,88 @@ int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set // Wait (only if there is no timeout, or it is greater than zero if( !Timeout || *Timeout > 0 ) { - ret = Semaphore_Wait(&thread_info.SleepHandle, 0); + ret = Semaphore_Wait(&thread_info->SleepHandle, 1); + // TODO: Do something with ret? } // Fill output (modify *Handles) // - Also, de-register - ret = VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 0, IsKernel); - ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 1, IsKernel); - ret += VFS_int_Select_Deregister(&thread_info, MaxHandle, ReadHandles, 2, IsKernel); + ret = VFS_int_Select_Deregister(thread_info, MaxHandle, ReadHandles, 0, IsKernel); + ret += VFS_int_Select_Deregister(thread_info, MaxHandle, WriteHandles, 1, IsKernel); + ret += VFS_int_Select_Deregister(thread_info, MaxHandle, ErrHandles, 2, IsKernel); + free(thread_info); + LEAVE('i', ret); return ret; } +// Mark a node as having data ready for reading +int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable) +{ + ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable); + Node->DataAvaliable = !!IsDataAvaliable; + if( Node->DataAvaliable ) + VFS_int_Select_SignalAll(Node->ReadThreads); + LEAVE('i', 0); + return 0; +} + +// Mark a node as having a full buffer +int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull) +{ + ENTER("pNode bIsDataAvaliable", Node, IsBufferFull); + Node->BufferFull = !!IsBufferFull; + if( !Node->BufferFull ) + VFS_int_Select_SignalAll(Node->WriteThreads); + LEAVE('i', 0); + return 0; +} + +// Mark a node as errored +int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState) +{ + ENTER("pNode bIsDataAvaliable", Node, IsErrorState); + Node->ErrorOccurred = !!IsErrorState; + if( Node->ErrorOccurred ) + VFS_int_Select_SignalAll(Node->ErrorThreads); + LEAVE('i', 0); + return 0; +} + // --- Internal --- +int VFS_int_Select_GetType(enum eVFS_SelectTypes Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed) +{ + // Get the type of the listen + switch(Type) + { + case 0: // Read + if(List) *List = &Node->ReadThreads; + if(Flag) *Flag = &Node->DataAvaliable; + if(WantedFlag) *WantedFlag = 1; + if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read + break; + case 1: // Write + if(List) *List = &Node->WriteThreads; + if(Flag) *Flag = &Node->BufferFull; + if(WantedFlag) *WantedFlag = 0; + if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write + break; + case 2: // Error + if(List) *List = &Node->ErrorThreads; + if(Flag) *Flag = &Node->ErrorOccurred; + if(WantedFlag) *WantedFlag = 1; + if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners + break; + default: + Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type); + return 1; + } + return 0; +} + /** * \return Number of files with an action */ -int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel) +int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel) { int i, numFlagged = 0; tVFS_SelectList **list; @@ -101,12 +237,15 @@ int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Ha if( !Handles ) return 0; + ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel); + for( i = 0; i < MaxHandle; i ++ ) { tVFS_Handle *handle; // Is the descriptor set if( !FD_ISSET(i, Handles) ) continue; + LOG("FD #%i", i); handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) ); // Is the handle valid? @@ -122,28 +261,8 @@ int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Ha } // Get the type of the listen - switch(Type) - { - case 0: // Read - list = &handle->Node->ReadThreads; - flag = &handle->Node->DataAvaliable; - wantedFlagValue = 1; - maxAllowed = 1; // Max of 1 for read - break; - case 1: // Write - list = &handle->Node->WriteThreads; - flag = &handle->Node->BufferFull; - wantedFlagValue = 0; - maxAllowed = 1; // Max of 1 for write - break; - case 2: // Error - list = &handle->Node->ErrorThreads; - flag = &handle->Node->ErrorOccurred; - wantedFlagValue = 1; - maxAllowed = -1; // No max for error listeners - break; - default: - Log_Error("VFS", "VFS_int_Select_Deregister: BUG CHECK, Unknown Type %i", Type); + if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) { + LEAVE('i', 0); return 0; } @@ -164,12 +283,14 @@ int VFS_int_Select_Register(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Ha numFlagged ++; } + LEAVE('i', numFlagged); + return numFlagged; } /** * \return Number of files with an action */ -int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel) +int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *Handles, enum eVFS_SelectTypes Type, BOOL IsKernel) { int i, numFlagged = 0; tVFS_SelectList **list; @@ -177,12 +298,15 @@ int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set * if( !Handles ) return 0; + ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel); + for( i = 0; i < MaxHandle; i ++ ) { tVFS_Handle *handle; // Is the descriptor set if( !FD_ISSET(i, Handles) ) continue; + LOG("FD #%i", i); handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) ); // Is the handle valid? @@ -198,25 +322,10 @@ int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set * } // Get the type of the listen - switch(Type) - { - case 0: // Read - list = &handle->Node->ReadThreads; - flag = &handle->Node->DataAvaliable; - wantedFlagValue = 1; - break; - case 1: // Write - list = &handle->Node->WriteThreads; - flag = &handle->Node->BufferFull; - wantedFlagValue = 0; - break; - case 2: // Error - list = &handle->Node->ErrorThreads; - flag = &handle->Node->ErrorOccurred; - wantedFlagValue = 1; - break; - default: - Log_Error("VFS", "VFS_int_Select_Deregister: BUG CHECK, Unknown Type %i", Type); + + // Get the type of the listen + if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) { + LEAVE('i', 0); return 0; } @@ -232,6 +341,8 @@ int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set * } } + LEAVE('i', numFlagged); + return numFlagged; } @@ -243,6 +354,8 @@ int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, i int i, count = 0; tVFS_SelectListEnt *block, *prev; + ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed); + // Lock to avoid concurrency issues Mutex_Acquire(&List->Lock); @@ -257,10 +370,12 @@ int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, i { block->Threads[i] = Thread; Mutex_Release(&List->Lock); + LEAVE('i', 0); return 0; } count ++; if( MaxAllowed && count >= MaxAllowed ) { + LEAVE('i', 1); return 1; } } @@ -269,6 +384,8 @@ int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, i block = block->Next; } while(block); + LOG("New block"); + // Create new block block = malloc( sizeof(tVFS_SelectListEnt) ); if( !block ) { @@ -289,6 +406,7 @@ int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, i // Release Mutex_Release(&List->Lock); + LEAVE('i', 0); return 0; } @@ -297,6 +415,8 @@ void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread) int i; tVFS_SelectListEnt *block, *prev; + ENTER("pList pThread", List, Thread); + // Lock to avoid concurrency issues Mutex_Acquire(&List->Lock); @@ -319,6 +439,7 @@ void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread) break; // If empty, free it if( i == NUM_THREADS_PER_ALLOC ) { + LOG("Deleting block"); prev->Next = block->Next; free(block); } @@ -326,6 +447,7 @@ void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread) } Mutex_Release(&List->Lock); + LEAVE('-'); return ; } } @@ -335,5 +457,46 @@ void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread) } while(block); // Not on list, is this an error? + + Mutex_Release(&List->Lock); + + LOG("Not on list"); + LEAVE('-'); } +/** + * \brief Signal all threads on a list + */ +void VFS_int_Select_SignalAll(tVFS_SelectList *List) +{ + int i; + tVFS_SelectListEnt *block; + + if( !List ) return ; + + ENTER("pList", List); + + // Lock to avoid concurrency issues + Mutex_Acquire(&List->Lock); + + block = &List->FirstEnt; + + // Look for the thread + do + { + for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ ) + { + if( block->Threads[i] ) + { + LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]); + Semaphore_Signal( &block->Threads[i]->SleepHandle, 1 ); + } + } + + block = block->Next; + } while(block); + + Mutex_Release(&List->Lock); + + LEAVE('-'); +}