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