Skip to content

Instantly share code, notes, and snippets.

@eao197
Created January 25, 2019 07:12
Show Gist options
  • Save eao197/4998a37f24442d2d11c7cbd6d3700695 to your computer and use it in GitHub Desktop.
Save eao197/4998a37f24442d2d11c7cbd6d3700695 to your computer and use it in GitHub Desktop.
Simple implementation of Dijkstra solution for Dining Philosophers problem. This implementation is built on top of std::threads and CSP-like channels from SObjectizer-5.5
#include <dining_philosophers/common/fork_messages.hpp>
#include <dining_philosophers/common/random_generator.hpp>
#include <dining_philosophers/csp_based/trace_maker/all.hpp>
#include <fmt/format.h>
#include <queue>
void fork_process(
so_5::mchain_t fork_ch )
{
// State of the fork.
bool taken = false;
// Queue of waiting philosophers.
std::queue< so_5::mbox_t > wait_queue;
// Receive and handle all messages until the channel will be closed.
so_5::receive( so_5::from( fork_ch ),
[&]( so_5::mhood_t<take_t> cmd ) {
if( taken )
wait_queue.push( cmd->m_who );
else
{
taken = true;
so_5::send< taken_t >( cmd->m_who );
}
},
[&]( so_5::mhood_t<put_t> ) {
if( wait_queue.empty() )
taken = false;
else
{
const auto who = wait_queue.front();
wait_queue.pop();
so_5::send< taken_t >( who );
}
} );
}
void philosopher_process(
trace_maker_t & tracer,
so_5::mchain_t control_ch,
std::size_t philosopher_index,
so_5::mchain_t left_fork_ch,
so_5::mchain_t right_fork_ch,
int meals_count )
{
int meals_eaten{ 0 };
thinking_type_t thinking_type{ thinking_type_t::normal };
random_pause_generator_t pause_generator;
auto self_ch = so_5::create_mchain( control_ch->environment() );
while( meals_eaten < meals_count )
{
tracer.thinking_started( philosopher_index, thinking_type );
// Simulate thinking by suspending the thread.
std::this_thread::sleep_for( pause_generator.think_pause( thinking_type ) );
// For the case if we can't take forks.
thinking_type = thinking_type_t::hungry;
// Try to get the left fork.
tracer.take_left_attempt( philosopher_index );
so_5::send< take_t >( left_fork_ch, self_ch->as_mbox(), philosopher_index );
// Request sent, wait for a reply.
so_5::receive( self_ch, so_5::infinite_wait,
[&]( so_5::mhood_t<taken_t> ) {
// Left fork is taken.
// Try to get the right fork.
tracer.take_right_attempt( philosopher_index );
so_5::send< take_t >(
right_fork_ch, self_ch->as_mbox(), philosopher_index );
// Request sent, wait for a reply.
so_5::receive( self_ch, so_5::infinite_wait,
[&]( so_5::mhood_t<taken_t> ) {
// Both fork are taken. We can eat.
tracer.eating_started( philosopher_index );
// Simulate eating by suspending the thread.
std::this_thread::sleep_for( pause_generator.eat_pause() );
// One step closer to the end.
++meals_eaten;
// Right fork should be returned after eating.
so_5::send< put_t >( right_fork_ch );
// Next thinking will be normal, not 'hungry_thinking'.
thinking_type = thinking_type_t::normal;
} );
// Left fork should be returned too.
so_5::send< put_t >( left_fork_ch );
} );
}
// Notify about the completion of the work.
tracer.philosopher_done( philosopher_index );
so_5::send< philosopher_done_t >( control_ch, philosopher_index );
}
void run_simulation(
so_5::environment_t & env,
const names_holder_t & names ) noexcept
{
const auto table_size = names.size();
const auto join_all = []( std::vector<std::thread> & threads ) {
for( auto & t : threads )
t.join();
};
trace_maker_t tracer{
env,
names,
random_pause_generator_t::trace_step() };
// Create forks.
std::vector< so_5::mchain_t > fork_chains;
std::vector< std::thread > fork_threads( table_size );
for( std::size_t i{}; i != table_size; ++i )
{
// Personal channel for fork.
fork_chains.emplace_back( so_5::create_mchain(env) );
// Run fork as a thread.
fork_threads[ i ] = std::thread{ fork_process, fork_chains.back() };
}
// Chain for acks from philosophers.
auto control_ch = so_5::create_mchain( env );
// Create philosophers.
std::vector< std::thread > philosopher_threads( table_size );
for( std::size_t i{}; i != table_size - 1u; ++i )
{
// Run philosopher as a thread.
philosopher_threads[ i ] = std::thread{
philosopher_process,
std::ref(tracer),
control_ch,
i,
fork_chains[ i ],
fork_chains[ i + 1u ],
5 };
}
// The last philosopher should take forks in opposite direction.
philosopher_threads[ table_size - 1u ] = std::thread{
philosopher_process,
std::ref(tracer),
control_ch,
table_size - 1u,
fork_chains[ table_size - 1u ],
fork_chains[ 0 ],
5 };
// Wait while all philosophers completed.
so_5::receive( so_5::from( control_ch ).handle_n( table_size ),
[&names]( so_5::mhood_t<philosopher_done_t> cmd ) {
fmt::print( "{}: done\n", names[ cmd->m_philosopher_index ] );
} );
// Wait for completion of philosopher threads.
join_all( philosopher_threads );
// Close channels for all forks.
for( auto & ch : fork_chains )
so_5::close_drop_content( ch );
// Wait for completion of fork threads.
join_all( fork_threads );
// Show the result.
tracer.done();
// Stop the SObjectizer.
env.stop();
}
int main()
{
try
{
const names_holder_t names{
"Socrates", "Plato", "Aristotle", "Descartes", "Spinoza", "Kant",
"Schopenhauer", "Nietzsche", "Wittgenstein", "Heidegger", "Sartre"
};
so_5::launch( [&]( so_5::environment_t & env ) {
run_simulation( env, names );
} );
}
catch( const std::exception & ex )
{
std::cerr << "Error: " << ex.what() << std::endl;
return 1;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment