////////////////////////////////////////////////////////////////////////////// // Copyright 2005-2008 Andreas Huber Doenni // Distributed under the Boost Software License, Version 1.0. (See accompany- // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include namespace sc = boost::statechart; namespace mpl = boost::mpl; struct EvCheckCtorArgs : sc::event< EvCheckCtorArgs > { public: EvCheckCtorArgs( int expectedArgs ) : expectedArgs_( expectedArgs ) {} const int expectedArgs_; private: // avoids C4512 (assignment operator could not be generated) EvCheckCtorArgs & operator=( const EvCheckCtorArgs & ); }; struct EvTerminate : sc::event< EvTerminate > {}; struct EvFail : sc::event< EvFail > {}; struct Initial; struct FifoSchedulerTest : sc::asynchronous_state_machine< FifoSchedulerTest, Initial > { public: ////////////////////////////////////////////////////////////////////////// FifoSchedulerTest( my_context ctx ) : my_base( ctx ), ctorArgs_( 0 ) { } FifoSchedulerTest( my_context ctx, int arg1 ) : my_base( ctx ), ctorArgs_( arg1 ) { } FifoSchedulerTest( my_context ctx, int arg1, int arg2 ) : my_base( ctx ), ctorArgs_( arg1 * 10 + arg2 ) { } FifoSchedulerTest( my_context ctx, int arg1, int arg2, int arg3 ) : my_base( ctx ), ctorArgs_( ( arg1 * 10 + arg2 ) * 10 + arg3 ) { } FifoSchedulerTest( my_context ctx, int arg1, int arg2, int arg3, int arg4 ) : my_base( ctx ), ctorArgs_( ( ( arg1 * 10 + arg2 ) * 10 + arg3 ) * 10 + arg4 ) { } FifoSchedulerTest( my_context ctx, int arg1, int arg2, int arg3, int arg4, int arg5 ) : my_base( ctx ), ctorArgs_( ( ( ( arg1 * 10 + arg2 ) * 10 + arg3 ) * 10 + arg4 ) * 10 + arg5 ) { } FifoSchedulerTest( my_context ctx, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6 ) : my_base( ctx ), ctorArgs_( ( ( ( ( arg1 * 10 + arg2 ) * 10 + arg3 ) * 10 + arg4 ) * 10 + arg5 ) * 10 + arg6 ) { } int CtorArgs() { return ctorArgs_; } private: ////////////////////////////////////////////////////////////////////////// const int ctorArgs_; }; boost::intrusive_ptr< const sc::event_base > MakeEvent( const sc::event_base * pEvent ) { return boost::intrusive_ptr< const sc::event_base >( pEvent ); } struct Initial : sc::simple_state< Initial, FifoSchedulerTest > { typedef mpl::list< sc::custom_reaction< EvCheckCtorArgs >, sc::termination< EvTerminate >, sc::custom_reaction< EvFail > > reactions; sc::result react( const EvCheckCtorArgs & ev ) { BOOST_REQUIRE( ev.expectedArgs_ == outermost_context().CtorArgs() ); outermost_context_type & machine = outermost_context(); machine.my_scheduler().queue_event( machine.my_handle(), MakeEvent( new EvTerminate() ) ); return discard_event(); } sc::result react( const EvFail & ) { BOOST_FAIL( "State machine is unexpectedly still running." ); return discard_event(); } }; struct UnexpectedEventCount : public std::runtime_error { UnexpectedEventCount() : std::runtime_error( "" ) {} }; void RunScheduler( sc::fifo_scheduler<> & scheduler, unsigned long expectedEventCount ) { // Workaround: For some reason MSVC has a problem with BOOST_REQUIRE here // (C1055: compiler limit: out of keys) if ( scheduler() != expectedEventCount ) { throw UnexpectedEventCount(); } } static int refArg1; static int refArg2; static int refArg3; static int refArg4; static int refArg5; static int refArg6; void Check( sc::fifo_scheduler<> & scheduler, const sc::fifo_scheduler<>::processor_handle & processor, int ctorArgs ) { refArg1 = 6; refArg2 = 5; refArg3 = 4; refArg4 = 3; refArg5 = 2; refArg6 = 1; // Make sure the processor has been created RunScheduler( scheduler, 1UL ); refArg1 = refArg2 = refArg3 = refArg4 = refArg5 = refArg6 = 0; scheduler.initiate_processor( processor ); // This event triggers the queueing of another event, which itself // terminates the machine ... scheduler.queue_event( processor, MakeEvent( new EvCheckCtorArgs( ctorArgs ) ) ); // ... that's why 3 instead of two events must have been processed RunScheduler( scheduler, 3UL ); // Since the machine has been terminated, this event will be ignored scheduler.queue_event( processor, MakeEvent( new EvFail() ) ); RunScheduler( scheduler, 1UL ); // Check that we can reinitiate the machine scheduler.initiate_processor( processor ); scheduler.queue_event( processor, MakeEvent( new EvCheckCtorArgs( ctorArgs ) ) ); RunScheduler( scheduler, 3UL ); // Check that we are terminated again scheduler.queue_event( processor, MakeEvent( new EvFail() ) ); RunScheduler( scheduler, 1UL ); scheduler.destroy_processor( processor ); // The following will simply be ignored because the processor has already // be destroyed scheduler.initiate_processor( processor ); scheduler.queue_event( processor, MakeEvent( new EvCheckCtorArgs( ctorArgs ) ) ); RunScheduler( scheduler, 3UL ); } void SetToTrue( bool & value ) { value = true; } int test_main( int, char* [] ) { try { sc::fifo_scheduler<> scheduler; Check( scheduler, scheduler.create_processor< FifoSchedulerTest >(), 0 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( 1 ), 1 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( boost::cref( refArg1 ) ), 6 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( 1, 2 ), 12 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( boost::cref( refArg1 ), boost::cref( refArg2 ) ), 65 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3 ), 123 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( boost::cref( refArg1 ), boost::cref( refArg2 ), boost::cref( refArg3 ) ), 654 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4 ), 1234 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( boost::cref( refArg1 ), boost::cref( refArg2 ), boost::cref( refArg3 ), boost::cref( refArg4 ) ), 6543 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4, 5 ), 12345 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( boost::cref( refArg1 ), boost::cref( refArg2 ), boost::cref( refArg3 ), boost::cref( refArg4 ), boost::cref( refArg5 ) ), 65432 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4, 5, 6 ), 123456 ); Check( scheduler, scheduler.create_processor< FifoSchedulerTest >( boost::cref( refArg1 ), boost::cref( refArg2 ), boost::cref( refArg3 ), boost::cref( refArg4 ), boost::cref( refArg5 ), boost::cref( refArg6 ) ), 654321 ); RunScheduler( scheduler, 0UL ); bool workItem1Processed = false; scheduler.queue_work_item( boost::bind( &SetToTrue, boost::ref( workItem1Processed ) ) ); RunScheduler( scheduler, 1UL ); BOOST_REQUIRE( workItem1Processed ); scheduler.terminate(); RunScheduler( scheduler, 1UL ); BOOST_REQUIRE( scheduler.terminated() ); RunScheduler( scheduler, 0UL ); bool workItem2Processed = false; scheduler.queue_work_item( boost::bind( &SetToTrue, boost::ref( workItem2Processed ) ) ); // After being terminated, a call to operator() must not process any more // events RunScheduler( scheduler, 0UL ); BOOST_REQUIRE( !workItem2Processed ); } catch ( const UnexpectedEventCount & ) { BOOST_FAIL( "Unexpected event count." ); } return 0; }