Merge branch 'master' of git://git.ucc.asn.au/tpg/acess2
[tpg/acess2.git] / Kernel / vfs / select.c
index 46d6f39..439afc0 100644 (file)
@@ -4,6 +4,9 @@
  * 
  * select.c
  * - Implements the select() system call (and supporting code)
+ *
+ * TODO: Implment timeouts (via an alarm event?)
+ * TODO: Remove malloc for read/write queues
  */
 #define DEBUG  0
 #include <acess.h>
 #include "vfs_int.h"
 #include "vfs_ext.h"
 #include <semaphore.h>
+#include <threads.h>
+#include <events.h>
 
 // === CONSTANTS ===
 #define        NUM_THREADS_PER_ALLOC   4
 
+// === IMPORTS ===
+extern tThread *Proc_GetCurThread(void);
+
 // === TYPES ===
-typedef struct sVFS_SelectThread       tVFS_SelectThread;
 typedef struct sVFS_SelectListEnt      tVFS_SelectListEnt;
 
 // === STRUCTURES ===
 struct sVFS_SelectListEnt
 {
        tVFS_SelectListEnt      *Next;
-       tVFS_SelectThread       *Threads[NUM_THREADS_PER_ALLOC];
+       tThread *Threads[NUM_THREADS_PER_ALLOC];
 };
 
+// NOTE: Typedef is in vfs.h
 struct sVFS_SelectList
 {
        tMutex  Lock;
        tVFS_SelectListEnt      FirstEnt;
 };
 
-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_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed);
-void   VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread);
+// 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, Uint32 ExtraEvents, 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(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed);
+ int   VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
+ int   VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
+ int   VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed);
+void   VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *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, int TypeFlags, tTime *Timeout, const char *Name)
 {
-       tVFS_SelectThread       thread_info;
+       tThread *thisthread = Proc_GetCurThread();
+        int    ret, type;
+       
+       ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name);
+       
+       // Initialise
+       for( type = 0; type < 3; type ++ )
+       {
+               tVFS_SelectList **list;
+                int    *flag, wanted, maxAllowed;
+               if( !(TypeFlags & (1 << type)) )        continue;
+               if( VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed) ) {
+                       LEAVE('i', -1);
+                       return -1;
+               }
+       
+               // Alloc if needed
+               if( !*list )    *list = calloc(1, sizeof(tVFS_SelectList));
+       
+               VFS_int_Select_AddThread(*list, thisthread, maxAllowed);
+               if( *flag == wanted )
+               {
+                       VFS_int_Select_RemThread(*list, thisthread);
+                       LEAVE('i', 1);
+                       return 1;
+               }
+       }
+
+       // - Fast return for polling
+       if( Timeout && *Timeout == 0 )  return 0;
+
+       // Wait for things      
+       if( !Timeout || *Timeout > 0 )
+       {
+               LOG("Semaphore_Wait()");
+               // TODO: Actual timeout
+               Threads_WaitEvents( THREAD_EVENT_VFS );
+       }
+       
+       // Get return value
+       ret = 0;
+       for( type = 0; type < 3; type ++ )
+       {
+               tVFS_SelectList **list;
+                int    *flag, wanted, maxAllowed;
+               if( !(TypeFlags & (1 << type)) )        continue;
+               VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed);
+               LOG("VFS_int_Select_RemThread()");
+               VFS_int_Select_RemThread(*list, thisthread);
+               ret = ret || *flag == wanted;
+       }
+       
+       LEAVE('i', ret);
+       return ret;
+}
+
+int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel)
+{
+       tThread *thisthread = Proc_GetCurThread();
         int    ret;
        
+       ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel",
+               MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel);
+       
+       // Notes: The idea is to make sure we only enter wait (Threads_WaitEvents)
+       // 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(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
+       ret += VFS_int_Select_Register(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
+       ret += VFS_int_Select_Register(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
+       
+       LOG("Register ret = %i", ret);
        
        // If there were events waiting, de-register and return
-       if( ret )
+       if( ret > 0 )
        {
-               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(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
+               ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
+               ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
+               LEAVE('i', ret);
                return ret;
        }
        
        // TODO: Implement timeout
+       LOG("Timeout = %p", Timeout);
        
        // Wait (only if there is no timeout, or it is greater than zero
        if( !Timeout || *Timeout > 0 )
        {
-               ret = Semaphore_Wait(&thread_info.SleepHandle, 0);
+               // TODO: Timeout
+               // TODO: Allow extra events to be waited upon
+               Threads_WaitEvents( THREAD_EVENT_VFS|ExtraEvents );
        }
        
        // 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(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
+       ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
+       ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
+       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 bIsBufferFull", 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 bIsErrorState", 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(int 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(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
 {
         int    i, numFlagged = 0;
        tVFS_SelectList **list;
@@ -101,12 +242,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 +266,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 +288,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(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
 {
         int    i, numFlagged = 0;
        tVFS_SelectList **list;
@@ -177,12 +303,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 +327,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,17 +346,21 @@ int VFS_int_Select_Deregister(tVFS_SelectThread *Thread, int MaxHandle, fd_set *
                }
        }
        
+       LEAVE('i', numFlagged);
+       
        return numFlagged;
 }
 
 /**
  * \return Boolean failure
  */
-int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
+int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed)
 {
         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 +375,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 +389,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,13 +411,16 @@ int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, i
        // Release
        Mutex_Release(&List->Lock);
        
+       LEAVE('i', 0);
        return 0;
 }
 
-void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
+void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread)
 {
         int    i;
-       tVFS_SelectListEnt      *block, *prev;
+       tVFS_SelectListEnt      *block, *prev = NULL;
+       
+       ENTER("pList pThread", List, Thread);
        
        // Lock to avoid concurrency issues
        Mutex_Acquire(&List->Lock);
@@ -319,6 +444,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 +452,7 @@ void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
                                }
                                
                                Mutex_Release(&List->Lock);
+                               LEAVE('-');
                                return ;
                        }
                }
@@ -335,5 +462,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]);
+                               Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS );
+                       }
+               }
+               
+               block = block->Next;
+       } while(block);
+       
+       Mutex_Release(&List->Lock);
+       
+       LEAVE('-');
+}

UCC git Repository :: git.ucc.asn.au