changeset 410:064536ee7f68

* src/fio.h: Added macros to deal with signal handling. These will call into the POSIX package if it is present, but are null if it is not. * src/select.cxx: Renamed original select() to cyg_pselect() and added select() and pselect() routine from POSIX-200X to call it. Reorganized code a little and added calls to macros to deal with signal delivery and detection. * cdl/fileio.cdl: * tests/pselect.c: New test added to verify the signal masking and delivery behaviour of pselect(). * tests/select.c: Fixed up some printfs to make it a little tidier.
author nickg
date Mon, 11 Nov 2002 23:58:53 +0000
parents 10e7722168ad
children de449c532876
files packages/io/fileio/current/ChangeLog packages/io/fileio/current/cdl/fileio.cdl packages/io/fileio/current/src/fio.h packages/io/fileio/current/src/select.cxx packages/io/fileio/current/tests/pselect.c packages/io/fileio/current/tests/select.c
diffstat 6 files changed, 513 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/packages/io/fileio/current/ChangeLog
+++ b/packages/io/fileio/current/ChangeLog
@@ -1,3 +1,20 @@
+2002-11-10  Nick Garnett  <nickg@ecoscentric.com>
+
+	* src/fio.h: Added macros to deal with signal handling. These will
+	call into the POSIX package if it is present, but are null if it
+	is not.
+
+	* src/select.cxx: Renamed original select() to cyg_pselect() and
+	added select() and pselect() routine from POSIX-200X to call
+	it. Reorganized code a little and added calls to macros to deal
+	with signal delivery and detection.
+
+	* cdl/fileio.cdl: 
+	* tests/pselect.c: New test added to verify the signal masking and
+	delivery behaviour of pselect().
+
+	* tests/select.c: Fixed up some printfs to make it a little tidier.
+
 2002-11-03  Gary Thomas  <gthomas@ecoscentric.com>
 
 	* src/io.cxx: 
--- a/packages/io/fileio/current/cdl/fileio.cdl
+++ b/packages/io/fileio/current/cdl/fileio.cdl
@@ -9,6 +9,7 @@
 ## -------------------------------------------
 ## This file is part of eCos, the Embedded Configurable Operating System.
 ## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+## Copyright (C) 2002 Nick Garnett
 ##
 ## eCos is free software; you can redistribute it and/or modify it under
 ## the terms of the GNU General Public License as published by the Free
@@ -220,7 +221,7 @@ cdl_package CYGPKG_IO_FILEIO {
             display "Fileio tests"
             flavor  data
             no_define
-            calculated { "tests/fileio1.c tests/socket.c tests/select.c tests/stdio.c" }
+            calculated { "tests/fileio1.c tests/socket.c tests/select.c tests/stdio.c tests/pselect.c" }
             description   "
                 This option specifies the set of tests for the FileIO package."
         }
--- a/packages/io/fileio/current/src/fio.h
+++ b/packages/io/fileio/current/src/fio.h
@@ -11,6 +11,7 @@
 // -------------------------------------------
 // This file is part of eCos, the Embedded Configurable Operating System.
 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// Copyright (C) 2002 Nick Garnett
 //
 // eCos is free software; you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free
@@ -98,12 +99,28 @@
 
 #define CYG_FILEIO_FUNCTION_FINISH() CYG_POSIX_FUNCTION_FINISH()
 
+#define CYG_FILEIO_SIGMASK_SET( __set, __oset ) \
+        CYG_PTHREAD_SIGMASK_SET( __set, __oset )
+
+#define CYG_FILEIO_SIGPENDING() CYG_POSIX_SIGPENDING()
+
+#define CYG_FILEIO_DELIVER_SIGNALS( __mask ) \
+        CYG_POSIX_DELIVER_SIGNALS( __mask )
+
 #else
 
 #define CYG_FILEIO_FUNCTION_START() CYG_EMPTY_STATEMENT
 
 #define CYG_FILEIO_FUNCTION_FINISH() CYG_EMPTY_STATEMENT
 
+#define CYG_FILEIO_SIGMASK_SET( __set, __oset ) CYG_EMPTY_STATEMENT
+
+#define CYG_FILEIO_SIGPENDING() (0)
+
+#define CYG_FILEIO_DELIVER_SIGNALS( __mask ) CYG_EMPTY_STATEMENT
+
+typedef int sigset_t;
+
 #endif
 
 //=============================================================================
--- a/packages/io/fileio/current/src/select.cxx
+++ b/packages/io/fileio/current/src/select.cxx
@@ -9,6 +9,7 @@
 // -------------------------------------------
 // This file is part of eCos, the Embedded Configurable Operating System.
 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// Copyright (C) 2002 Nick Garnett
 //
 // eCos is free software; you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free
@@ -137,8 +138,9 @@ externC cyg_tick_count cyg_timeval_to_ti
 //==========================================================================
 // Select API function
 
-__externC int
-select(int nfd, fd_set *in, fd_set *out, fd_set *ex, struct timeval *tv)
+static int
+cyg_pselect(int nfd, fd_set *in, fd_set *out, fd_set *ex,
+           struct timeval *tv, const sigset_t *mask)
 {
     FILEIO_ENTRY();
 
@@ -150,7 +152,8 @@ select(int nfd, fd_set *in, fd_set *out,
     cyg_tick_count ticks;
     int mode_type[] = {CYG_FREAD, CYG_FWRITE, 0};
     cyg_uint32 wake_count;
-    
+    sigset_t oldmask;
+
     FD_ZERO(&in_res);
     FD_ZERO(&out_res);
     FD_ZERO(&ex_res);
@@ -207,61 +210,132 @@ select(int nfd, fd_set *in, fd_set *out,
             if (out) FD_COPY( &out_res, out );
             if (ex)  FD_COPY( &ex_res, ex );
             select_mutex.unlock();
+            CYG_FILEIO_DELIVER_SIGNALS( mask );
             FILEIO_RETURN_VALUE(num);
         }
 
         Cyg_Scheduler::lock();
 
-        if( wake_count == selwake_count )
+        // Switch to the supplied signal mask. This will permit delivery
+        // of any signals that might terminate this select operation.
+        
+        CYG_FILEIO_SIGMASK_SET( mask, &oldmask );
+    
+        do
         {
-            // Nothing found, see if we want to wait
-            if (tv)
+
+            // We need to see if any signals have been posted while we
+            // were testing all those files. The handlers will not
+            // have run because we have ASRs inhibited but the signal
+            // will have been set pending.
+
+            if( CYG_FILEIO_SIGPENDING() )
             {
-                if (ticks == 0)
+                // There are pending signals so we need to terminate
+                // the select operation and return EINTR. Handlers for
+                // the pending signals will be called just before we
+                // return.
+
+                error = EINTR;
+                break;
+            }
+            
+            if( wake_count == selwake_count )
+            {
+                // Nothing found, see if we want to wait
+                if (tv)
                 {
                     // Special case of "poll"
-                    select_mutex.unlock();
-                    Cyg_Scheduler::unlock();
-                    FILEIO_RETURN_VALUE(0);
-                }
+                    if (ticks == 0)
+                    {
+                        error = EAGAIN;
+                        break;
+                    }
 
-                ticks += Cyg_Clock::real_time_clock->current_value();
+                    ticks += Cyg_Clock::real_time_clock->current_value();
                 
-                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 )
+                    if( !selwait.wait( ticks ) )
                     {
-                        select_mutex.unlock();
-                        Cyg_Scheduler::unlock();
-                        FILEIO_RETURN_VALUE(0);
+                        // 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 )
+                        {
+                            error = EAGAIN;
+                            break;
+                        }
+                        else error = EINTR;
                     }
-                    else error = EINTR;
+
+                    ticks -= Cyg_Clock::real_time_clock->current_value();
                 }
-
-                ticks -= Cyg_Clock::real_time_clock->current_value();
+                else
+                {
+                    // Wait forever (until something happens)
+            
+                    if( !selwait.wait() )
+                        error = EINTR;
+                }
             }
-            else
-            {
-                // Wait forever (until something happens)
-            
-                if( !selwait.wait() )
-                    error = EINTR;
-            }
-        }
+
+        } while(0);
 
+        CYG_FILEIO_SIGMASK_SET( &oldmask, NULL );
+        
         Cyg_Scheduler::unlock();
         
     } // while(!error)
 
     select_mutex.unlock();
  
+    // If the error code is EAGAIN, this means that a timeout has
+    // happened. We return zero in that case, rather than a proper
+    // error code.
+    // If the error code is EINTR, then a signal may be pending
+    // delivery. Call back into the POSIX package to handle it.
+    
+    if( error == EAGAIN )
+        FILEIO_RETURN_VALUE(0);
+    else if( error == EINTR )
+        CYG_FILEIO_DELIVER_SIGNALS( mask );
+
     FILEIO_RETURN(error);
 }
 
+// -------------------------------------------------------------------------
+// Select API function
+
+__externC int
+select(int nfd, fd_set *in, fd_set *out, fd_set *ex, struct timeval *tv)
+{
+	return cyg_pselect(nfd, in, out, ex, tv, NULL);
+}
+
+// -------------------------------------------------------------------------
+// Pselect API function
+//
+// This is derived from the POSIX-200X specification.
+
+#ifdef CYGPKG_POSIX
+
+__externC int
+pselect(int nfd, fd_set *in, fd_set *out, fd_set *ex,
+	const struct timespec *ts, const sigset_t *sigmask)
+{
+	struct timeval tv;
+
+	if (ts != NULL)
+        {
+            tv.tv_sec = ts->tv_sec;
+            tv.tv_usec = ts->tv_nsec/1000;
+        }
+
+	return cyg_pselect(nfd, in, out, ex, &tv, sigmask);
+}
+
+#endif
+
 //==========================================================================
 // Select support functions.
 
new file mode 100644
--- /dev/null
+++ b/packages/io/fileio/current/tests/pselect.c
@@ -0,0 +1,361 @@
+//==========================================================================
+//
+//      pselect.c
+//
+//      Test pselect implementation
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// Copyright (C) 2002 Nick Garnett
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):           nickg
+// Contributors:        nickg
+// Date:                2002-11-08
+// Purpose:             Test pselect implementation
+// Description:         
+//                      
+//                      
+//                      
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/system.h>
+#include <pkgconf/isoinfra.h>
+
+#ifndef CYGINT_ISO_PTHREAD_IMPL
+# define NA_MSG "POSIX threads needed to run test"
+#elif !defined CYGPKG_NET
+# define NA_MSG "NET package needed to run test"
+#endif
+
+#include <cyg/infra/testcase.h>
+
+#ifndef NA_MSG
+
+#include <pkgconf/hal.h>
+#include <pkgconf/kernel.h>
+#include <pkgconf/io_fileio.h>
+
+#define __ECOS 1                        // dont like this at all
+
+#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 <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+
+#include <network.h>
+#include <arpa/inet.h>
+
+#include <pthread.h>
+#include <signal.h>
+
+#include <sys/select.h>
+
+
+#include <cyg/infra/diag.h>            // HAL polled output
+
+//--------------------------------------------------------------------------
+
+#define SHOW_RESULT( _fn, _res ) \
+diag_printf("INFO: " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):"");
+
+//--------------------------------------------------------------------------
+// Thread stacks
+
+char thread1_stack[PTHREAD_STACK_MIN*2];
+char thread2_stack[PTHREAD_STACK_MIN*2];
+
+//--------------------------------------------------------------------------
+// Local variables
+
+// Thread IDs
+pthread_t thread1;
+pthread_t thread2;
+
+struct sockaddr_in sa;
+
+volatile int sigusr1_calls = 0;
+volatile int sigusr1_sent = 0;
+volatile int pselect_wakeups = 0;
+volatile int pselect_eintr = 0;
+
+volatile cyg_bool running = true;
+
+//--------------------------------------------------------------------------
+
+void show_fdsets( char *s, int nfd, fd_set *rd, fd_set *wr, fd_set *ex )
+{
+    int i;
+    diag_printf("INFO:<%s nfd %d ",s,nfd);
+
+    if( rd )
+    {
+        diag_printf("rd: [");
+        for( i = 0; i < nfd ; i++ )
+            if( FD_ISSET( i, rd ) ) diag_printf("%d ",i);
+        diag_printf("] ");        
+    }    
+    if( wr )
+    {
+        diag_printf("wr: [");                
+        for( i = 0; i < nfd ; i++ )
+            if( FD_ISSET( i, wr ) ) diag_printf("%d ",i);
+        diag_printf("] ");        
+    }
+    if( ex )
+    {
+        diag_printf("ex: [");                
+        for( i = 0; i < nfd ; i++ )
+            if( FD_ISSET( i, ex ) ) diag_printf("%d ",i);
+        diag_printf("] ");                
+    }
+
+    diag_printf(">\n");
+}
+
+//--------------------------------------------------------------------------
+
+void sigusr1( int sig, siginfo_t *info, void *context )
+{
+    CYG_TEST_CHECK( pthread_self() == thread1, "Sigusr1: not called by thread 1\n");
+    
+    sigusr1_calls++;
+}
+
+//--------------------------------------------------------------------------
+// Selecting thread
+
+// This thread just opens up a socket ready to accept a connection and
+// then calls pselect() to wait for it. The timeout is set to 0 so we
+// actually just poll.
+
+void *pthread_entry1( void *arg)
+{
+    int fd = 0;
+    int err;
+    fd_set rd, wr;
+    sigset_t mask, oldmask;
+    struct sigaction sigact;
+    struct timespec ts;
+    
+    CYG_TEST_INFO( "Thread 1 running" );
+
+    FD_ZERO( &rd );
+    FD_ZERO( &wr );
+
+    sigfillset( &mask );
+    pthread_sigmask( SIG_SETMASK, &mask, &oldmask );
+    
+    sigdelset( &mask, SIGUSR1 );
+
+    sigact.sa_mask = mask;
+    sigact.sa_flags = SA_SIGINFO;
+    sigact.sa_sigaction = sigusr1;
+
+    err = sigaction( SIGUSR1, &sigact, NULL );
+    if( err < 0 ) SHOW_RESULT( sigact, err );
+
+    CYG_TEST_INFO( "Thread1: calling socket()");        
+    fd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
+    if( fd < 0 ) SHOW_RESULT( socket, fd );
+    CYG_TEST_CHECK( fd >= 0, "socket() returned error");
+
+    CYG_TEST_INFO( "Thread1: calling bind()");
+    err = bind( fd, (struct sockaddr *)&sa, sizeof(sa));
+    if( err < 0 ) SHOW_RESULT( bind, err );    
+    CYG_TEST_CHECK( err == 0, "bind() returned error");
+
+    CYG_TEST_INFO( "Thread1: calling listen()");
+    err = listen( fd, 3);
+    if( err < 0 ) SHOW_RESULT( listen, err );    
+    CYG_TEST_CHECK( err == 0, "listen() returned error");
+
+    FD_SET( fd, &rd );
+
+    ts.tv_sec = 0;
+    ts.tv_nsec = 0;
+        
+    
+    while( running )
+    {
+        fd_set rd_res = rd;
+        fd_set wr_res = wr;
+
+//        ts.tv_nsec = 1000000 * (pselect_wakeups % 10);
+        
+        err = pselect( 8, &rd_res, &wr_res, NULL, &ts, &mask );
+        if( err < 0 )
+        {
+            if( errno == EINTR ) pselect_eintr++;
+            else SHOW_RESULT( pselect, err );
+        }
+        if( err > 0 ) show_fdsets( "Thread1 result: ", 8, &rd_res, &wr_res, NULL );
+        pselect_wakeups++;
+        
+    }
+
+    pthread_sigmask( SIG_SETMASK, &oldmask, NULL );
+
+    pthread_exit(arg);
+}
+
+//--------------------------------------------------------------------------
+
+void *pthread_entry2( void *arg)
+{
+    struct timespec zzz;
+    int err;
+    
+    zzz.tv_sec = 0;
+    zzz.tv_nsec = 10*1000000;
+    
+    CYG_TEST_INFO( "Thread 2: running" );
+
+    CYG_TEST_INFO( "Thread 2: sleeping" );
+    nanosleep( &zzz, NULL );
+    nanosleep( &zzz, NULL );
+    nanosleep( &zzz, NULL );
+    
+    while( sigusr1_sent < 20000 )
+    {
+        nanosleep( &zzz, NULL );
+
+        err = pthread_kill( thread1, SIGUSR1 );
+        if( err < 0 ) SHOW_RESULT( pthread_kill, err );
+
+        sigusr1_sent++;
+
+        if( (sigusr1_sent % 500) == 0 )
+            diag_printf("INFO: <Thread 2: %d signals sent>\n",sigusr1_sent);
+    }
+
+    running = false;
+        
+    CYG_TEST_INFO( "Thread 2: exit" );
+    pthread_exit( arg );
+}
+
+//==========================================================================
+// main
+
+int main( int argc, char **argv )
+{
+    void *retval;
+    pthread_attr_t attr;
+    struct sched_param schedparam;
+
+    CYG_TEST_INIT();
+
+    sa.sin_family = AF_INET;
+    sa.sin_len = sizeof(sa);
+    inet_aton("127.0.0.1", &sa.sin_addr);
+    sa.sin_port = htons(1234);
+    init_all_network_interfaces();
+    
+    // Create test threads
+
+    {
+        pthread_attr_init( &attr );
+
+        schedparam.sched_priority = 5;
+        pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED );
+        pthread_attr_setschedpolicy( &attr, SCHED_RR );
+        pthread_attr_setschedparam( &attr, &schedparam );
+        pthread_attr_setstackaddr( &attr, (void *)&thread1_stack[sizeof(thread1_stack)] );
+        pthread_attr_setstacksize( &attr, sizeof(thread1_stack) );
+
+        pthread_create( &thread1,
+                        &attr,
+                        pthread_entry1,
+                        (void *)0x12345671);
+    }
+
+    {
+        pthread_attr_init( &attr );
+
+        schedparam.sched_priority = 10;
+        pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED );
+        pthread_attr_setschedpolicy( &attr, SCHED_RR );
+        pthread_attr_setschedparam( &attr, &schedparam );
+        pthread_attr_setstackaddr( &attr, (void *)&thread2_stack[sizeof(thread2_stack)] );
+        pthread_attr_setstacksize( &attr, sizeof(thread2_stack) );
+
+        pthread_create( &thread2,
+                        &attr,
+                        pthread_entry2,
+                        (void *)0x12345672);
+    }
+    
+    // Now join with thread1
+    CYG_TEST_INFO( "Main: calling pthread_join(thread1)");
+    pthread_join( thread1, &retval );
+
+    // And thread 2
+    CYG_TEST_INFO( "Main: calling pthread_join(thread2)");
+    pthread_join( thread2, &retval );
+
+    diag_printf("INFO: pselect returns: %d\n", pselect_wakeups );
+    diag_printf("INFO: pselect EINTR returns: %d\n", pselect_eintr );
+    diag_printf("INFO: SIGUSR1 sent: %d\n", sigusr1_sent );
+    diag_printf("INFO: SIGUSR1 delivered: %d\n", sigusr1_calls );
+    
+    CYG_TEST_CHECK( sigusr1_sent == sigusr1_calls, "SIGUSR1 calls != delivered");
+    CYG_TEST_CHECK( sigusr1_sent == pselect_eintr, "SIGUSR1 calls != pselect EINTR wakeups");
+    
+    CYG_TEST_PASS_FINISH("pselect");
+}
+
+#else
+
+//==========================================================================
+// main
+
+void cyg_start(void)
+{
+    CYG_TEST_INIT();
+
+    CYG_TEST_NA(NA_MSG);
+}
+
+#endif
+
--- a/packages/io/fileio/current/tests/select.c
+++ b/packages/io/fileio/current/tests/select.c
@@ -103,7 +103,7 @@
 //--------------------------------------------------------------------------
 
 #define SHOW_RESULT( _fn, _res ) \
-diag_printf("<INFO>: " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):"");
+diag_printf("INFO: " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):"");
 
 //--------------------------------------------------------------------------
 // Thread stack.
@@ -147,25 +147,28 @@ static char sbuf3[TEST_BUFSIZE];
 void show_fdsets( char *s, int nfd, fd_set *rd, fd_set *wr, fd_set *ex )
 {
     int i;
-    diag_printf("INFO:<%s nfd %d",s,nfd);
+    diag_printf("INFO:<%s nfd %d ",s,nfd);
 
     if( rd )
     {
-        diag_printf("rd: ");
+        diag_printf("rd: [");
         for( i = 0; i < nfd ; i++ )
             if( FD_ISSET( i, rd ) ) diag_printf("%d ",i);
-    }
+        diag_printf("] ");        
+    }    
     if( wr )
     {
-        diag_printf("wr: ");                
+        diag_printf("wr: [");                
         for( i = 0; i < nfd ; i++ )
             if( FD_ISSET( i, wr ) ) diag_printf("%d ",i);
+        diag_printf("] ");        
     }
     if( ex )
     {
-        diag_printf("ex: ");                
+        diag_printf("ex: [");                
         for( i = 0; i < nfd ; i++ )
             if( FD_ISSET( i, ex ) ) diag_printf("%d ",i);
+        diag_printf("] ");                
     }
 
     diag_printf(">\n");