1 | /* |
2 | Copyright (c) 2005-2019 Intel Corporation |
3 | |
4 | Licensed under the Apache License, Version 2.0 (the "License"); |
5 | you may not use this file except in compliance with the License. |
6 | You may obtain a copy of the License at |
7 | |
8 | http://www.apache.org/licenses/LICENSE-2.0 |
9 | |
10 | Unless required by applicable law or agreed to in writing, software |
11 | distributed under the License is distributed on an "AS IS" BASIS, |
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | See the License for the specific language governing permissions and |
14 | limitations under the License. |
15 | */ |
16 | |
17 | #if _MSC_VER |
18 | // Suppress "decorated name length exceeded, name was truncated" warning |
19 | #pragma warning (disable: 4503) |
20 | #endif |
21 | |
22 | #include "tbb/flow_graph.h" |
23 | #include "tbb/task_scheduler_init.h" |
24 | #include "tbb/tick_count.h" |
25 | #include "tbb/tbb_thread.h" |
26 | #include "tbb/atomic.h" |
27 | #include "tbb/spin_mutex.h" |
28 | #include <iostream> |
29 | #include "../../common/utility/utility.h" |
30 | #include <cstdlib> |
31 | #include <cstdio> |
32 | |
33 | // Each philosopher is an object, and is invoked in the think() function_node, the |
34 | // eat() function_node and forward() multifunction_node. |
35 | // |
36 | // The graph is constructed, and each think() function_node is started with a continue_msg. |
37 | // |
38 | // The philosopher will think, then gather two chopsticks, eat, place the chopsticks back, |
39 | // and if they have not completed the required number of cycles, will start to think() again |
40 | // by sending a continue_msg to their corresponding think() function_node. |
41 | // |
42 | // The reserving join has as its inputs the left and right chopstick queues an a queue |
43 | // that stores the continue_msg emitted by the function_node after think()ing is done. |
44 | // When all three inputs are available, a tuple of the inputs will be forwarded to the |
45 | // eat() function_node. The output of the eat() function_node is sent to the forward() |
46 | // multifunction_node. |
47 | |
48 | const tbb::tick_count::interval_t think_time(1.0); |
49 | const tbb::tick_count::interval_t eat_time(1.0); |
50 | const int num_times = 10; |
51 | |
52 | tbb::tick_count t0; |
53 | bool verbose = false; |
54 | |
55 | const char *names[] = { "Archimedes" , "Bakunin" , "Confucius" , "Democritus" , "Euclid" |
56 | , "Favorinus" , "Geminus" , "Heraclitus" , "Ichthyas" , "Jason of Nysa" , |
57 | "Kant" , "Lavrov" , "Metrocles" , "Nausiphanes" , "Onatas" , "Phaedrus" , |
58 | "Quillot" , "Russell" , "Socrates" , "Thales" , "Udayana" , |
59 | "Vernadsky" , "Wittgenstein" , "Xenophilus" , "Yen Yuan" , "Zenodotus" |
60 | }; |
61 | const int NumPhilosophers = sizeof(names) / sizeof(char*); |
62 | |
63 | struct RunOptions { |
64 | utility::thread_number_range threads; |
65 | int number_of_philosophers; |
66 | bool silent; |
67 | RunOptions(utility::thread_number_range threads_, int number_of_philosophers_, bool silent_) : |
68 | threads(threads_), number_of_philosophers(number_of_philosophers_), silent(silent_) { } |
69 | }; |
70 | |
71 | RunOptions ParseCommandLine(int argc, char *argv[]) { |
72 | int auto_threads = tbb::task_scheduler_init::default_num_threads(); |
73 | utility::thread_number_range threads(tbb::task_scheduler_init::default_num_threads, auto_threads, auto_threads); |
74 | int nPhilosophers = 5; |
75 | bool verbose = false; |
76 | char charbuf[100]; |
77 | std::sprintf(charbuf, "%d" , NumPhilosophers); |
78 | std::string pCount = "how many philosophers, from 2-" ; |
79 | pCount += charbuf; |
80 | |
81 | utility::cli_argument_pack cli_pack; |
82 | cli_pack.positional_arg(threads, "n-of_threads" , utility::thread_number_range_desc) |
83 | .positional_arg(nPhilosophers, "n-of-philosophers" , pCount) |
84 | .arg(verbose,"verbose" ,"verbose output" ); |
85 | utility::parse_cli_arguments(argc, argv, cli_pack); |
86 | if(nPhilosophers < 2 || nPhilosophers > NumPhilosophers) { |
87 | std::cout << "Number of philosophers (" << nPhilosophers << ") out of range [2:" << NumPhilosophers << "]\n" ; |
88 | std::cout << cli_pack.usage_string(argv[0]) << std::flush; |
89 | std::exit(1); |
90 | } |
91 | return RunOptions(threads, nPhilosophers,!verbose); |
92 | } |
93 | |
94 | |
95 | tbb::spin_mutex my_mutex; |
96 | |
97 | class chopstick {}; |
98 | |
99 | using namespace tbb::flow; |
100 | |
101 | typedef tbb::flow::tuple<continue_msg, chopstick, chopstick> join_output; |
102 | typedef join_node< join_output, reserving > join_node_type; |
103 | |
104 | typedef function_node<continue_msg, continue_msg> think_node_type; |
105 | typedef function_node<join_output, continue_msg> eat_node_type; |
106 | typedef multifunction_node<continue_msg, join_output> forward_node_type; |
107 | |
108 | class philosopher { |
109 | public: |
110 | |
111 | philosopher( const char *name ) : |
112 | my_name(name), my_count(num_times) { } |
113 | |
114 | ~philosopher() { |
115 | } |
116 | |
117 | void check(); |
118 | const char *name() const { return my_name; } |
119 | |
120 | private: |
121 | |
122 | friend std::ostream& operator<<(std::ostream& o, philosopher const &p); |
123 | |
124 | const char *my_name; |
125 | int my_count; |
126 | |
127 | friend class think_node_body; |
128 | friend class eat_node_body; |
129 | friend class forward_node_body; |
130 | |
131 | void think( ); |
132 | void eat(); |
133 | void forward( const continue_msg &in, forward_node_type::output_ports_type &out_ports ); |
134 | }; |
135 | |
136 | std::ostream& operator<<(std::ostream& o, philosopher const &p) { |
137 | o << "< philosopher[" << reinterpret_cast<uintptr_t>(const_cast<philosopher *>(&p)) << "] " << p.name() |
138 | << ", my_count=" << p.my_count; |
139 | return o; |
140 | } |
141 | |
142 | class think_node_body { |
143 | philosopher& my_philosopher; |
144 | public: |
145 | think_node_body( philosopher &p ) : my_philosopher(p) { } |
146 | think_node_body( const think_node_body &other ) : my_philosopher(other.my_philosopher) { } |
147 | continue_msg operator()( continue_msg /*m*/) { |
148 | my_philosopher.think(); |
149 | return continue_msg(); |
150 | } |
151 | }; |
152 | |
153 | class eat_node_body { |
154 | philosopher &my_philosopher; |
155 | public: |
156 | eat_node_body( philosopher &p) : my_philosopher(p) {} |
157 | eat_node_body( const eat_node_body &other ) : my_philosopher(other.my_philosopher) { } |
158 | continue_msg operator()(const join_output &in) { |
159 | my_philosopher.eat(); |
160 | return continue_msg(); |
161 | } |
162 | }; |
163 | |
164 | class forward_node_body { |
165 | philosopher &my_philosopher; |
166 | public: |
167 | forward_node_body( philosopher &p) : my_philosopher(p) {} |
168 | forward_node_body( const forward_node_body &other ) : my_philosopher(other.my_philosopher) { } |
169 | void operator()( const continue_msg &in, forward_node_type::output_ports_type &out) { |
170 | my_philosopher.forward( in, out); |
171 | } |
172 | }; |
173 | |
174 | void philosopher::check() { |
175 | if ( my_count != 0 ) { |
176 | std::printf("ERROR: philosopher %s still had to run %d more times\n" , name(), my_count); |
177 | std::exit(1); |
178 | } |
179 | } |
180 | |
181 | void philosopher::forward( const continue_msg &/*in*/, forward_node_type::output_ports_type &out_ports ) { |
182 | if(my_count < 0) abort(); |
183 | --my_count; |
184 | (void)tbb::flow::get<1>(out_ports).try_put(chopstick()); |
185 | (void)tbb::flow::get<2>(out_ports).try_put(chopstick()); |
186 | if (my_count > 0) { |
187 | (void)tbb::flow::get<0>(out_ports).try_put(continue_msg()); //start thinking again |
188 | } else { |
189 | if(verbose) { |
190 | tbb::spin_mutex::scoped_lock lock(my_mutex); |
191 | std::printf("%s has left the building\n" , name()); |
192 | } |
193 | } |
194 | } |
195 | |
196 | void philosopher::eat() { |
197 | if(verbose) { |
198 | tbb::spin_mutex::scoped_lock lock(my_mutex); |
199 | std::printf("%s eating\n" , name()); |
200 | } |
201 | tbb::this_tbb_thread::sleep(eat_time); |
202 | if(verbose) { |
203 | tbb::spin_mutex::scoped_lock lock(my_mutex); |
204 | std::printf("%s done eating\n" , name()); |
205 | } |
206 | } |
207 | |
208 | void philosopher::think() { |
209 | if(verbose) { |
210 | tbb::spin_mutex::scoped_lock lock(my_mutex); |
211 | std::printf("%s thinking\n" , name()); |
212 | } |
213 | tbb::this_tbb_thread::sleep(think_time); |
214 | if(verbose) { |
215 | tbb::spin_mutex::scoped_lock lock(my_mutex); |
216 | std::printf("%s done thinking\n" , name()); |
217 | } |
218 | } |
219 | |
220 | typedef queue_node<continue_msg> thinking_done_type; |
221 | |
222 | int main(int argc, char *argv[]) { |
223 | try { |
224 | tbb::tick_count main_time = tbb::tick_count::now(); |
225 | int num_threads; |
226 | int num_philosophers; |
227 | |
228 | RunOptions options = ParseCommandLine(argc, argv); |
229 | num_philosophers = options.number_of_philosophers; |
230 | verbose = !options.silent; |
231 | |
232 | for(num_threads = options.threads.first; num_threads <= options.threads.last; num_threads = options.threads.step(num_threads)) { |
233 | |
234 | tbb::task_scheduler_init init(num_threads); |
235 | |
236 | graph g; |
237 | |
238 | if(verbose) std::cout << std::endl << num_philosophers << " philosophers with " |
239 | << num_threads << " threads" << std::endl << std::endl; |
240 | t0 = tbb::tick_count::now(); |
241 | |
242 | std::vector<queue_node<chopstick> > places(num_philosophers, queue_node<chopstick>(g)); |
243 | std::vector<philosopher> philosophers; |
244 | philosophers.reserve(num_philosophers); |
245 | std::vector<think_node_type *> think_nodes; |
246 | think_nodes.reserve(num_philosophers); |
247 | std::vector<thinking_done_type> done_vector(num_philosophers, thinking_done_type(g)); |
248 | std::vector<join_node_type> join_vector(num_philosophers,join_node_type(g)); |
249 | std::vector<eat_node_type *> eat_nodes; |
250 | eat_nodes.reserve(num_philosophers); |
251 | std::vector<forward_node_type *> forward_nodes; |
252 | forward_nodes.reserve(num_philosophers); |
253 | for ( int i = 0; i < num_philosophers; ++i ) { |
254 | places[i].try_put(chopstick()); |
255 | philosophers.push_back( philosopher( names[i] ) ); // allowed because of default generated assignment |
256 | if(verbose) { |
257 | tbb::spin_mutex::scoped_lock lock(my_mutex); |
258 | std::cout << "Built philosopher " << philosophers[i] << std::endl; |
259 | } |
260 | think_nodes.push_back(new think_node_type(g, unlimited, think_node_body(philosophers[i]))); |
261 | eat_nodes.push_back( new eat_node_type(g, unlimited, eat_node_body(philosophers[i]))); |
262 | forward_nodes.push_back( new forward_node_type(g, unlimited, forward_node_body(philosophers[i]))); |
263 | } |
264 | |
265 | // attach chopstick buffers and think function_nodes to joins |
266 | for(int i = 0; i < num_philosophers; ++i) { |
267 | make_edge( *think_nodes[i], done_vector[i] ); |
268 | make_edge( done_vector[i], input_port<0>(join_vector[i]) ); |
269 | make_edge( places[i], input_port<1>(join_vector[i]) ); // left chopstick |
270 | make_edge( places[(i+1) % num_philosophers], input_port<2>(join_vector[i]) ); // right chopstick |
271 | make_edge( join_vector[i], *eat_nodes[i] ); |
272 | make_edge( *eat_nodes[i], *forward_nodes[i] ); |
273 | make_edge( output_port<0>(*forward_nodes[i]), *think_nodes[i] ); |
274 | make_edge( output_port<1>(*forward_nodes[i]), places[i] ); |
275 | make_edge( output_port<2>(*forward_nodes[i]), places[(i+1) % num_philosophers] ); |
276 | } |
277 | |
278 | // start all the philosophers thinking |
279 | for(int i = 0; i < num_philosophers; ++i) think_nodes[i]->try_put(continue_msg()); |
280 | |
281 | g.wait_for_all(); |
282 | |
283 | tbb::tick_count t1 = tbb::tick_count::now(); |
284 | if(verbose) std::cout << std::endl << num_philosophers << " philosophers with " |
285 | << num_threads << " threads have taken " << (t1-t0).seconds() << "seconds" << std::endl; |
286 | |
287 | for ( int i = 0; i < num_philosophers; ++i ) philosophers[i].check(); |
288 | |
289 | for(int i = 0; i < num_philosophers; ++i) { |
290 | delete think_nodes[i]; |
291 | delete eat_nodes[i]; |
292 | delete forward_nodes[i]; |
293 | } |
294 | } |
295 | |
296 | utility::report_elapsed_time((tbb::tick_count::now() - main_time).seconds()); |
297 | return 0; |
298 | } catch(std::exception& e) { |
299 | std::cerr<<"error occurred. error text is :\"" <<e.what()<<"\"\n" ; |
300 | return 1; |
301 | } |
302 | } |
303 | |