M68k C++ Exceptions working!

Charles-Antoine Gauthier charles.gauthier at nrc.ca
Fri Jan 14 01:11:50 UTC 2000



Rosimildo da Silva wrote:
> 
> Charles-Antoine Gauthier wrote:
> >
> > Now all we need to figure out is whether exception handling is
> > multithread safe and for which configurations, if any. I don't quite
> > know what needs to be done to test this. A simple test would be to force
> > a context switch from the exception handler to another higher priority
> > thread that throws an exception, but that may not prove that the __throw
> > functions and its friends are multithread safe.
> >
> > Any suggestions?
> >
> 
> First of all, Congratulations. Now, the java VM porting is even closier.
> :-).
> 
> What do you imagine, is making exceptions not thread safe ?
> Some description here might to come up with some test cases.

I don't have it in front of me now, but I seem to recall that the text
in the gcc source tree that describes the exception processing mechanism
was quite clear that exception handling was not multithread safe. Global
variables are used by the low-level exception handling code. If a
context switch were to occur while the system was looking for an
exception handler and another exception was raised, good luck when the
first thread is re-dispatched.

Use of a per-thread context was described as future work.


> 
> I imagine that an exception carring some data, and it been propagated a
> few levels( 3 or 4 )
> could be a start for a test. I have put together a test case for that.
> it seems to run fine on PC386.
> In a thread safe environment, the data holded by each instance of the
> exception class should be kept
> consistent.

Yes, but that is an application programming problem, IMHO. One should
not 'throw' shared objects around unless it is safe to do that.

> 
> You might want to improve it, with a more agressive schedule.
> 
> Sorry, if you find errors. I just put this together in 10 minutes.
> 
> Rosimildo.
> 
>   ------------------------------------------------------------------------
> /*
> */
> 
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <assert.h>
> 
> #include <pthread.h>
> #include <sched.h>
> #include <rtems.h>
> 
> const int MAX_THREADS = 3;
> const int MAX_BUFFER  = 1024;
> 
> // to be fancy we could use posix keys to hold these values,
> // but I am too lazy to do that...
> struct DataPerThread
> {
>    char match;
>    size_t current_seq;
>    rtems_id tid;
> } dataPerThread[ MAX_THREADS ] = {
> 
>   { 'A', 0,    0 },
>   { 'B', 100,  0 },
>   { 'C', 1000, 0 }
> };
> 
> // Class to hold the "exception data"
> class MTExceptionChecker
> {
> public:
>    MTExceptionChecker( char match, size_t seq );
> 
>    bool stillGood( char match, size_t seq ) const;
> 
>    rtems_id get_tid() const { return tid; }
> 
>    size_t get_seq() const { return seq_num; }
> 
> private:
> 
>      rtems_id tid;                  // thread identifier
>      size_t   seq_num;              // unique sequencer number incremented per exception
>      char     buffer[ MAX_BUFFER ]; // buffer to hold an unique parttern per thread.
> };
> 
> // Ctor; Creates a instance of the exception class.
> MTExceptionChecker::MTExceptionChecker( char match, size_t seq ) :
> seq_num( seq )
> {
>   rtems_task_ident( RTEMS_SELF, 0, &tid );
>   memset( buffer, match, sizeof( buffer ) );
> }
> 
> // checks if the internal data of the exception still consistent
> // with the ones used to generate it.
> bool MTExceptionChecker::stillGood( char match, size_t seq ) const
> {
>    rtems_id id;
>    rtems_task_ident( RTEMS_SELF, 0, &id );
>    if( ( id == tid ) && ( seq_num == seq ) )
>    {
>       for( int i=0; i < MAX_BUFFER; i++ )
>       {
>          if( buffer[ i ] != match )
>             return false;
>          // make this slow...
>          sched_yield();
>       }
>       return true;
>    }
>    return false;
> }
> 
> // Raises one exception to the calling thread.
> void funcBadBoy( int index )
> {
>    dataPerThread[ index ].current_seq++;
>    throw MTExceptionChecker( dataPerThread[ index ].match,
>                              dataPerThread[ index ].current_seq );
> }
> 
> // Catch exception, and if it still consistent, re-raise it to the
> // next try block...
> void funcLevel( int level, int index )
> {
>    try
>    {
>       funcBadBoy( index );
>    }
>    catch( const MTExceptionChecker & e )
>    {
>       if( !e.stillGood( dataPerThread[ index ].match,
>                         dataPerThread[ index ].current_seq ) )
>       {
>         printf( "Level(%d, %d) failure: tid=%X, seq=%d\n",
>                   level, index, e.get_tid(), e.get_seq() );
>       }
>       throw e;
>    }
>    catch( ... )
>    {
>       printf( "weird exception raised, level(%d,%d), tid =%X\n",
>                   level, index, dataPerThread[ index ].tid );
>       assert( 0 );
>    }
> }
> 
> void funcLevel0( int index )
> {
>    try
>    {
>       funcLevel( 1, index );
>    }
>    catch( const MTExceptionChecker & e )
>    {
>       if( !e.stillGood( dataPerThread[ index ].match,
>                         dataPerThread[ index ].current_seq ) )
>       {
>         printf( "Level(0, %d) failure: tid=%X, seq=%d\n",
>                               index, e.get_tid(), e.get_seq() );
>       }
>    }
>    catch( ... )
>    {
>       printf( "Level(0, %d) - weird exception raised, tid =%X\n",
>                          index, dataPerThread[ index ].tid );
>       assert( 0 );
>    }
> }
> 
> void *thread_1( void *arg )
> {
>    rtems_id id;
>    rtems_task_ident( RTEMS_SELF, 0, &id );
>    printf( "Thread 1 executing, id=%X\n", id );
>    dataPerThread[ 0 ].tid = id;
> 
>    while( true )
>    {
>       funcLevel0( 0 );
>       // sched_yield();
>    }
>    return 0;
> }
> 
> void *thread_2( void *arg )
> {
>    rtems_id id;
>    rtems_task_ident( RTEMS_SELF, 0, &id );
>    printf( "Thread 2 executing, id=%X\n", id );
>    dataPerThread[ 1 ].tid = id;
> 
>    while( true )
>    {
>       funcLevel0( 1 );
>       // sched_yield();
>    }
>    return 0;
> }
> 
> void *thread_3( void *arg )
> {
>    rtems_id id;
>    rtems_task_ident( RTEMS_SELF, 0, &id );
>    printf( "Thread 3 executing, id=%X\n", id );
>    dataPerThread[ 2 ].tid = id;
>    while( true )
>    {
>       funcLevel0( 2 );
>       // sched_yield();
>    }
>    return 0;
> }
> 
> int rtems_main(int argc, char **argv)
> {
>    int status;
>    pthread_t id1;
>    pthread_t id2;
>    pthread_t id3;
> 
>   printf( "Exception Checker starting\n" );
>   status = pthread_create( &id1, NULL, thread_1, NULL );
>   printf( "Thread 1 - created. Status=%d\n", status );
>   status = pthread_create( &id2, NULL, thread_2, NULL );
>   printf( "Thread 2 - created. Status=%d\n", status );
>   status = pthread_create( &id3, NULL, thread_3, NULL );
>   printf( "Thread 3 - created. Status=%d\n", status );
> 
>   size_t i = 0;
>   while( true )
>   {
>     sched_yield();
> 
> // Uncomment this if you wnat to force errors
> #if 0
>     if( i == 10000 )
>     {
>        dataPerThread[ 0 ].current_seq++;
>     }
> 
>     if( i == 20000 )
>     {
>        dataPerThread[ 1 ].current_seq++;
>     }
> 
>     if( i == 30000 )
>     {
>        dataPerThread[ 2 ].current_seq++;
>        i = 0;
>     }
>     i++;
> #endif
> 
>   }
>   return 0;
> }

-- 
Charles-Antoine Gauthier
Institute for Information Technology  Institut de technologie de l'information
National Research Council of Canada   Conseil national de recherches du Canada



More information about the users mailing list