| 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 |  |