-
Notifications
You must be signed in to change notification settings - Fork 59
Homework - dining philosophers problem with visualization #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
cmake_minimum_required(VERSION 3.0) | ||
|
||
set(CMAKE_CXX_STANDARD 17) | ||
set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||
set(CMAKE_CXX_COMPILER g++) | ||
|
||
add_definitions(-Wno-unused-variable) | ||
add_definitions(-Wall -Werror -pedantic -Wextra -fsanitize=thread) | ||
#add_link_options(-fsanitize=thread) | ||
|
||
file(GLOB SOURCES *.cpp) | ||
|
||
set(BINNAME dining_philosophers) | ||
add_executable(${BINNAME} ${SOURCES}) | ||
target_link_libraries(${BINNAME} pthread -fsanitize=thread) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
#include "philosopher.h" | ||
#include "dining_table.h" | ||
|
||
|
||
Table::Table(int seats) : seats_(seats) { | ||
// self_ = std::shared_ptr<Table>(this); | ||
wait_for = seats; | ||
for (int n = 0; n < seats_; ++n) { | ||
forks.emplace_back(std::make_shared<Fork>(n)); | ||
} | ||
for (int n = 0; n < seats_; ++n) { | ||
int r = n; | ||
int l = (n+1) % seats_; | ||
philosophers.emplace_back(std::make_shared<Philosopher>(n, this, forks[r], forks[l])); | ||
} | ||
} | ||
|
||
void Table::updateCallback(int id) { | ||
std::lock_guard<std::mutex> lock(m_update_mtx); | ||
|
||
if (philosophers[id]->getStatus() == PhilStatus::Done) { | ||
wait_for--; | ||
// std::cout << "Philosopher " << id << " reported as done. wait_for = " << wait_for << std::endl; | ||
|
||
} | ||
|
||
// std::cout << "Status update from phil id=" << id << std::endl; | ||
m_cond_update.notify_one(); | ||
|
||
} | ||
|
||
void Table::drawHeader() { | ||
std::stringstream ss; | ||
for (auto p: philosophers) { | ||
ss << "| Phil " << p->getId() << " "; | ||
} | ||
ss << std::endl << std::string(philosophers.size() * 12, '-') << std::endl; | ||
std::cout << ss.rdbuf(); | ||
} | ||
|
||
void Table::drawStatus() { | ||
std::stringstream ss; | ||
for (auto p: philosophers) { | ||
ss << p->readStatus(); | ||
} | ||
ss << std::endl; | ||
std::cout << ss.rdbuf(); | ||
} | ||
|
||
void Table::joinPhilosopherToTheFeast(std::shared_ptr<Philosopher> p) { | ||
std::thread t = std::thread(&Philosopher::threadStartTheFeast, &(*p)); | ||
p->setThread(t); | ||
std::cout << "Started " << p->getId() << std::endl; | ||
} | ||
|
||
void Table::Run() { | ||
for (auto p: philosophers) { | ||
joinPhilosopherToTheFeast(p); | ||
} | ||
std::cout << "Start monitoring Eating Philosophers" << std::endl; | ||
drawHeader(); | ||
do { | ||
std::unique_lock<std::mutex> mlock(m_update_mtx); | ||
m_cond_update.wait(mlock); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dobry pomysł z synchronizacją wyświetlania za pomocą condition_variable :) |
||
drawStatus(); | ||
} while (wait_for); | ||
} | ||
|
||
|
||
int main() { | ||
Table table(15); | ||
table.Run(); | ||
|
||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
#ifndef _DINING_TABLE_H_ | ||
#define _DINING_TABLE_H_ | ||
|
||
#include <iostream> | ||
#include <mutex> | ||
#include <shared_mutex> | ||
#include <condition_variable> | ||
#include <algorithm> | ||
#include <random> | ||
#include <memory> | ||
#include <thread> | ||
#include <chrono> | ||
#include <sstream> | ||
#include <functional> | ||
|
||
#include "philosopher.h" | ||
|
||
class Fork { | ||
int id_; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. U mnie był błąd:
|
||
public: | ||
mutable std::mutex mtx_; | ||
Fork(int id) : id_(id) {} | ||
|
||
}; | ||
|
||
|
||
class Table { | ||
int seats_; | ||
int wait_for; | ||
std::shared_ptr<Table> self_; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wygląda na niepotrzebne |
||
std::vector<std::shared_ptr<Fork> > forks; | ||
std::vector<std::shared_ptr<Philosopher> > philosophers; | ||
std::mutex m_update_mtx; | ||
std::condition_variable m_cond_update; | ||
|
||
void drawHeader(); | ||
void drawStatus(); | ||
void joinPhilosopherToTheFeast(std::shared_ptr<Philosopher> p); | ||
public: | ||
Table(int seats); | ||
void updateCallback(int id); | ||
void Run(); | ||
int seats() { return seats_; } | ||
}; | ||
|
||
|
||
#endif // _DINING_TABLE_H_ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#include "philosopher.h" | ||
#include "dining_table.h" | ||
|
||
using namespace std::chrono_literals; | ||
|
||
EatTimeEngine* EatTimeEngine::pinstance_{nullptr}; | ||
std::mutex EatTimeEngine::mutex_; | ||
|
||
EatTimeEngine* EatTimeEngine::GetInstance() | ||
{ | ||
std::lock_guard<std::mutex> lock(mutex_); | ||
if(pinstance_ == 0) | ||
{ | ||
std::random_device rd; | ||
pinstance_ = new EatTimeEngine(rd()); | ||
} | ||
return pinstance_; | ||
} | ||
|
||
/* *********************************************************** | ||
class Philosopher | ||
*********************************************************** */ | ||
std::string Philosopher::readStatus() { | ||
std::lock_guard<std::mutex> lock(mtx_); | ||
std::string s; | ||
switch (status_) | ||
{ | ||
case PhilStatus::Done: | ||
s = "| ****** "; | ||
break; | ||
case PhilStatus::Eating: | ||
s = "| Eating "; | ||
break; | ||
default: | ||
s = "| "; | ||
break; | ||
} | ||
return s; | ||
} | ||
|
||
PhilStatus Philosopher::getStatus() { | ||
std::lock_guard<std::mutex> lock(mtx_); | ||
return status_; | ||
} | ||
|
||
void Philosopher::startEating() | ||
{ | ||
std::lock_guard<std::mutex> lock(mtx_); | ||
status_ = PhilStatus::Eating; | ||
} | ||
|
||
void Philosopher::stopEating() { | ||
std::lock_guard<std::mutex> lock(mtx_); | ||
status_ = PhilStatus::Thinking; | ||
} | ||
|
||
void Philosopher::finished() { | ||
{ | ||
std::lock_guard<std::mutex> lock(mtx_); | ||
status_ = PhilStatus::Done; | ||
} | ||
// std::stringstream ss; | ||
// ss << "Philosopher " << id_ << " finished " << quota << " dishes." << std::endl; | ||
// std::cout << ss.rdbuf(); | ||
p_table->updateCallback(id_); | ||
} | ||
|
||
void Philosopher::threadStartTheFeast() { | ||
int counter = this->quota; | ||
std::stringstream ss; | ||
|
||
while(counter--) { | ||
int t; | ||
|
||
{ | ||
std::scoped_lock both_forks(fork_r->mtx_, fork_l->mtx_); | ||
startEating(); | ||
t = lottery->pickTime(); | ||
p_table->updateCallback(id_); | ||
std::this_thread::sleep_for(t * 10ms); | ||
} | ||
|
||
stopEating(); // Eating finished | ||
p_table->updateCallback(id_); | ||
|
||
} | ||
finished(); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,100 @@ | ||||||
#ifndef _PHILOSOPHER_H_ | ||||||
#define _PHILOSOPHER_H_ | ||||||
|
||||||
#include <iostream> | ||||||
#include <mutex> | ||||||
#include <shared_mutex> | ||||||
#include <condition_variable> | ||||||
#include <algorithm> | ||||||
#include <random> | ||||||
#include <memory> | ||||||
#include <thread> | ||||||
#include <chrono> | ||||||
#include <sstream> | ||||||
#include <functional> | ||||||
|
||||||
|
||||||
class EatTimeEngine | ||||||
{ | ||||||
private: | ||||||
std::mt19937 m_generator; | ||||||
std::uniform_int_distribution<int> m_distribution; | ||||||
static EatTimeEngine *pinstance_; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. polecam implementować singletony Meyersa - https://stackoverflow.com/questions/17712001/how-is-meyers-implementation-of-a-singleton-actually-a-singleton Najprostsze i bezpieczne bez dodatkowych mechanizmów. |
||||||
static std::mutex mutex_; | ||||||
protected: | ||||||
EatTimeEngine(std::random_device::result_type seed) : m_generator(seed), m_distribution(1, 100) {} | ||||||
~EatTimeEngine() {} | ||||||
public: | ||||||
int pickTime() { | ||||||
std::lock_guard<std::mutex> lock(mutex_); | ||||||
return m_distribution(m_generator); | ||||||
} | ||||||
static EatTimeEngine* GetInstance(); | ||||||
EatTimeEngine(EatTimeEngine &other) = delete; // Singletons should not be cloneable. | ||||||
void operator=(const EatTimeEngine &) = delete; // Singletons should not be assignable. | ||||||
}; | ||||||
|
||||||
/* ********************************************************************** */ | ||||||
enum class PhilStatus { | ||||||
Eating, Thinking, Done | ||||||
}; | ||||||
|
||||||
class Table; | ||||||
class Fork; | ||||||
|
||||||
class Philosopher { | ||||||
private: | ||||||
int id_; | ||||||
const int quota = 6; // How many dishes every philosopher must eat | ||||||
EatTimeEngine* lottery; | ||||||
std::thread m_t; | ||||||
mutable std::mutex mtx_; | ||||||
PhilStatus status_; | ||||||
Table* p_table; | ||||||
std::shared_ptr<Fork> fork_r, fork_l; | ||||||
|
||||||
void startEating(); | ||||||
void stopEating(); | ||||||
void finished(); | ||||||
public: | ||||||
Philosopher(int id, Table* t, std::shared_ptr<Fork> &right, std::shared_ptr<Fork> &left) : | ||||||
id_(id), | ||||||
status_(PhilStatus::Thinking), | ||||||
p_table(t), | ||||||
fork_r(right), | ||||||
fork_l(left) { | ||||||
lottery = EatTimeEngine::GetInstance(); | ||||||
} | ||||||
|
||||||
~Philosopher() { | ||||||
std::lock_guard<std::mutex> lock(mtx_); | ||||||
if (m_t.joinable()) { | ||||||
m_t.join(); | ||||||
} | ||||||
} | ||||||
|
||||||
Philosopher(const Philosopher&) = delete; //Delete the copy constructor | ||||||
Philosopher& operator=(const Philosopher&) = delete; //Delete the Assignment opeartor | ||||||
|
||||||
std::string readStatus(); | ||||||
PhilStatus getStatus(); | ||||||
void threadStartTheFeast(); | ||||||
|
||||||
void setThread(std::thread &t) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
m_t = std::move(t); | ||||||
} | ||||||
|
||||||
void endThread() { | ||||||
std::lock_guard<std::mutex> lock(mtx_); | ||||||
if (m_t.joinable()) { | ||||||
m_t.join(); | ||||||
} | ||||||
} | ||||||
Comment on lines
+87
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nieużywane? |
||||||
|
||||||
int getId() { | ||||||
std::lock_guard<std::mutex> lock(mtx_); | ||||||
return id_; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gdyby id_ było const to do odczytania danych nie trzeba by było blokować mutexa |
||||||
} | ||||||
}; | ||||||
|
||||||
#endif //_DINING_TABLE_H_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.