#include <memory>
#include <vector>
#include <thread>
#include <mutex>
#include <iostream>

using namespace std;

//--------ZAD2--------------------------------------------------------------------------
// brak skalowalności w aplikacji współbieżnej
// W systemie istnieje jeden obiekt typu Queue, do którego wstawia dane jeden wątek (kodu nie zamieszczono). Ilość danych w kolejce
// jest duża i rośnie. Aby temu zapobiec system przeniesiono na platformę wieloprocesorową  i zwiększono ilość wątków przetwarzających
// dane (wykonują one funcję calc_thrd). Niestety nie obserwuje się poprawy. Popraw kod funkcji calc_thrd.

using Data = int;
//class Data {}; //nie zamieszczono skladowych
using PData = std::shared_ptr<Data>;

class Vector {
public:
    bool empty_safe() {
        lock_guard lock(m_);
        return empty_impl();
    }
    bool empty_impl() {
        return v_.empty();
    }
    PData read_safe() {
        lock_guard lock(m_);
        return read_impl();
    };
    PData read_impl() {
        PData out(nullptr);
        if(! empty_impl() ) {
            out = v_.back();
            v_.pop_back();
        }
        return out;
    }
    mutex m_;
    //nie zamieszczono metod do wstawiania elementow
private:
    std::vector<PData> v_;
public:
    void push(PData data) {
        lock_guard lock(m_);
        v_.push_back(data);
    }
};

void calc(const Data& d) { //dlugotrwale obliczenia
    cout << "calc start d=" << d << endl;
    this_thread::sleep_for( chrono::milliseconds(1000) );
    cout << "calc end d= " << d << endl;
}

void thrd_main(Vector& v) {
    while(! v.empty_safe()) {
        lock_guard l(v.m_);
        calc(*v.read_impl());
    }
}

void thrd_main_better(Vector& v) {
    for(PData data = v.read_safe(); data != nullptr; data = v.read_safe() ) {

        //obliczenia poza sekcja krytyczna
        calc(*data);
    }
}

int main() {
     Vector v;
     //pominięto tworzenie wątków, które wstawiają obiekty PData do kolejki
     for(int i=0;i<100;++i) {
         v.push(PData(new Data(i)));
     }

     const int NUM = std::thread::hardware_concurrency();
    std::cout << NUM << " concurrent threads are supported by platform.\n";
    std::vector<std::thread> thrds;
    thrds.reserve(NUM);
    for (int i=0; i < NUM; ++i) {
        //thrds.emplace_back(&thrd_main, ref(v));
        thrds.emplace_back(&thrd_main_better, ref(v));
    }
    //join_all
    for(int i=0; i< NUM; ++i)
        thrds[i].join();
}
