diff packages/io/fileio/current/src/select.cxx @ 115:6ed91473a1cd ecos-sw-2000-08-21

Merge from eCos master repository on 2000-08-21-22:40:54-BST
author jlarmour
date Fri, 25 Aug 2000 17:32:38 +0000
parents
children 77cec8369160
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/io/fileio/current/src/select.cxx
@@ -0,0 +1,318 @@
+//==========================================================================
+//
+//      select.cxx
+//
+//      Fileio select() support
+//
+//==========================================================================
+//####COPYRIGHTBEGIN####
+//                                                                          
+// -------------------------------------------                              
+// The contents of this file are subject to the Red Hat eCos Public License 
+// Version 1.1 (the "License"); you may not use this file except in         
+// compliance with the License.  You may obtain a copy of the License at    
+// http://www.redhat.com/                                                   
+//                                                                          
+// Software distributed under the License is distributed on an "AS IS"      
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
+// License for the specific language governing rights and limitations under 
+// the License.                                                             
+//                                                                          
+// The Original Code is eCos - Embedded Configurable Operating System,      
+// released September 30, 1998.                                             
+//                                                                          
+// The Initial Developer of the Original Code is Red Hat.                   
+// Portions created by Red Hat are                                          
+// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
+// All Rights Reserved.                                                     
+// -------------------------------------------                              
+//                                                                          
+//####COPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):           nickg
+// Contributors:        nickg
+// Date:                2000-05-25
+// Purpose:             Fileio select() support
+// Description:         Support for select().
+//                      
+//              
+//              
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/hal.h>
+#include <pkgconf/kernel.h>
+#include <pkgconf/io_fileio.h>
+
+#include <cyg/kernel/ktypes.h>         // base kernel types
+#include <cyg/infra/cyg_trac.h>        // tracing macros
+#include <cyg/infra/cyg_ass.h>         // assertion macros
+
+#include <stdarg.h>                     // for fcntl()
+
+#include "fio.h"                       // Private header
+
+#include <cyg/kernel/sched.hxx>        // scheduler definitions
+#include <cyg/kernel/thread.hxx>       // thread definitions
+#include <cyg/kernel/mutex.hxx>        // mutex definitions
+#include <cyg/kernel/clock.hxx>        // clock definitions
+
+#include <cyg/kernel/sched.inl>
+#include <cyg/kernel/thread.inl>
+#include <cyg/kernel/clock.inl>
+
+//==========================================================================
+// File object locking
+
+#define LOCK_FILE( fp ) cyg_file_lock( fp )
+
+#define UNLOCK_FILE( fp ) cyg_file_unlock( fp )
+
+//==========================================================================
+// Local variables
+
+// Mutex for serializing select processing. This essntially controls
+// access to the contents of the selinfo structures embedded in the
+// client system data structures.
+static Cyg_Mutex select_mutex;
+
+// Condition variable where any thread that is waiting for a select to
+// fire is suspended. Note that select is not intended to be a real time
+// operation. Whenever any selectable event occurs, all selecting threads
+// will be resumed. They must then rescan their selectees and resuspend if
+// necessary.
+static Cyg_Condition_Variable selwait( select_mutex );
+
+static volatile cyg_uint32 selwake_count = 0;
+
+//==========================================================================
+// Timeval to ticks conversion support
+
+// Converters from sec and us to ticks
+static struct Cyg_Clock::converter us_converter, sec_converter;
+
+static cyg_bool converters_initialized = false;
+
+externC cyg_tick_count cyg_timeval_to_ticks( const struct timeval *tv )
+{
+    if( !converters_initialized )
+    {
+        // Create the converters we need.
+        Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1000, &us_converter );
+        Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1000000000, &sec_converter );
+
+        converters_initialized = true;
+    }
+    
+    // Short circuit zero timeval
+    if( tv->tv_sec == 0 && tv->tv_usec == 0 )
+    {
+        return 0;
+    }
+        
+    // Convert the seconds field to ticks.
+    cyg_tick_count ticks = Cyg_Clock::convert( tv->tv_sec, &sec_converter );
+
+    // Convert the nanoseconds. This will round down to nearest whole tick.
+    ticks += Cyg_Clock::convert( (cyg_tick_count)tv->tv_usec, &us_converter );
+
+    return ticks;
+}
+
+//==========================================================================
+// Select API function
+
+__externC int
+select(int nfd, fd_set *in, fd_set *out, fd_set *ex, struct timeval *tv)
+{
+    FILEIO_ENTRY();
+
+    int error = ENOERR;
+    int fd, mode, num;
+    cyg_file *fp;
+    fd_set in_res, out_res, ex_res;  // Result sets
+    fd_set *selection[3], *result[3];
+    cyg_tick_count ticks;
+    int mode_type[] = {CYG_FREAD, CYG_FWRITE, 0};
+    cyg_uint32 wake_count;
+    
+    FD_ZERO(&in_res);
+    FD_ZERO(&out_res);
+    FD_ZERO(&ex_res);
+
+    // Set up sets
+    selection[0] = in;   result[0] = &in_res;
+    selection[1] = out;  result[1] = &out_res;
+    selection[2] = ex;   result[2] = &ex_res;
+
+    // Compute end time
+    if (tv)
+        ticks = Cyg_Clock::real_time_clock->current_value() +
+            cyg_timeval_to_ticks( tv );
+    else ticks = 0;
+
+    // Lock the mutex
+    select_mutex.lock();
+
+    // Scan sets for possible I/O until something found, timeout or error.
+    while (!error)
+    {
+        wake_count = selwake_count;
+        
+        num = 0;  // Total file descriptors "ready"
+        for (mode = 0;  !error && mode < 3;  mode++)
+        {
+            if (selection[mode]) {
+                for (fd = 0;  !error && fd < nfd;  fd++)
+                {
+                    if (FD_ISSET(fd, selection[mode]))
+                    {
+                        fp = cyg_fp_get( fd );
+                        if( fp == NULL )
+                        {
+                            error = EBADF;
+                            break;
+                        }
+
+                        if ((*fp->f_ops->fo_select)(fp, mode_type[mode], 0))
+                        {
+                            FD_SET(fd, result[mode]);
+                            num++;
+                        }
+
+                        cyg_fp_free( fp );
+                    }
+                }
+            }
+        }
+        
+        if (num)
+        {
+            // Found something, update user's sets
+            if (in)  FD_COPY( &in_res, in );
+            if (out) FD_COPY( &out_res, out );
+            if (ex)  FD_COPY( &ex_res, ex );
+            select_mutex.unlock();
+            FILEIO_RETURN_VALUE(num);
+        }
+
+        Cyg_Scheduler::lock();
+
+        if( wake_count == selwake_count )
+        {
+            // Nothing found, see if we want to wait
+            if (tv)
+            {
+                if (ticks == 0)
+                {
+                    // Special case of "poll"
+                    select_mutex.unlock();
+                    Cyg_Scheduler::unlock();
+                    FILEIO_RETURN_VALUE(0);
+                }
+
+                if( !selwait.wait( ticks ) )
+                {
+                    // A non-standard wakeup, if the current time is equal to
+                    // or past the timeout, return zero. Otherwise return
+                    // EINTR, since we have been released.
+                    if( Cyg_Clock::real_time_clock->current_value() >= ticks )
+                    {
+                        select_mutex.unlock();
+                        FILEIO_RETURN_VALUE(0);
+                    }
+                    else error = EINTR;
+                }
+            }
+            else
+            {
+                // Wait forever (until something happens)
+            
+                if( !selwait.wait() )
+                    error = EINTR;
+            }
+        }
+
+        Cyg_Scheduler::unlock();
+        
+    } // while(!error)
+
+    select_mutex.unlock();
+ 
+    FILEIO_RETURN(error);
+}
+
+//==========================================================================
+// Select support functions.
+
+// -------------------------------------------------------------------------
+// cyg_selinit() is used to initialize a selinfo structure
+
+void cyg_selinit( struct CYG_SELINFO_TAG *sip )
+{
+    sip->si_info = 0;
+    sip->si_thread = 0;
+}
+
+// -------------------------------------------------------------------------
+// cyg_selrecord() is called when a client device needs to register
+// the current thread for selection.
+
+void cyg_selrecord( CYG_ADDRWORD info, struct CYG_SELINFO_TAG *sip )
+{
+    sip->si_info = info;
+    sip->si_thread = (CYG_ADDRESS)Cyg_Thread::self();
+}
+
+// -------------------------------------------------------------------------
+// cyg_selwakeup() is called when the client device matches the select
+// criterion, and needs to wake up a selector.
+
+void cyg_selwakeup( struct CYG_SELINFO_TAG *sip )
+{
+    // We don't actually use the si_info field of selinfo at present.
+    // A potential use would be to select one of several selwait condition
+    // variables to signal. However, that would only be necessary if we
+    // end up having lots of threads in select.
+
+    Cyg_Scheduler::lock();
+ 
+    if( sip->si_thread != 0 )
+    {
+        // If the thread pointer is still present, this selection has
+        // not been fired before. Test whether the thread is still
+        // waiting on the selwait condvar and if so, wake all waiters
+        // on it.
+        
+        // Note that this code has to be a bit careful with accessing
+        // the thread. Since there is no select cancel mechanism, the
+        // information in si_thread may be old, and the thread may
+        // have been deleted in the meantime. However, we do know that
+        // any address in si_thread was for a thread object at some
+        // time in the past, so will not be an illegal address, and
+        // the get_current_queue() member is a simple memory
+        // access. Hence the worse that this code could do is give a
+        // false positive and wake up some selectors when it
+        // shouldn't. However, care should be taken in making any
+        // changes to this code to avoid adding assumptions that the
+        // thread pointer is always valid.
+        
+        Cyg_Thread *thread = (Cyg_Thread *)sip->si_thread;
+        
+        if( thread->get_current_queue() == selwait.get_queue() )
+        {
+            sip->si_thread = 0;
+            selwait.broadcast();
+            selwake_count++;
+        }
+    }
+
+    Cyg_Scheduler::unlock();    
+}
+
+// -------------------------------------------------------------------------
+// EOF select.cxx