An event-based "Dining Philosophers" implementation.
An event-based "Dining Philosophers" implementation.
#include "caf/all.hpp"
#include <chrono>
#include <iostream>
#include <map>
#include <sstream>
#include <thread>
#include <utility>
#include <vector>
CAF_BEGIN_TYPE_ID_BLOCK(dining_philosophers, first_custom_type_id)
CAF_ADD_ATOM(dining_philosophers, take_atom)
CAF_ADD_ATOM(dining_philosophers, taken_atom)
CAF_ADD_ATOM(dining_philosophers, eat_atom)
CAF_ADD_ATOM(dining_philosophers, think_atom)
CAF_END_TYPE_ID_BLOCK(dining_philosophers)
using std::cerr;
using std::cout;
using std::endl;
using std::chrono::seconds;
namespace {
using chopstick
chopstick::behavior_type taken_chopstick(chopstick::pointer,
chopstick::behavior_type available_chopstick(chopstick::pointer self) {
return {
self->become(taken_chopstick(self, self->current_sender()));
return {taken_atom_v, true};
},
[](put_atom) { cerr << "chopstick received unexpected 'put'" << endl; },
};
}
chopstick::behavior_type taken_chopstick(chopstick::pointer self,
return {
return {taken_atom_v, false};
},
[=](put_atom) {
if (self->current_sender() == user)
self->become(available_chopstick(self));
},
};
}
public:
philosopher(
actor_config& cfg, std::string n, chopstick l, chopstick r)
name_(std::move(n)),
left_(std::move(l)),
right_(std::move(r)) {
set_default_handler(skip);
thinking_.assign([this](eat_atom) {
become(hungry_);
send(left_, take_atom_v);
send(right_, take_atom_v);
});
hungry_.assign([
this](taken_atom,
bool result) {
become(granted_);
else
become(denied_);
});
granted_.assign([
this](taken_atom,
bool result) {
aout(this) << name_ << " has picked up chopsticks with IDs "
<< left_->id() << " and " << right_->id()
<< " and starts to eat\n";
delayed_send(this, seconds(5), think_atom_v);
become(eating_);
} else {
send(current_sender() == left_ ? right_ : left_, put_atom_v);
send(this, eat_atom_v);
become(thinking_);
}
});
denied_.assign([
this](taken_atom,
bool result) {
send(current_sender() == left_ ? left_ : right_, put_atom_v);
send(this, eat_atom_v);
become(thinking_);
});
eating_.assign([this](think_atom) {
send(left_, put_atom_v);
send(right_, put_atom_v);
delayed_send(this, seconds(5), eat_atom_v);
aout(this) << name_ << " puts down his chopsticks and starts to think\n";
become(thinking_);
});
}
const char* name() const override {
return name_.c_str();
}
protected:
send(this, think_atom_v);
return {
[this](think_atom) {
aout(this) << name_ << " starts to think\n";
delayed_send(this, seconds(5), eat_atom_v);
become(thinking_);
},
};
}
private:
std::string name_;
chopstick left_;
chopstick right_;
};
}
aout(self) <<
"chopstick ids are:";
std::vector<chopstick> chopsticks;
for (size_t i = 0; i < 5; ++i) {
chopsticks.push_back(self->spawn(available_chopstick));
aout(self) <<
" " << chopsticks.back()->id();
}
std::vector<std::string> names{"Plato", "Hume", "Kant", "Nietzsche",
"Descartes"};
for (size_t i = 0; i < 5; ++i)
self->spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]);
}
CAF_MAIN(id_block::dining_philosophers)
Stores spawn-time flags and groups.
Definition actor_config.hpp:19
Actor environment including scheduler, registry, and optional components such as a middleman.
Definition actor_system.hpp:90
Describes the behavior of an actor, i.e., provides a message handler and an optional timeout.
Definition behavior.hpp:25
A cooperatively scheduled, event-based actor implementation.
Definition event_based_actor.hpp:40
Wraps the result of a message handler to represent either a value (wrapped into a message),...
Definition fwd.hpp:61
A scoped handle to a blocking actor.
Definition scoped_actor.hpp:18
Identifies a statically typed actor.
Definition typed_actor.hpp:32
Root namespace of libcaf.
Definition abstract_actor.cpp:25
actor_ostream aout(local_actor *self)
Convenience factory function for creating an actor output stream.
Definition actor_ostream.cpp:47