From 114f2698d54589b31ccf701ca9f9dfb8f908a076 Mon Sep 17 00:00:00 2001 From: Lukasz Plewa Date: Fri, 8 Jan 2021 14:01:36 +0100 Subject: [PATCH] Homework - dining philosophers problem with visualization Modify number of philosophers in main() as parameter to Table object Modify number of dishes to be eaten by every philosopher in Philosopher class declaration. --- module2/Homework/CMakeLists.txt | 16 +++++ module2/Homework/dining_table.cpp | 75 ++++++++++++++++++++++ module2/Homework/dining_table.h | 47 ++++++++++++++ module2/Homework/philosopher.cpp | 88 ++++++++++++++++++++++++++ module2/Homework/philosopher.h | 100 ++++++++++++++++++++++++++++++ 5 files changed, 326 insertions(+) create mode 100644 module2/Homework/CMakeLists.txt create mode 100644 module2/Homework/dining_table.cpp create mode 100644 module2/Homework/dining_table.h create mode 100644 module2/Homework/philosopher.cpp create mode 100644 module2/Homework/philosopher.h diff --git a/module2/Homework/CMakeLists.txt b/module2/Homework/CMakeLists.txt new file mode 100644 index 0000000..1e7f20a --- /dev/null +++ b/module2/Homework/CMakeLists.txt @@ -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) + diff --git a/module2/Homework/dining_table.cpp b/module2/Homework/dining_table.cpp new file mode 100644 index 0000000..4896975 --- /dev/null +++ b/module2/Homework/dining_table.cpp @@ -0,0 +1,75 @@ +#include "philosopher.h" +#include "dining_table.h" + + +Table::Table(int seats) : seats_(seats) { +// self_ = std::shared_ptr(this); + wait_for = seats; + for (int n = 0; n < seats_; ++n) { + forks.emplace_back(std::make_shared(n)); + } + for (int n = 0; n < seats_; ++n) { + int r = n; + int l = (n+1) % seats_; + philosophers.emplace_back(std::make_shared(n, this, forks[r], forks[l])); + } +} + +void Table::updateCallback(int id) { + std::lock_guard 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 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 mlock(m_update_mtx); + m_cond_update.wait(mlock); + drawStatus(); + } while (wait_for); +} + + +int main() { + Table table(15); + table.Run(); + + return 0; +} diff --git a/module2/Homework/dining_table.h b/module2/Homework/dining_table.h new file mode 100644 index 0000000..b00dfa0 --- /dev/null +++ b/module2/Homework/dining_table.h @@ -0,0 +1,47 @@ +#ifndef _DINING_TABLE_H_ +#define _DINING_TABLE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "philosopher.h" + +class Fork { + int id_; +public: + mutable std::mutex mtx_; + Fork(int id) : id_(id) {} + +}; + + +class Table { + int seats_; + int wait_for; + std::shared_ptr
self_; + std::vector > forks; + std::vector > philosophers; + std::mutex m_update_mtx; + std::condition_variable m_cond_update; + + void drawHeader(); + void drawStatus(); + void joinPhilosopherToTheFeast(std::shared_ptr p); +public: + Table(int seats); + void updateCallback(int id); + void Run(); + int seats() { return seats_; } +}; + + +#endif // _DINING_TABLE_H_ \ No newline at end of file diff --git a/module2/Homework/philosopher.cpp b/module2/Homework/philosopher.cpp new file mode 100644 index 0000000..d473d50 --- /dev/null +++ b/module2/Homework/philosopher.cpp @@ -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 lock(mutex_); + if(pinstance_ == 0) + { + std::random_device rd; + pinstance_ = new EatTimeEngine(rd()); + } + return pinstance_; +} + +/* *********************************************************** + class Philosopher + *********************************************************** */ +std::string Philosopher::readStatus() { + std::lock_guard 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 lock(mtx_); + return status_; +} + +void Philosopher::startEating() +{ + std::lock_guard lock(mtx_); + status_ = PhilStatus::Eating; +} + +void Philosopher::stopEating() { + std::lock_guard lock(mtx_); + status_ = PhilStatus::Thinking; +} + +void Philosopher::finished() { + { + std::lock_guard 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(); +} diff --git a/module2/Homework/philosopher.h b/module2/Homework/philosopher.h new file mode 100644 index 0000000..d660f01 --- /dev/null +++ b/module2/Homework/philosopher.h @@ -0,0 +1,100 @@ +#ifndef _PHILOSOPHER_H_ +#define _PHILOSOPHER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class EatTimeEngine +{ +private: + std::mt19937 m_generator; + std::uniform_int_distribution m_distribution; + static EatTimeEngine *pinstance_; + 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 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_r, fork_l; + + void startEating(); + void stopEating(); + void finished(); +public: + Philosopher(int id, Table* t, std::shared_ptr &right, std::shared_ptr &left) : + id_(id), + status_(PhilStatus::Thinking), + p_table(t), + fork_r(right), + fork_l(left) { + lottery = EatTimeEngine::GetInstance(); + } + + ~Philosopher() { + std::lock_guard 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) { + m_t = std::move(t); + } + + void endThread() { + std::lock_guard lock(mtx_); + if (m_t.joinable()) { + m_t.join(); + } + } + + int getId() { + std::lock_guard lock(mtx_); + return id_; + } +}; + +#endif //_DINING_TABLE_H_