Advertisement
eao197

Реализация dining_philosophers с использованием арбитра.

Oct 6th, 2014
322
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.89 KB | None | 0 0
  1. /*
  2.  * A simple implementation of arbiter-based solution dining philosophers
  3.  * problem. See description of this problem in Wikipedia:
  4.  * http://en.wikipedia.org/wiki/Dining_philosophers_problem
  5.  */
  6.  
  7. #include <iostream>
  8. #include <iterator>
  9. #include <numeric>
  10. #include <cstdlib>
  11. #include <vector>
  12. #include <mutex>
  13. #include <tuple>
  14.  
  15. #include <so_5/all.hpp>
  16.  
  17. // This request will be sent by the hungry agent.
  18. struct msg_start_eating_request : public so_5::rt::message_t
  19. {
  20.     // Agent identifier.
  21.     std::size_t m_philosopher;
  22.  
  23.     msg_start_eating_request( std::size_t philosopher )
  24.         :   m_philosopher( philosopher )
  25.     {}
  26. };
  27.  
  28. // This signal will be sent to the hungry agent to whom
  29. // eating is allowed.
  30. struct msg_start_eating : public so_5::rt::signal_t {};
  31.  
  32. // This is notification about end of eating session.
  33. struct msg_eating_finished : public so_5::rt::message_t
  34. {
  35.     // Agent identifier.
  36.     std::size_t m_philosopher;
  37.  
  38.     msg_eating_finished( std::size_t philosopher )
  39.         :   m_philosopher( philosopher )
  40.     {}
  41. };
  42.  
  43. // The state of a fork.
  44. struct fork_state_t
  45. {
  46.     // Indication that the fork is in use.
  47.     // It is true if some agent is holding it and waiting
  48.     // for the right fork.
  49.     // Or if agent is eating (e.g. agent holds both forks).
  50.     bool m_in_use = false;
  51.  
  52.     // Indication that someone is waiting on that fork.
  53.     // It could be agent which waits for his left fork
  54.     // (but in that case agent is waiting only for the left fork).
  55.     // Or it could be agent which waits for his right fork
  56.     // (in that case agent is already holding his left fork).
  57.     //
  58.     // Value false means that there is no any waiting agents.
  59.     bool m_someone_is_waiting = false;
  60. };
  61.  
  62. // An arbiter who allows philosophers to eat.
  63. //
  64. // It also finishes the sample after test_duration seconds.
  65. class a_arbiter_t : public so_5::rt::agent_t
  66. {
  67.     struct msg_shutdown : public so_5::rt::signal_t {};
  68.  
  69. public :
  70.     a_arbiter_t(
  71.         so_5::rt::environment_t & env,
  72.         std::size_t philosophers_count,
  73.         std::chrono::seconds test_duration )
  74.         :   so_5::rt::agent_t( env )
  75.         ,   m_philosophers_count( philosophers_count )
  76.         ,   m_test_duration( test_duration )
  77.         ,   m_forks( philosophers_count, fork_state_t() )
  78.     {
  79.         m_philosophers.reserve( philosophers_count );
  80.     }
  81.  
  82.     // This method must be subsequently called during
  83.     // the creation of the philosophers.
  84.     void
  85.     add_philosopher(
  86.         const so_5::rt::mbox_ref_t & mbox )
  87.     {
  88.         m_philosophers.emplace_back( mbox );
  89.     }
  90.  
  91.     virtual void
  92.     so_define_agent() override
  93.     {
  94.         so_subscribe( so_direct_mbox() )
  95.             .event( so_5::signal< msg_shutdown >,
  96.                     [this]() { so_environment().stop(); } );
  97.  
  98.         so_subscribe( so_direct_mbox() )
  99.             .event( &a_arbiter_t::evt_start_eating_request );
  100.  
  101.         so_subscribe( so_direct_mbox() )
  102.             .event( &a_arbiter_t::evt_eating_finished );
  103.     }
  104.  
  105.     virtual void
  106.     so_evt_start()
  107.     {
  108.         so_environment().single_timer< msg_shutdown >(
  109.                 so_direct_mbox(),
  110.                 m_test_duration );
  111.     }
  112.  
  113.     // Some philosopher is hungry and wants to eat.
  114.     // This request is fulfilled or philosopher will wait for
  115.     // one of his forks.
  116.     void
  117.     evt_start_eating_request( const msg_start_eating_request & evt )
  118.     {
  119.         try_allow_philosopher_to_eat( evt.m_philosopher );
  120.     }
  121.  
  122.     // Some philosopher completed eating.
  123.     // The forks of this philosopher will be marked as free and
  124.     // if there is someone who is waiting for them it will be
  125.     // processed.
  126.     void
  127.     evt_eating_finished( const msg_eating_finished & evt )
  128.     {
  129.         // Free left fork and check if it is necessary
  130.         // for the left neighbor as right fork...
  131.         auto & left_fork = m_forks[ evt.m_philosopher ];
  132.         left_fork.m_in_use = false;
  133.  
  134.         if( left_fork.m_someone_is_waiting )
  135.         {
  136.             // Left neighbor is waiting this fork as his right fork.
  137.             left_fork.m_someone_is_waiting = false;
  138.             left_fork.m_in_use = true;
  139.  
  140.             enable_eating_for_philosopher( left_neighbor( evt.m_philosopher ) );
  141.         }
  142.  
  143.         // Free right fork and check if it is necessary
  144.         // for the right neighbor as left fork...
  145.         const auto right_fork_index = right_neighbor( evt.m_philosopher );
  146.         auto & right_fork = m_forks[ right_fork_index ];
  147.         right_fork.m_in_use = false;
  148.  
  149.         if( right_fork.m_someone_is_waiting )
  150.         {
  151.             // Right neighbor is waiting this fork as his left fork.
  152.             right_fork.m_someone_is_waiting = false;
  153.             try_allow_philosopher_to_eat( right_fork_index );
  154.         }
  155.     }
  156.  
  157. private :
  158.     // Count of the philosophers in the test.
  159.     const std::size_t m_philosophers_count;
  160.  
  161.     // Duration of the sample.
  162.     const std::chrono::seconds m_test_duration;
  163.  
  164.     // States of the forks.
  165.     std::vector< fork_state_t > m_forks;
  166.  
  167.     // Mboxes for the philosophers.
  168.     // They are necessary to send msg_start_eating signals
  169.     // for a philosopher when his right fork is taken to him.
  170.     std::vector< so_5::rt::mbox_ref_t > m_philosophers;
  171.  
  172.     void
  173.     try_allow_philosopher_to_eat( std::size_t philosopher )
  174.     {
  175.         // Left fork must be free to start the process.
  176.         auto & left_fork = m_forks[ philosopher ];
  177.         if( left_fork.m_in_use )
  178.         {
  179.             // Just mark that there is a waiting philosopher for this fork.
  180.             // No more can be done now.
  181.             left_fork.m_someone_is_waiting = true;
  182.         }
  183.         else
  184.         {
  185.             // This philosopher acquired his left fork.
  186.             left_fork.m_in_use = true;
  187.  
  188.             // Checking availability of this right fork.
  189.             const auto right_fork_index = right_neighbor( philosopher );
  190.             auto & right_fork = m_forks[ right_fork_index ];
  191.             if( right_fork.m_in_use )
  192.             {
  193.                 // Just mark that there is a waiting philosopher for this fork.
  194.                 // No more can be done now.
  195.                 right_fork.m_someone_is_waiting = true;
  196.             }
  197.             else
  198.             {
  199.                 // This philosopher acquired his right fork.
  200.                 right_fork.m_in_use = true;
  201.  
  202.                 // Philosopher can start eating.
  203.                 enable_eating_for_philosopher( philosopher );
  204.             }
  205.         }
  206.     }
  207.  
  208.     std::size_t
  209.     left_neighbor( std::size_t index ) const
  210.     {
  211.         if( index ) return index - 1;
  212.         else return m_philosophers_count - 1;
  213.     }
  214.  
  215.     std::size_t
  216.     right_neighbor( std::size_t index ) const
  217.     {
  218.         return (index + 1) % m_philosophers_count;
  219.     }
  220.  
  221.     void
  222.     enable_eating_for_philosopher( std::size_t philosopher )
  223.     {
  224.         m_philosophers[ philosopher ]->deliver_signal< msg_start_eating >();
  225.     }
  226. };
  227.  
  228. // A philosopher agent.
  229. // Does the infinite loop of think()/eat() methods.
  230. //
  231. // The switch from thinking to eating is done automatically
  232. // when think() method finishes. As opposite the switch from
  233. // eating to thinking is done automatically after return
  234. // from eat() method.
  235. class a_philosopher_t : public so_5::rt::agent_t
  236. {
  237.     struct msg_start_thinking : public so_5::rt::signal_t {};
  238.  
  239. public :
  240.     a_philosopher_t(
  241.         so_5::rt::environment_t & env,
  242.         std::size_t index,
  243.         const so_5::rt::mbox_ref_t & arbiter_mbox )
  244.         :   so_5::rt::agent_t( env )
  245.         ,   m_index( index )
  246.         ,   m_arbiter_mbox( arbiter_mbox )
  247.     {}
  248.  
  249.     virtual void
  250.     so_define_agent()
  251.     {
  252.         so_subscribe( so_direct_mbox() )
  253.             .event( so_5::signal< msg_start_thinking >,
  254.                     &a_philosopher_t::evt_start_thinking );
  255.  
  256.         so_subscribe( so_direct_mbox() )
  257.             .event( so_5::signal< msg_start_eating >,
  258.                     &a_philosopher_t::evt_start_eating );
  259.     }
  260.  
  261.     virtual void
  262.     so_evt_start()
  263.     {
  264.         initiate_thinking();
  265.     }
  266.  
  267.     void
  268.     evt_start_thinking()
  269.     {
  270.         think();
  271.  
  272.         m_arbiter_mbox->deliver_message(
  273.                 new msg_start_eating_request( m_index ) );
  274.     }
  275.  
  276.     void
  277.     evt_start_eating()
  278.     {
  279.         eat();
  280.  
  281.         m_arbiter_mbox->deliver_message(
  282.                 new msg_eating_finished( m_index ) );
  283.  
  284.         initiate_thinking();
  285.     }
  286.  
  287. protected :
  288.     virtual void
  289.     think()
  290.     {
  291.         const auto p = std::chrono::milliseconds( std::rand() % 250 );
  292.  
  293.         std::this_thread::sleep_for( p );
  294.     }
  295.  
  296.     virtual void
  297.     eat()
  298.     {
  299.         const auto p = std::chrono::milliseconds( std::rand() % 250 );
  300.  
  301.         std::this_thread::sleep_for( p );
  302.     }
  303.  
  304. private :
  305.     // Agent identifier.
  306.     const std::size_t m_index;
  307.  
  308.     // Arbiter mbox. Necessary for sending requests and notifications.
  309.     const so_5::rt::mbox_ref_t m_arbiter_mbox;
  310.  
  311.     void
  312.     initiate_thinking()
  313.     {
  314.         so_direct_mbox()->deliver_signal< msg_start_thinking >();
  315.     }
  316. };
  317.  
  318. void
  319. init( so_5::rt::environment_t & env,
  320.     const std::size_t philosophers_count,
  321.     const std::chrono::seconds test_duration )
  322. {
  323.     auto coop = env.create_coop( "dining_philosophers_with_arbiter",
  324.             // All philosophers will be active objects.
  325.             so_5::disp::active_obj::create_disp_binder( "active_obj" ) );
  326.  
  327.     auto arbiter = coop->add_agent(
  328.             new a_arbiter_t( env, philosophers_count, test_duration ),
  329.             // But the arbiter will work on different context.
  330.             so_5::rt::create_default_disp_binder() );
  331.  
  332.     for( std::size_t i = 0; i != philosophers_count; ++i )
  333.     {
  334.         auto p = coop->add_agent(
  335.                 new a_philosopher_t(
  336.                     env,
  337.                     i,
  338.                     arbiter->so_direct_mbox() ) );
  339.  
  340.         arbiter->add_philosopher( p->so_direct_mbox() );
  341.     }
  342.  
  343.     env.register_coop( std::move( coop ) );
  344. }
  345.  
  346. std::tuple< std::size_t, std::chrono::seconds >
  347. process_command_line_args( int argc, char ** argv )
  348. {
  349.     std::size_t philosophers = 5;
  350.     unsigned int test_duration = 20;
  351.  
  352.     if( argc > 1 )
  353.     {
  354.         philosophers = std::atoi( argv[ 1 ] );
  355.         if( philosophers < 2 || philosophers > 10000 )
  356.             throw std::invalid_argument(
  357.                     "philosophers count must be in [2..10000]" );
  358.     }
  359.     if( argc > 2 )
  360.     {
  361.         test_duration = std::atoi( argv[ 2 ] );
  362.         if( !test_duration || test_duration > 3600 )
  363.             throw std::invalid_argument(
  364.                     "philosophers count must be in [1..3600] seconds" );
  365.     }
  366.  
  367.     return std::make_tuple( philosophers, std::chrono::seconds( test_duration ) );
  368. }
  369.  
  370. int
  371. main( int argc, char ** argv )
  372. {
  373.     try
  374.     {
  375.         auto params = process_command_line_args( argc, argv );
  376.  
  377.         so_5::launch(
  378.                 [params]( so_5::rt::environment_t & env )
  379.                 {
  380.                     init( env, std::get<0>(params), std::get<1>(params) );
  381.                 },
  382.                 []( so_5::rt::environment_params_t & p ) {
  383.                     p.add_named_dispatcher( "active_obj",
  384.                             so_5::disp::active_obj::create_disp() );
  385.                 } );
  386.     }
  387.     catch( const std::exception & ex )
  388.     {
  389.         std::cerr << "Error: " << ex.what() << std::endl;
  390.         return 1;
  391.     }
  392.  
  393.     return 0;
  394. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement