Skip to content

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions module2/Homework/CMakeLists.txt
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)

75 changes: 75 additions & 0 deletions module2/Homework/dining_table.cpp
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
p->setThread(t);
p->setThread(std::move(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);
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
}
47 changes: 47 additions & 0 deletions module2/Homework/dining_table.h
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_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

U mnie był błąd:

dining_table.h:19:9: error: private field 'id_' is not used
      [-Werror,-Wunused-private-field]
    int id_;

public:
mutable std::mutex mtx_;
Fork(int id) : id_(id) {}

};


class Table {
int seats_;
int wait_for;
std::shared_ptr<Table> self_;
Copy link
Contributor

Choose a reason for hiding this comment

The 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_
88 changes: 88 additions & 0 deletions module2/Homework/philosopher.cpp
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();
}
100 changes: 100 additions & 0 deletions module2/Homework/philosopher.h
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_;
Copy link
Contributor

Choose a reason for hiding this comment

The 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void setThread(std::thread &t) {
void setThread(std::thread &&t) {

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
Copy link
Contributor

Choose a reason for hiding this comment

The 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_;
Copy link
Contributor

Choose a reason for hiding this comment

The 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_