1 | ////////////////////////////////////////////////////////////////////////////// |
---|
2 | // Copyright 2002-2006 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 | ////////////////////////////////////////////////////////////////////////////// |
---|
10 | // #define USE_TWO_THREADS // ignored for single-threaded builds |
---|
11 | // #define CUSTOMIZE_MEMORY_MANAGEMENT |
---|
12 | ////////////////////////////////////////////////////////////////////////////// |
---|
13 | // The following example program demonstrates the use of asynchronous state |
---|
14 | // machines. First, it creates two objects of the same simple state machine |
---|
15 | // mimicking a table tennis player. It then sends an event (the ball) to the |
---|
16 | // first state machine. Upon reception, the first machine sends a similar |
---|
17 | // event to the second state machine, which then sends the event back to the |
---|
18 | // first machine. The two machines continue to bounce the event back and forth |
---|
19 | // until one machine "has enough" and aborts the game. The two players don't |
---|
20 | // "know" each other, they can only pass the ball back and forth because the |
---|
21 | // event representing the ball also carries two boost::function objects. |
---|
22 | // Both reference the fifo_scheduler<>::queue_event() function, binding the |
---|
23 | // scheduler and the handle of the opponent. One can be used to return the |
---|
24 | // ball to the opponent and the other can be used to abort the game. |
---|
25 | // Depending on whether the program is compiled single-threaded or |
---|
26 | // multi-threaded and the USE_TWO_THREADS define above, the two |
---|
27 | // machines either run in the same thread without/with mutex locking or in two |
---|
28 | // different threads with mutex locking. |
---|
29 | ////////////////////////////////////////////////////////////////////////////// |
---|
30 | |
---|
31 | |
---|
32 | |
---|
33 | #include <boost/statechart/event.hpp> |
---|
34 | #include <boost/statechart/asynchronous_state_machine.hpp> |
---|
35 | #include <boost/statechart/state.hpp> |
---|
36 | #include <boost/statechart/transition.hpp> |
---|
37 | #include <boost/statechart/custom_reaction.hpp> |
---|
38 | #include <boost/statechart/fifo_scheduler.hpp> |
---|
39 | #include <boost/statechart/fifo_worker.hpp> |
---|
40 | |
---|
41 | #include <boost/mpl/list.hpp> |
---|
42 | |
---|
43 | #include <boost/config.hpp> |
---|
44 | #include <boost/intrusive_ptr.hpp> |
---|
45 | #include <boost/shared_ptr.hpp> |
---|
46 | #include <boost/function.hpp> |
---|
47 | #include <boost/bind.hpp> |
---|
48 | |
---|
49 | #ifdef BOOST_MSVC |
---|
50 | # pragma warning( push ) |
---|
51 | # pragma warning( disable: 4127 ) // conditional expression is constant |
---|
52 | # pragma warning( disable: 4251 ) // class needs to have dll-interface |
---|
53 | # pragma warning( disable: 4275 ) // non-dll class used as base for dll class |
---|
54 | # pragma warning( disable: 4800 ) // forcing value to bool 'true' or 'false' |
---|
55 | #endif |
---|
56 | |
---|
57 | #ifdef BOOST_HAS_THREADS |
---|
58 | # include <boost/thread/thread.hpp> |
---|
59 | #endif |
---|
60 | #ifdef CUSTOMIZE_MEMORY_MANAGEMENT |
---|
61 | # ifdef BOOST_HAS_THREADS |
---|
62 | // for some reason the following is not automatically defined |
---|
63 | # if defined( BOOST_MSVC ) | defined( BOOST_INTEL ) |
---|
64 | # define __WIN32__ |
---|
65 | # endif |
---|
66 | # else |
---|
67 | # define BOOST_NO_MT |
---|
68 | # endif |
---|
69 | # include <boost/pool/pool_alloc.hpp> |
---|
70 | #endif |
---|
71 | |
---|
72 | #ifdef BOOST_MSVC |
---|
73 | # pragma warning( pop ) |
---|
74 | #endif |
---|
75 | |
---|
76 | #include <iostream> |
---|
77 | #include <ctime> |
---|
78 | #include <memory> // std::allocator |
---|
79 | |
---|
80 | #ifdef BOOST_NO_STDC_NAMESPACE |
---|
81 | namespace std |
---|
82 | { |
---|
83 | using ::clock_t; |
---|
84 | using ::clock; |
---|
85 | } |
---|
86 | #endif |
---|
87 | |
---|
88 | #ifdef BOOST_INTEL |
---|
89 | # pragma warning( disable: 304 ) // access control not specified |
---|
90 | # pragma warning( disable: 383 ) // reference to temporary used |
---|
91 | # pragma warning( disable: 981 ) // operands are evaluated in unspecified order |
---|
92 | #endif |
---|
93 | |
---|
94 | |
---|
95 | |
---|
96 | namespace sc = boost::statechart; |
---|
97 | namespace mpl = boost::mpl; |
---|
98 | |
---|
99 | |
---|
100 | |
---|
101 | ////////////////////////////////////////////////////////////////////////////// |
---|
102 | const unsigned int noOfEvents = 1000000; |
---|
103 | |
---|
104 | template< class T > |
---|
105 | boost::intrusive_ptr< T > MakeIntrusive( T * pObject ) |
---|
106 | { |
---|
107 | return boost::intrusive_ptr< T >( pObject ); |
---|
108 | } |
---|
109 | |
---|
110 | |
---|
111 | ////////////////////////////////////////////////////////////////////////////// |
---|
112 | struct BallReturned : sc::event< BallReturned > |
---|
113 | { |
---|
114 | boost::function1< void, const boost::intrusive_ptr< const BallReturned > & > |
---|
115 | returnToOpponent; |
---|
116 | boost::function0< void > abortGame; |
---|
117 | }; |
---|
118 | |
---|
119 | struct GameAborted : sc::event< GameAborted > {}; |
---|
120 | |
---|
121 | #ifdef CUSTOMIZE_MEMORY_MANAGEMENT |
---|
122 | typedef boost::fast_pool_allocator< int > MyAllocator; |
---|
123 | typedef sc::fifo_scheduler< |
---|
124 | sc::fifo_worker< MyAllocator >, MyAllocator > MyScheduler; |
---|
125 | #else |
---|
126 | typedef std::allocator< void > MyAllocator; |
---|
127 | typedef sc::fifo_scheduler<> MyScheduler; |
---|
128 | #endif |
---|
129 | |
---|
130 | struct Waiting; |
---|
131 | struct Player : sc::asynchronous_state_machine< |
---|
132 | Player, Waiting, MyScheduler, MyAllocator > |
---|
133 | { |
---|
134 | public: |
---|
135 | Player( |
---|
136 | my_context ctx, |
---|
137 | unsigned int maxNoOfReturns |
---|
138 | ) : |
---|
139 | my_base( ctx ), |
---|
140 | maxNoOfReturns_( maxNoOfReturns ) |
---|
141 | { |
---|
142 | } |
---|
143 | |
---|
144 | static unsigned int & TotalNoOfProcessedEvents() |
---|
145 | { |
---|
146 | return totalNoOfProcessedEvents_; |
---|
147 | } |
---|
148 | |
---|
149 | unsigned int GetMaxNoOfReturns() const |
---|
150 | { |
---|
151 | return maxNoOfReturns_; |
---|
152 | } |
---|
153 | |
---|
154 | private: |
---|
155 | static unsigned int totalNoOfProcessedEvents_; |
---|
156 | const unsigned int maxNoOfReturns_; |
---|
157 | }; |
---|
158 | |
---|
159 | unsigned int Player::totalNoOfProcessedEvents_ = 0; |
---|
160 | |
---|
161 | |
---|
162 | struct Waiting : sc::state< Waiting, Player > |
---|
163 | { |
---|
164 | public: |
---|
165 | ////////////////////////////////////////////////////////////////////////// |
---|
166 | typedef mpl::list< |
---|
167 | sc::custom_reaction< BallReturned >, |
---|
168 | sc::custom_reaction< GameAborted > |
---|
169 | > reactions; |
---|
170 | |
---|
171 | Waiting( my_context ctx ) : |
---|
172 | my_base( ctx ), |
---|
173 | noOfReturns_( 0 ), |
---|
174 | pBallReturned_( new BallReturned() ) |
---|
175 | { |
---|
176 | outermost_context_type & machine = outermost_context(); |
---|
177 | // as we will always return the same event to the opponent, we construct |
---|
178 | // and fill it here so that we can reuse it over and over |
---|
179 | pBallReturned_->returnToOpponent = boost::bind( |
---|
180 | &MyScheduler::queue_event, |
---|
181 | &machine.my_scheduler(), machine.my_handle(), _1 ); |
---|
182 | pBallReturned_->abortGame = boost::bind( |
---|
183 | &MyScheduler::queue_event, |
---|
184 | &machine.my_scheduler(), machine.my_handle(), |
---|
185 | MakeIntrusive( new GameAborted() ) ); |
---|
186 | } |
---|
187 | |
---|
188 | sc::result react( const GameAborted & ) |
---|
189 | { |
---|
190 | return DestroyMyself(); |
---|
191 | } |
---|
192 | |
---|
193 | sc::result react( const BallReturned & ballReturned ) |
---|
194 | { |
---|
195 | outermost_context_type & machine = outermost_context(); |
---|
196 | ++machine.TotalNoOfProcessedEvents(); |
---|
197 | |
---|
198 | if ( noOfReturns_++ < machine.GetMaxNoOfReturns() ) |
---|
199 | { |
---|
200 | ballReturned.returnToOpponent( pBallReturned_ ); |
---|
201 | return discard_event(); |
---|
202 | } |
---|
203 | else |
---|
204 | { |
---|
205 | ballReturned.abortGame(); |
---|
206 | return DestroyMyself(); |
---|
207 | } |
---|
208 | } |
---|
209 | |
---|
210 | private: |
---|
211 | ////////////////////////////////////////////////////////////////////////// |
---|
212 | sc::result DestroyMyself() |
---|
213 | { |
---|
214 | outermost_context_type & machine = outermost_context(); |
---|
215 | machine.my_scheduler().destroy_processor( machine.my_handle() ); |
---|
216 | machine.my_scheduler().terminate(); |
---|
217 | return terminate(); |
---|
218 | } |
---|
219 | |
---|
220 | unsigned int noOfReturns_; |
---|
221 | const boost::intrusive_ptr< BallReturned > pBallReturned_; |
---|
222 | }; |
---|
223 | |
---|
224 | |
---|
225 | ////////////////////////////////////////////////////////////////////////////// |
---|
226 | char GetKey() |
---|
227 | { |
---|
228 | char key; |
---|
229 | std::cin >> key; |
---|
230 | return key; |
---|
231 | } |
---|
232 | |
---|
233 | |
---|
234 | ////////////////////////////////////////////////////////////////////////////// |
---|
235 | int main() |
---|
236 | { |
---|
237 | std::cout << "Boost.Statechart PingPong example\n\n"; |
---|
238 | std::cout << "Threading configuration:\n"; |
---|
239 | #ifdef BOOST_HAS_THREADS |
---|
240 | std::cout << "Multi-threaded build with "; |
---|
241 | #ifdef USE_TWO_THREADS |
---|
242 | std::cout << 2; |
---|
243 | #else |
---|
244 | std::cout << 1; |
---|
245 | #endif |
---|
246 | std::cout << " thread(s).\n"; |
---|
247 | #else |
---|
248 | std::cout << "Single-threaded build\n"; |
---|
249 | #endif |
---|
250 | |
---|
251 | std::cout << "\np<CR>: Performance test\n"; |
---|
252 | std::cout << "e<CR>: Exits the program\n\n"; |
---|
253 | |
---|
254 | char key = GetKey(); |
---|
255 | |
---|
256 | while ( key != 'e' ) |
---|
257 | { |
---|
258 | switch( key ) |
---|
259 | { |
---|
260 | case 'p': |
---|
261 | { |
---|
262 | #ifdef BOOST_HAS_THREADS |
---|
263 | MyScheduler scheduler1( true ); |
---|
264 | #else |
---|
265 | MyScheduler scheduler1; |
---|
266 | #endif |
---|
267 | |
---|
268 | #ifdef USE_TWO_THREADS |
---|
269 | #ifdef BOOST_HAS_THREADS |
---|
270 | MyScheduler scheduler2( true ); |
---|
271 | #else |
---|
272 | MyScheduler & scheduler2 = scheduler1; |
---|
273 | #endif |
---|
274 | #else |
---|
275 | MyScheduler & scheduler2 = scheduler1; |
---|
276 | #endif |
---|
277 | |
---|
278 | MyScheduler::processor_handle player1 = |
---|
279 | scheduler1.create_processor< Player >( noOfEvents / 2 ); |
---|
280 | scheduler1.initiate_processor( player1 ); |
---|
281 | MyScheduler::processor_handle player2 = |
---|
282 | scheduler2.create_processor< Player >( noOfEvents / 2 ); |
---|
283 | scheduler2.initiate_processor( player2 ); |
---|
284 | |
---|
285 | boost::intrusive_ptr< BallReturned > pInitialBall = new BallReturned(); |
---|
286 | pInitialBall->returnToOpponent = boost::bind( |
---|
287 | &MyScheduler::queue_event, &scheduler1, player1, _1 ); |
---|
288 | pInitialBall->abortGame = boost::bind( |
---|
289 | &MyScheduler::queue_event, |
---|
290 | &scheduler1, player1, MakeIntrusive( new GameAborted() ) ); |
---|
291 | |
---|
292 | scheduler2.queue_event( player2, pInitialBall ); |
---|
293 | |
---|
294 | std::cout << "\nHaving players return the ball " << |
---|
295 | noOfEvents << " times. Please wait...\n"; |
---|
296 | |
---|
297 | const unsigned int prevCount = Player::TotalNoOfProcessedEvents(); |
---|
298 | const std::clock_t startTime = std::clock(); |
---|
299 | |
---|
300 | #ifdef USE_TWO_THREADS |
---|
301 | #ifdef BOOST_HAS_THREADS |
---|
302 | boost::thread otherThread( |
---|
303 | boost::bind( &MyScheduler::operator(), &scheduler2, 0 ) ); |
---|
304 | scheduler1(); |
---|
305 | otherThread.join(); |
---|
306 | #else |
---|
307 | scheduler1(); |
---|
308 | #endif |
---|
309 | #else |
---|
310 | scheduler1(); |
---|
311 | #endif |
---|
312 | |
---|
313 | const std::clock_t elapsedTime = std::clock() - startTime; |
---|
314 | std::cout << "Time to send and dispatch one event and\n" << |
---|
315 | "perform the resulting transition: "; |
---|
316 | std::cout << elapsedTime / static_cast< double >( CLOCKS_PER_SEC ) * |
---|
317 | 1000000.0 / ( Player::TotalNoOfProcessedEvents() - prevCount ) |
---|
318 | << " microseconds\n\n"; |
---|
319 | } |
---|
320 | break; |
---|
321 | |
---|
322 | default: |
---|
323 | { |
---|
324 | std::cout << "Invalid key!\n"; |
---|
325 | } |
---|
326 | } |
---|
327 | |
---|
328 | key = GetKey(); |
---|
329 | } |
---|
330 | |
---|
331 | return 0; |
---|
332 | } |
---|