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: |
35 | 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 | ////////////////////////////////////////////////////////////////////////// |
53 | FifoSchedulerTest( my_context ctx ) : |
54 | my_base( ctx ), |
55 | ctorArgs_( 0 ) |
56 | { |
57 | } |
58 | |
59 | FifoSchedulerTest( my_context ctx, int arg1 ) : |
60 | my_base( ctx ), |
61 | ctorArgs_( arg1 ) |
62 | { |
63 | } |
64 | |
65 | FifoSchedulerTest( my_context ctx, int arg1, int arg2 ) : |
66 | my_base( ctx ), |
67 | ctorArgs_( arg1 * 10 + arg2 ) |
68 | { |
69 | } |
70 | |
71 | FifoSchedulerTest( my_context ctx, int arg1, int arg2, int arg3 ) : |
72 | my_base( ctx ), |
73 | ctorArgs_( ( arg1 * 10 + arg2 ) * 10 + arg3 ) |
74 | { |
75 | } |
76 | |
77 | 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 | |
86 | 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 | |
96 | 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 | |
106 | int CtorArgs() |
107 | { |
108 | return ctorArgs_; |
109 | } |
110 | |
111 | private: |
112 | ////////////////////////////////////////////////////////////////////////// |
113 | const int ctorArgs_; |
114 | }; |
115 | |
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 | |
130 | 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 | processor: machine.my_handle(), pEvent: MakeEvent( pEvent: new EvTerminate() ) ); |
136 | return discard_event(); |
137 | } |
138 | |
139 | 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 | { |
149 | UnexpectedEventCount() : std::runtime_error( "" ) {} |
150 | }; |
151 | |
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 | |
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, expectedEventCount: 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, pEvent: MakeEvent( pEvent: new EvCheckCtorArgs( ctorArgs ) ) ); |
192 | // ... that's why 3 instead of two events must have been processed |
193 | RunScheduler( scheduler, expectedEventCount: 3UL ); |
194 | |
195 | // Since the machine has been terminated, this event will be ignored |
196 | scheduler.queue_event( processor, pEvent: MakeEvent( pEvent: new EvFail() ) ); |
197 | RunScheduler( scheduler, expectedEventCount: 1UL ); |
198 | |
199 | // Check that we can reinitiate the machine |
200 | scheduler.initiate_processor( processor ); |
201 | scheduler.queue_event( |
202 | processor, pEvent: MakeEvent( pEvent: new EvCheckCtorArgs( ctorArgs ) ) ); |
203 | RunScheduler( scheduler, expectedEventCount: 3UL ); |
204 | |
205 | // Check that we are terminated again |
206 | scheduler.queue_event( processor, pEvent: MakeEvent( pEvent: new EvFail() ) ); |
207 | RunScheduler( scheduler, expectedEventCount: 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, pEvent: MakeEvent( pEvent: new EvCheckCtorArgs( ctorArgs ) ) ); |
215 | RunScheduler( scheduler, expectedEventCount: 3UL ); |
216 | } |
217 | |
218 | void SetToTrue( bool & value ) |
219 | { |
220 | value = true; |
221 | } |
222 | |
223 | int test_main( int, char* [] ) |
224 | { |
225 | try |
226 | { |
227 | sc::fifo_scheduler<> scheduler; |
228 | Check( scheduler, processor: scheduler.create_processor< FifoSchedulerTest >(), ctorArgs: 0 ); |
229 | |
230 | Check( |
231 | scheduler, processor: scheduler.create_processor< FifoSchedulerTest >( arg1: 1 ), ctorArgs: 1 ); |
232 | |
233 | Check( |
234 | scheduler, |
235 | processor: scheduler.create_processor< FifoSchedulerTest >( |
236 | arg1: boost::cref( t: refArg1 ) ), |
237 | ctorArgs: 6 ); |
238 | |
239 | Check( |
240 | scheduler, |
241 | processor: scheduler.create_processor< FifoSchedulerTest >( arg1: 1, arg2: 2 ), |
242 | ctorArgs: 12 ); |
243 | |
244 | Check( |
245 | scheduler, |
246 | processor: scheduler.create_processor< FifoSchedulerTest >( |
247 | arg1: boost::cref( t: refArg1 ), arg2: boost::cref( t: refArg2 ) ), |
248 | ctorArgs: 65 ); |
249 | |
250 | Check( |
251 | scheduler, |
252 | processor: scheduler.create_processor< FifoSchedulerTest >( arg1: 1, arg2: 2, arg3: 3 ), |
253 | ctorArgs: 123 ); |
254 | |
255 | Check( |
256 | scheduler, |
257 | processor: scheduler.create_processor< FifoSchedulerTest >( |
258 | arg1: boost::cref( t: refArg1 ), arg2: boost::cref( t: refArg2 ), |
259 | arg3: boost::cref( t: refArg3 ) ), |
260 | ctorArgs: 654 ); |
261 | |
262 | Check( |
263 | scheduler, |
264 | processor: scheduler.create_processor< FifoSchedulerTest >( arg1: 1, arg2: 2, arg3: 3, arg4: 4 ), |
265 | ctorArgs: 1234 ); |
266 | |
267 | Check( |
268 | scheduler, |
269 | processor: scheduler.create_processor< FifoSchedulerTest >( |
270 | arg1: boost::cref( t: refArg1 ), arg2: boost::cref( t: refArg2 ), |
271 | arg3: boost::cref( t: refArg3 ), arg4: boost::cref( t: refArg4 ) ), |
272 | ctorArgs: 6543 ); |
273 | |
274 | Check( |
275 | scheduler, |
276 | processor: scheduler.create_processor< FifoSchedulerTest >( arg1: 1, arg2: 2, arg3: 3, arg4: 4, arg5: 5 ), |
277 | ctorArgs: 12345 ); |
278 | |
279 | Check( |
280 | scheduler, |
281 | processor: scheduler.create_processor< FifoSchedulerTest >( |
282 | arg1: boost::cref( t: refArg1 ), arg2: boost::cref( t: refArg2 ), |
283 | arg3: boost::cref( t: refArg3 ), arg4: boost::cref( t: refArg4 ), |
284 | arg5: boost::cref( t: refArg5 ) ), |
285 | ctorArgs: 65432 ); |
286 | |
287 | Check( |
288 | scheduler, |
289 | processor: scheduler.create_processor< FifoSchedulerTest >( arg1: 1, arg2: 2, arg3: 3, arg4: 4, arg5: 5, arg6: 6 ), |
290 | ctorArgs: 123456 ); |
291 | |
292 | Check( |
293 | scheduler, |
294 | processor: scheduler.create_processor< FifoSchedulerTest >( |
295 | arg1: boost::cref( t: refArg1 ), arg2: boost::cref( t: refArg2 ), |
296 | arg3: boost::cref( t: refArg3 ), arg4: boost::cref( t: refArg4 ), |
297 | arg5: boost::cref( t: refArg5 ), arg6: boost::cref( t: refArg6 ) ), |
298 | ctorArgs: 654321 ); |
299 | |
300 | RunScheduler( scheduler, expectedEventCount: 0UL ); |
301 | bool workItem1Processed = false; |
302 | scheduler.queue_work_item( |
303 | item: boost::bind( f: &SetToTrue, a1: boost::ref( t&: workItem1Processed ) ) ); |
304 | RunScheduler( scheduler, expectedEventCount: 1UL ); |
305 | BOOST_REQUIRE( workItem1Processed ); |
306 | |
307 | scheduler.terminate(); |
308 | RunScheduler( scheduler, expectedEventCount: 1UL ); |
309 | BOOST_REQUIRE( scheduler.terminated() ); |
310 | |
311 | RunScheduler( scheduler, expectedEventCount: 0UL ); |
312 | bool workItem2Processed = false; |
313 | scheduler.queue_work_item( |
314 | item: boost::bind( f: &SetToTrue, a1: boost::ref( t&: workItem2Processed ) ) ); |
315 | // After being terminated, a call to operator() must not process any more |
316 | // events |
317 | RunScheduler( scheduler, expectedEventCount: 0UL ); |
318 | BOOST_REQUIRE( !workItem2Processed ); |
319 | } |
320 | catch ( const UnexpectedEventCount & ) |
321 | { |
322 | BOOST_FAIL( "Unexpected event count." ); |
323 | } |
324 | |
325 | return 0; |
326 | } |
327 | |