Kernel - Implementing select()
authorJohn Hodge <[email protected]>
Sun, 27 Feb 2011 13:54:09 +0000 (21:54 +0800)
committerJohn Hodge <[email protected]>
Sun, 27 Feb 2011 13:54:09 +0000 (21:54 +0800)
- Fully untested, but it does compile
- TODO: Complete the helpers (flag setting), and port blocking calls
  to use select() instead of using their own semaphores.
- TODO: Implement an alarm in threads (or timer) to implement the timeout
  argument to select()

Design Notes/VFS - Select.txt
Kernel/Makefile
Kernel/include/acess.h
Kernel/include/vfs.h
Kernel/include/vfs_ext.h
Kernel/threads.c
Kernel/vfs/select.c [new file with mode: 0644]
Makefile.cfg

index cceef5c..70e1993 100644 (file)
@@ -10,3 +10,16 @@ The VFS function select()
 - Maintains a list of processes on each node (if select has been called on that node)
 - Each process maybe has a semaphore on it (to use the semaphore code to maintain the process list)
  > Could maybe use a mutex instead
+
+
+VFS_Select(int, fd_set* read, fd_set* write, fd_set* except)
+
+read is the set of sockets that we are waiting to read from
+write         "           "           "           be able to write to
+except        "           "           "        for possible errors on
+
+
+Hence, each VFS_Node has three listener lists (or just pointers)
+- One for when data is avaliable
+- One for when space is avaliable for writing
+- One for when an error occurs (closed pipe, etc ...)
index 39e41dd..72590b8 100644 (file)
@@ -32,7 +32,7 @@ OBJ += messages.o modules.o syscalls.o system.o threads.o
 OBJ += $(addprefix vfs/fs/, $(addsuffix .o,$(FILESYSTEMS)))
 OBJ += drv/kb.o drv/vterm.o drv/proc.o drv/fifo.o drv/iocache.o drv/dma.o drv/pci.o drv/vga.o
 OBJ += binary.o bin/elf.o bin/pe.o
-OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o vfs/memfile.o vfs/nodecache.o vfs/handle.o
+OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o vfs/memfile.o vfs/nodecache.o vfs/handle.o vfs/select.o
 OBJ += vfs/fs/root.o vfs/fs/devfs.o
 OBJ += $(addprefix drv/, $(addsuffix .o,$(DRIVERS)))
 OBJ := $(addsuffix .$(ARCH), $(OBJ))
index 4286f5e..a2d406b 100644 (file)
@@ -20,6 +20,7 @@ typedef  int  tTID;
 typedef Uint   tUID;
 typedef Uint   tGID;
 typedef Sint64 tTimestamp;
+typedef Sint64 tTime;
 typedef struct sShortSpinlock  tShortSpinlock;
 typedef struct sMutex  tMutex;
 
index 8618a04..e37ebfe 100644 (file)
 
 #include <acess.h>
 
+/**
+ * \brief Thread list datatype for VFS_Select
+ */
+typedef struct sVFS_SelectList tVFS_SelectList;
+
 /**
  * \name tVFS_Node Flags
  * \brief Flag values for tVFS_Node.Flags
@@ -127,7 +132,7 @@ typedef struct sVFS_Node
         */
        
        /**
-        * \name Access controll
+        * \name Access control
         * \{
         */
        tUID    UID;    //!< ID of Owning User
@@ -139,6 +144,20 @@ typedef struct sVFS_Node
         * \}
         */
        
+       /**
+        * \name VFS_Select() fields
+        * \{
+        */
+        int    DataAvaliable;
+       tVFS_SelectList *ReadThreads;   //!< Threads waiting to read
+        int    BufferFull;
+       tVFS_SelectList *WriteThreads;  //!< Threads waiting to write
+        int    ErrorOccurred;
+       tVFS_SelectList *ErrorThreads;  //!< Threads waiting for an error
+       /**
+        * \}
+        */
+       
        /**
         * \name Common Functions
         * \brief Functions that are used no matter the value of .Flags
index 720bcbe..acee164 100644 (file)
@@ -29,6 +29,9 @@
 //! Marks a VFS handle as belonging to the kernel
 #define VFS_KERNEL_FLAG        0x40000000
 
+//! Architectual maximum number of file descriptors
+#define MAX_FILE_DESCS 128
+
 /**
  * \brief VFS_Seek directions
  */
@@ -104,6 +107,18 @@ typedef struct sFInfo
        tVFS_ACL        acls[]; //!< ACL buffer (size is passed in the \a MaxACLs argument to VFS_FInfo)
 }      tFInfo;
 
+/**
+ * \brief fd_set for select()
+ */
+typedef struct
+{
+       Uint16  flags[MAX_FILE_DESCS/16];
+}      fd_set;
+
+#define FD_CLR(fd, fdsetp) ((fdsetp)->flags[(fd)/16]&=~(1<<((fd)%16)))
+#define FD_SET(fd, fdsetp) ((fdsetp)->flags[(fd)/16]|=~(1<<((fd)%16)))
+#define FD_ISSET(fd, fdsetp) ((fdsetp)->flags[(fd)/16]&(1<<((fd)%16)))
+
 // === FUNCTIONS ===
 /**
  * \brief Initialise the VFS (called by system.c)
index ee48e3e..cafb806 100644 (file)
@@ -1312,6 +1312,7 @@ int Mutex_Acquire(tMutex *Mutex)
 /**
  * \brief Release a held mutex
  * \param Mutex        Mutex to release
+ * \note Releasing a non-held mutex has no effect
  */
 void Mutex_Release(tMutex *Mutex)
 {
diff --git a/Kernel/vfs/select.c b/Kernel/vfs/select.c
new file mode 100644 (file)
index 0000000..46d6f39
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Acess2 VFS
+ * - By thePowersGang (John Hodge)
+ * 
+ * select.c
+ * - Implements the select() system call (and supporting code)
+ */
+#define DEBUG  0
+#include <acess.h>
+#include "vfs.h"
+#include "vfs_int.h"
+#include "vfs_ext.h"
+#include <semaphore.h>
+
+// === CONSTANTS ===
+#define        NUM_THREADS_PER_ALLOC   4
+
+// === 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];
+};
+
+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);
+
+// === GLOBALS ===
+
+// === FUNCTIONS ===
+int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, int IsKernel)
+{
+       tVFS_SelectThread       thread_info;
+        int    ret;
+       
+       // 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);
+       
+       // 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);
+               return ret;
+       }
+       
+       // TODO: Implement 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);
+       }
+       
+       // 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);
+       return ret;
+}
+
+// --- Internal ---
+/**
+ * \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    i, numFlagged = 0;
+       tVFS_SelectList **list;
+        int    *flag, wantedFlagValue;
+        int    maxAllowed;
+       
+       if( !Handles )  return 0;
+       
+       for( i = 0; i < MaxHandle; i ++ )
+       {
+               tVFS_Handle     *handle;
+               
+               // Is the descriptor set
+               if( !FD_ISSET(i, Handles) )     continue;
+               
+               handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
+               // Is the handle valid?
+               if( !handle || !handle->Node )
+               {
+                       if( Type == 2 ) {       // Bad FD counts as an error
+                               numFlagged ++;
+                       }
+                       else {
+                               FD_CLR(i, Handles);
+                       }
+                       continue;
+               }
+       
+               // 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);
+                       return 0;
+               }
+               
+               // Alloc if needed
+               if( !*list ) {
+                       *list = calloc(1, sizeof(tVFS_SelectList));
+               }
+               
+               // Register
+               if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
+               {
+                       // Oops, error (or just no space)
+                       FD_CLR(i, Handles);
+               }
+               
+               // Check for the flag
+               if( !!*flag == !!wantedFlagValue )
+                       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    i, numFlagged = 0;
+       tVFS_SelectList **list;
+        int    *flag, wantedFlagValue;
+       
+       if( !Handles )  return 0;
+       
+       for( i = 0; i < MaxHandle; i ++ )
+       {
+               tVFS_Handle     *handle;
+               
+               // Is the descriptor set
+               if( !FD_ISSET(i, Handles) )     continue;
+               
+               handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
+               // Is the handle valid?
+               if( !handle || !handle->Node )
+               {
+                       if( Type == 2 ) {       // Bad FD counts as an error
+                               numFlagged ++;
+                       }
+                       else {
+                               FD_CLR(i, Handles);
+                       }
+                       continue;
+               }
+       
+               // 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);
+                       return 0;
+               }
+               
+               // Remove
+               VFS_int_Select_RemThread(*list, Thread );
+               
+               // Check for the flag
+               if( !!*flag == !!wantedFlagValue ) {
+                       numFlagged ++;
+               }
+               else {
+                       FD_CLR(i, Handles);
+               }
+       }
+       
+       return numFlagged;
+}
+
+/**
+ * \return Boolean failure
+ */
+int VFS_int_Select_AddThread(tVFS_SelectList *List, tVFS_SelectThread *Thread, int MaxAllowed)
+{
+        int    i, count = 0;
+       tVFS_SelectListEnt      *block, *prev;
+       
+       // Lock to avoid concurrency issues
+       Mutex_Acquire(&List->Lock);
+       
+       block = &List->FirstEnt;
+       
+       // Look for free space
+       do
+       {
+               for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
+               {
+                       if( block->Threads[i] == NULL )
+                       {
+                               block->Threads[i] = Thread;
+                               Mutex_Release(&List->Lock);
+                               return 0;
+                       }
+                       count ++;
+                       if( MaxAllowed && count >= MaxAllowed ) {
+                               return 1;
+                       }
+               }
+               
+               prev = block;
+               block = block->Next;
+       } while(block);
+       
+       // Create new block
+       block = malloc( sizeof(tVFS_SelectListEnt) );
+       if( !block ) {
+               Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
+               Mutex_Release(&List->Lock);
+               return -1;
+       }
+       block->Next = NULL;
+       block->Threads[0] = Thread;
+       for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
+       {
+               block->Threads[i] = NULL;
+       }
+       
+       // Add to list
+       prev->Next = block;
+       
+       // Release
+       Mutex_Release(&List->Lock);
+       
+       return 0;
+}
+
+void VFS_int_Select_RemThread(tVFS_SelectList *List, tVFS_SelectThread *Thread)
+{
+        int    i;
+       tVFS_SelectListEnt      *block, *prev;
+       
+       // 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] == Thread )
+                       {
+                               block->Threads[i] = NULL;
+                               
+                               // Check if this block is empty
+                               if( block != &List->FirstEnt )
+                               {
+                                       for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
+                                               if( block->Threads[i] )
+                                                       break;
+                                       // If empty, free it
+                                       if( i == NUM_THREADS_PER_ALLOC ) {
+                                               prev->Next = block->Next;
+                                               free(block);
+                                       }
+                                       //TODO: If not empty, check if it can be merged downwards
+                               }
+                               
+                               Mutex_Release(&List->Lock);
+                               return ;
+                       }
+               }
+               
+               prev = block;
+               block = block->Next;
+       } while(block);
+       
+       // Not on list, is this an error?
+}
+
index 0ccb74a..8c5b59b 100644 (file)
@@ -5,7 +5,7 @@
 # Install destination configuration
 DISTROOT := a:/Acess2
 xCP := mcopy -D o
-xMKDIR := mmd
+xMKDIR := mmd -D s
 
 ACESSDIR := $(dir $(lastword $(MAKEFILE_LIST)))
 ACESSDIR := $(shell cd $(ACESSDIR) && pwd)

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