1 //////////////////////////////////////////////////////////////////////////////
2 // Copyright 2005-2008 Andreas Huber Doenni
3 // Distributed under the Boost Software License, Version 1.0. (See accompany-
4 // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 //////////////////////////////////////////////////////////////////////////////
6 
7 
8 
9 #include <boost/test/test_tools.hpp>
10 
11 #include <boost/statechart/asynchronous_state_machine.hpp>
12 #include <boost/statechart/fifo_scheduler.hpp>
13 #include <boost/statechart/event.hpp>
14 #include <boost/statechart/simple_state.hpp>
15 #include <boost/statechart/termination.hpp>
16 #include <boost/statechart/custom_reaction.hpp>
17 
18 #include <boost/mpl/list.hpp>
19 
20 #include <boost/bind.hpp>
21 #include <boost/ref.hpp>
22 
23 #include <stdexcept>
24 
25 
26 
27 namespace sc = boost::statechart;
28 namespace mpl = boost::mpl;
29 
30 
31 
32 struct EvCheckCtorArgs : sc::event< EvCheckCtorArgs >
33 {
34   public:
EvCheckCtorArgsEvCheckCtorArgs35     EvCheckCtorArgs( int expectedArgs ) : expectedArgs_( expectedArgs ) {}
36     const int expectedArgs_;
37 
38   private:
39     // avoids C4512 (assignment operator could not be generated)
40     EvCheckCtorArgs & operator=( const EvCheckCtorArgs & );
41 };
42 
43 struct EvTerminate : sc::event< EvTerminate > {};
44 struct EvFail : sc::event< EvFail > {};
45 
46 
47 struct Initial;
48 struct FifoSchedulerTest :
49   sc::asynchronous_state_machine< FifoSchedulerTest, Initial >
50 {
51   public:
52     //////////////////////////////////////////////////////////////////////////
FifoSchedulerTestFifoSchedulerTest53     FifoSchedulerTest( my_context ctx ) :
54       my_base( ctx ),
55       ctorArgs_( 0 )
56     {
57     }
58 
FifoSchedulerTestFifoSchedulerTest59     FifoSchedulerTest( my_context ctx, int arg1 ) :
60       my_base( ctx ),
61       ctorArgs_( arg1 )
62     {
63     }
64 
FifoSchedulerTestFifoSchedulerTest65     FifoSchedulerTest( my_context ctx, int arg1, int arg2 ) :
66       my_base( ctx ),
67       ctorArgs_( arg1 * 10 + arg2 )
68     {
69     }
70 
FifoSchedulerTestFifoSchedulerTest71     FifoSchedulerTest( my_context ctx, int arg1, int arg2, int arg3 ) :
72       my_base( ctx ),
73       ctorArgs_( ( arg1 * 10 + arg2 ) * 10 + arg3 )
74     {
75     }
76 
FifoSchedulerTestFifoSchedulerTest77     FifoSchedulerTest(
78       my_context ctx,
79       int arg1, int arg2, int arg3, int arg4
80     ) :
81       my_base( ctx ),
82       ctorArgs_( ( ( arg1 * 10 + arg2 ) * 10 + arg3 ) * 10 + arg4 )
83     {
84     }
85 
FifoSchedulerTestFifoSchedulerTest86     FifoSchedulerTest(
87       my_context ctx,
88       int arg1, int arg2, int arg3, int arg4, int arg5
89     ) :
90       my_base( ctx ),
91       ctorArgs_( ( ( ( arg1 * 10 + arg2 ) * 10 +
92         arg3 ) * 10 + arg4 ) * 10 + arg5 )
93     {
94     }
95 
FifoSchedulerTestFifoSchedulerTest96     FifoSchedulerTest(
97       my_context ctx,
98       int arg1, int arg2, int arg3, int arg4, int arg5, int arg6
99     ) :
100       my_base( ctx ),
101       ctorArgs_( ( ( ( ( arg1 * 10 + arg2 ) * 10 +
102         arg3 ) * 10 + arg4 ) * 10 + arg5 ) * 10 + arg6 )
103     {
104     }
105 
CtorArgsFifoSchedulerTest106     int CtorArgs()
107     {
108       return ctorArgs_;
109     }
110 
111   private:
112     //////////////////////////////////////////////////////////////////////////
113     const int ctorArgs_;
114 };
115 
MakeEvent(const sc::event_base * pEvent)116 boost::intrusive_ptr< const sc::event_base > MakeEvent(
117   const sc::event_base * pEvent )
118 {
119   return boost::intrusive_ptr< const sc::event_base >( pEvent );
120 }
121 
122 struct Initial : sc::simple_state< Initial, FifoSchedulerTest >
123 {
124   typedef mpl::list<
125     sc::custom_reaction< EvCheckCtorArgs >,
126     sc::termination< EvTerminate >,
127     sc::custom_reaction< EvFail >
128   > reactions;
129 
reactInitial130   sc::result react( const EvCheckCtorArgs & ev )
131   {
132     BOOST_REQUIRE( ev.expectedArgs_ == outermost_context().CtorArgs() );
133     outermost_context_type & machine = outermost_context();
134     machine.my_scheduler().queue_event(
135       machine.my_handle(), MakeEvent( new EvTerminate() ) );
136     return discard_event();
137   }
138 
reactInitial139   sc::result react( const EvFail & )
140   {
141     BOOST_FAIL( "State machine is unexpectedly still running." );
142     return discard_event();
143   }
144 };
145 
146 
147 struct UnexpectedEventCount : public std::runtime_error
148 {
UnexpectedEventCountUnexpectedEventCount149   UnexpectedEventCount() : std::runtime_error( "" ) {}
150 };
151 
RunScheduler(sc::fifo_scheduler<> & scheduler,unsigned long expectedEventCount)152 void RunScheduler(
153   sc::fifo_scheduler<> & scheduler, unsigned long expectedEventCount )
154 {
155   // Workaround: For some reason MSVC has a problem with BOOST_REQUIRE here
156   // (C1055: compiler limit: out of keys)
157   if ( scheduler() != expectedEventCount )
158   {
159     throw UnexpectedEventCount();
160   }
161 }
162 
163 static int refArg1;
164 static int refArg2;
165 static int refArg3;
166 static int refArg4;
167 static int refArg5;
168 static int refArg6;
169 
Check(sc::fifo_scheduler<> & scheduler,const sc::fifo_scheduler<>::processor_handle & processor,int ctorArgs)170 void Check(
171   sc::fifo_scheduler<> & scheduler,
172   const sc::fifo_scheduler<>::processor_handle & processor,
173   int ctorArgs )
174 {
175   refArg1 = 6;
176   refArg2 = 5;
177   refArg3 = 4;
178   refArg4 = 3;
179   refArg5 = 2;
180   refArg6 = 1;
181 
182   // Make sure the processor has been created
183   RunScheduler( scheduler, 1UL );
184 
185   refArg1 = refArg2 = refArg3 = refArg4 = refArg5 = refArg6 = 0;
186 
187   scheduler.initiate_processor( processor );
188   // This event triggers the queueing of another event, which itself
189   // terminates the machine ...
190   scheduler.queue_event(
191     processor, MakeEvent( new EvCheckCtorArgs( ctorArgs ) ) );
192   // ... that's why 3 instead of two events must have been processed
193   RunScheduler( scheduler, 3UL );
194 
195   // Since the machine has been terminated, this event will be ignored
196   scheduler.queue_event( processor, MakeEvent( new EvFail() ) );
197   RunScheduler( scheduler, 1UL );
198 
199   // Check that we can reinitiate the machine
200   scheduler.initiate_processor( processor );
201   scheduler.queue_event(
202     processor, MakeEvent( new EvCheckCtorArgs( ctorArgs ) ) );
203   RunScheduler( scheduler, 3UL );
204 
205   // Check that we are terminated again
206   scheduler.queue_event( processor, MakeEvent( new EvFail() ) );
207   RunScheduler( scheduler, 1UL );
208 
209   scheduler.destroy_processor( processor );
210   // The following will simply be ignored because the processor has already
211   // be destroyed
212   scheduler.initiate_processor( processor );
213   scheduler.queue_event(
214     processor, MakeEvent( new EvCheckCtorArgs( ctorArgs ) ) );
215   RunScheduler( scheduler, 3UL );
216 }
217 
SetToTrue(bool & value)218 void SetToTrue( bool & value )
219 {
220   value = true;
221 }
222 
test_main(int,char * [])223 int test_main( int, char* [] )
224 {
225   try
226   {
227     sc::fifo_scheduler<> scheduler;
228     Check( scheduler, scheduler.create_processor< FifoSchedulerTest >(), 0 );
229 
230     Check(
231       scheduler, scheduler.create_processor< FifoSchedulerTest >( 1 ), 1 );
232 
233     Check(
234       scheduler,
235       scheduler.create_processor< FifoSchedulerTest >(
236         boost::cref( refArg1 ) ),
237       6 );
238 
239     Check(
240       scheduler,
241       scheduler.create_processor< FifoSchedulerTest >( 1, 2 ),
242       12 );
243 
244     Check(
245       scheduler,
246       scheduler.create_processor< FifoSchedulerTest >(
247         boost::cref( refArg1 ), boost::cref( refArg2 ) ),
248       65 );
249 
250     Check(
251       scheduler,
252       scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3 ),
253       123 );
254 
255     Check(
256       scheduler,
257       scheduler.create_processor< FifoSchedulerTest >(
258         boost::cref( refArg1 ), boost::cref( refArg2 ),
259         boost::cref( refArg3 ) ),
260       654 );
261 
262     Check(
263       scheduler,
264       scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4 ),
265       1234 );
266 
267     Check(
268       scheduler,
269       scheduler.create_processor< FifoSchedulerTest >(
270         boost::cref( refArg1 ), boost::cref( refArg2 ),
271         boost::cref( refArg3 ), boost::cref( refArg4 ) ),
272       6543 );
273 
274     Check(
275       scheduler,
276       scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4, 5 ),
277       12345 );
278 
279     Check(
280       scheduler,
281       scheduler.create_processor< FifoSchedulerTest >(
282         boost::cref( refArg1 ), boost::cref( refArg2 ),
283         boost::cref( refArg3 ), boost::cref( refArg4 ),
284         boost::cref( refArg5 ) ),
285       65432 );
286 
287     Check(
288       scheduler,
289       scheduler.create_processor< FifoSchedulerTest >( 1, 2, 3, 4, 5, 6 ),
290       123456 );
291 
292     Check(
293       scheduler,
294       scheduler.create_processor< FifoSchedulerTest >(
295         boost::cref( refArg1 ), boost::cref( refArg2 ),
296         boost::cref( refArg3 ), boost::cref( refArg4 ),
297         boost::cref( refArg5 ), boost::cref( refArg6 ) ),
298       654321 );
299 
300     RunScheduler( scheduler, 0UL );
301     bool workItem1Processed = false;
302     scheduler.queue_work_item(
303       boost::bind( &SetToTrue, boost::ref( workItem1Processed ) ) );
304     RunScheduler( scheduler, 1UL );
305     BOOST_REQUIRE( workItem1Processed );
306 
307     scheduler.terminate();
308     RunScheduler( scheduler, 1UL );
309     BOOST_REQUIRE( scheduler.terminated() );
310 
311     RunScheduler( scheduler, 0UL );
312     bool workItem2Processed = false;
313     scheduler.queue_work_item(
314       boost::bind( &SetToTrue, boost::ref( workItem2Processed ) ) );
315     // After being terminated, a call to operator() must not process any more
316     // events
317     RunScheduler( scheduler, 0UL );
318     BOOST_REQUIRE( !workItem2Processed );
319   }
320   catch ( const UnexpectedEventCount & )
321   {
322     BOOST_FAIL( "Unexpected event count." );
323   }
324 
325   return 0;
326 }
327