1 | ////////////////////////////////////////////////////////////////////////////// |
2 | // Copyright 2002-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 | ////////////////////////////////////////////////////////////////////////////// |
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 | #include "Player.hpp" |
33 | |
34 | #include <boost/statechart/asynchronous_state_machine.hpp> |
35 | #include <boost/statechart/fifo_worker.hpp> |
36 | |
37 | #include <boost/mpl/list.hpp> |
38 | #include <boost/config.hpp> |
39 | #include <boost/intrusive_ptr.hpp> |
40 | #include <boost/function.hpp> |
41 | #include <boost/bind.hpp> |
42 | |
43 | #ifdef BOOST_HAS_THREADS |
44 | # include <boost/thread/thread.hpp> |
45 | #endif |
46 | |
47 | #include <iostream> |
48 | #include <ctime> |
49 | |
50 | #ifdef BOOST_NO_STDC_NAMESPACE |
51 | namespace std |
52 | { |
53 | using ::clock_t; |
54 | using ::clock; |
55 | } |
56 | #endif |
57 | |
58 | #ifdef BOOST_INTEL |
59 | # pragma warning( disable: 304 ) // access control not specified |
60 | # pragma warning( disable: 383 ) // reference to temporary used |
61 | # pragma warning( disable: 981 ) // operands are evaluated in unspecified order |
62 | #endif |
63 | |
64 | |
65 | |
66 | namespace sc = boost::statechart; |
67 | |
68 | |
69 | |
70 | ////////////////////////////////////////////////////////////////////////////// |
71 | const unsigned int noOfEvents = 1000000; |
72 | |
73 | |
74 | ////////////////////////////////////////////////////////////////////////////// |
75 | char GetKey() |
76 | { |
77 | char key; |
78 | std::cin >> key; |
79 | return key; |
80 | } |
81 | |
82 | |
83 | ////////////////////////////////////////////////////////////////////////////// |
84 | int main() |
85 | { |
86 | std::cout << "Boost.Statechart PingPong example\n\n" ; |
87 | std::cout << "Threading configuration:\n" ; |
88 | #ifdef BOOST_HAS_THREADS |
89 | std::cout << "Multi-threaded build with " ; |
90 | #ifdef USE_TWO_THREADS |
91 | std::cout << 2; |
92 | #else |
93 | std::cout << 1; |
94 | #endif |
95 | std::cout << " thread(s).\n" ; |
96 | #else |
97 | std::cout << "Single-threaded build\n" ; |
98 | #endif |
99 | |
100 | std::cout << "\np<CR>: Performance test\n" ; |
101 | std::cout << "e<CR>: Exits the program\n\n" ; |
102 | |
103 | char key = GetKey(); |
104 | |
105 | while ( key != 'e' ) |
106 | { |
107 | switch( key ) |
108 | { |
109 | case 'p': |
110 | { |
111 | #ifdef BOOST_HAS_THREADS |
112 | MyScheduler scheduler1( true ); |
113 | #else |
114 | MyScheduler scheduler1; |
115 | #endif |
116 | |
117 | #ifdef USE_TWO_THREADS |
118 | #ifdef BOOST_HAS_THREADS |
119 | MyScheduler scheduler2( true ); |
120 | #else |
121 | MyScheduler & scheduler2 = scheduler1; |
122 | #endif |
123 | #else |
124 | MyScheduler & scheduler2 = scheduler1; |
125 | #endif |
126 | |
127 | MyScheduler::processor_handle player1 = |
128 | scheduler1.create_processor< Player >( arg1: noOfEvents / 2 ); |
129 | scheduler1.initiate_processor( processor: player1 ); |
130 | MyScheduler::processor_handle player2 = |
131 | scheduler2.create_processor< Player >( arg1: noOfEvents / 2 ); |
132 | scheduler2.initiate_processor( processor: player2 ); |
133 | |
134 | boost::intrusive_ptr< BallReturned > pInitialBall = new BallReturned(); |
135 | pInitialBall->returnToOpponent = boost::bind( |
136 | f: &MyScheduler::queue_event, a1: &scheduler1, a2: player1, a3: _1 ); |
137 | pInitialBall->abortGame = boost::bind( |
138 | f: &MyScheduler::queue_event, |
139 | a1: &scheduler1, a2: player1, a3: MakeIntrusive( pObject: new GameAborted() ) ); |
140 | |
141 | scheduler2.queue_event( processor: player2, pEvent: pInitialBall ); |
142 | |
143 | std::cout << "\nHaving players return the ball " << |
144 | noOfEvents << " times. Please wait...\n" ; |
145 | |
146 | const unsigned int prevCount = Player::TotalNoOfProcessedEvents(); |
147 | const std::clock_t startTime = std::clock(); |
148 | |
149 | #ifdef USE_TWO_THREADS |
150 | #ifdef BOOST_HAS_THREADS |
151 | boost::thread otherThread( |
152 | boost::bind( &MyScheduler::operator(), &scheduler2, 0 ) ); |
153 | scheduler1(); |
154 | otherThread.join(); |
155 | #else |
156 | scheduler1(); |
157 | #endif |
158 | #else |
159 | scheduler1(); |
160 | #endif |
161 | |
162 | const std::clock_t elapsedTime = std::clock() - startTime; |
163 | std::cout << "Time to send and dispatch one event and\n" << |
164 | "perform the resulting transition: " ; |
165 | std::cout << elapsedTime / static_cast< double >( CLOCKS_PER_SEC ) * |
166 | 1000000.0 / ( Player::TotalNoOfProcessedEvents() - prevCount ) |
167 | << " microseconds\n\n" ; |
168 | } |
169 | break; |
170 | |
171 | default: |
172 | { |
173 | std::cout << "Invalid key!\n" ; |
174 | } |
175 | } |
176 | |
177 | key = GetKey(); |
178 | } |
179 | |
180 | return 0; |
181 | } |
182 | |