Mercurial > ecos
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");
