1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
|
// This example is an implementation of the classical Dining Philosophers
// exercise using only libcaf's event-based actor implementation.
#include <chrono>
#include <iostream>
#include <map>
#include <sstream>
#include <thread>
#include <utility>
#include <vector>
#include "caf/all.hpp"
CAF_BEGIN_TYPE_ID_BLOCK(dining_philosophers, first_custom_type_id)
CAF_ADD_ATOM(dining_philosophers, custom, take_atom, "take")
CAF_ADD_ATOM(dining_philosophers, custom, taken_atom, "taken")
CAF_ADD_ATOM(dining_philosophers, custom, eat_atom, "eat")
CAF_ADD_ATOM(dining_philosophers, custom, think_atom, "think")
CAF_END_TYPE_ID_BLOCK(dining_philosophers)
using std::cerr;
using std::cout;
using std::endl;
using std::chrono::seconds;
using namespace caf;
using namespace custom;
namespace {
// atoms for chopstick and philosopher interfaces
// a chopstick
using chopstick = typed_actor<replies_to<take_atom>::with<taken_atom, bool>,
reacts_to<put_atom>>;
chopstick::behavior_type taken_chopstick(chopstick::pointer,
const strong_actor_ptr&);
// either taken by a philosopher or available
chopstick::behavior_type available_chopstick(chopstick::pointer self) {
return {
[=](take_atom) -> result<taken_atom, bool> {
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,
const strong_actor_ptr& user) {
return {
[](take_atom) -> result<taken_atom, bool> {
return {taken_atom_v, false};
},
[=](put_atom) {
if (self->current_sender() == user)
self->become(available_chopstick(self));
},
};
}
// Based on: http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
//
//
// +-------------+ {busy|taken}
// +-------->| thinking |<------------------+
// | +-------------+ |
// | | |
// | | {eat} |
// | | |
// | V |
// | +-------------+ {busy} +-------------+
// | | hungry |----------->| denied |
// | +-------------+ +-------------+
// | |
// | | {taken}
// | |
// | V
// | +-------------+
// | | granted |
// | +-------------+
// | | |
// | {busy} | | {taken}
// +-----------+ |
// | V
// | {think} +-------------+
// +---------| eating |
// +-------------+
class philosopher : public event_based_actor {
public:
philosopher(actor_config& cfg, std::string n, chopstick l, chopstick r)
: event_based_actor(cfg),
name_(std::move(n)),
left_(std::move(l)),
right_(std::move(r)) {
// we only accept one message per state and skip others in the meantime
set_default_handler(skip);
// a philosopher that receives {eat} stops thinking and becomes hungry
thinking_.assign([=](eat_atom) {
become(hungry_);
send(left_, take_atom_v);
send(right_, take_atom_v);
});
// wait for the first answer of a chopstick
hungry_.assign([=](taken_atom, bool result) {
if (result)
become(granted_);
else
become(denied_);
});
// philosopher was able to obtain the first chopstick
granted_.assign([=](taken_atom, bool result) {
if (result) {
aout(this) << name_ << " has picked up chopsticks with IDs "
<< left_->id() << " and " << right_->id()
<< " and starts to eat\n";
// eat some time
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_);
}
});
// philosopher was *not* able to obtain the first chopstick
denied_.assign([=](taken_atom, bool result) {
if (result)
send(current_sender() == left_ ? left_ : right_, put_atom_v);
send(this, eat_atom_v);
become(thinking_);
});
// philosopher obtained both chopstick and eats (for five seconds)
eating_.assign([=](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:
behavior make_behavior() override {
// start thinking
send(this, think_atom_v);
// philosophers start to think after receiving {think}
return {
[=](think_atom) {
aout(this) << name_ << " starts to think\n";
delayed_send(this, seconds(5), eat_atom_v);
become(thinking_);
},
};
}
private:
std::string name_; // the name of this philosopher
chopstick left_; // left chopstick
chopstick right_; // right chopstick
behavior thinking_; // initial behavior
behavior hungry_; // tries to take chopsticks
behavior granted_; // has one chopstick and waits for the second one
behavior denied_; // could not get first chopsticks
behavior eating_; // wait for some time, then go thinking again
};
} // namespace
void caf_main(actor_system& system) {
scoped_actor self{system};
// create five chopsticks
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();
}
aout(self) << endl;
// spawn five philosophers
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)
|