/*
Każdy z obiektów Data w kolekcji Out jest tworzony wykorzystując stan systemu podczas obsługi zdarzenia zegara.
Kolekcja Out nie jest używana zanim nie zostanie uzupełniona.


*/


#include <vector>
#include <memory>
#include <iostream>
#include <functional>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

using namespace std;

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

struct Out {
    std::vector<PData> v;
    mutex m;

	//pobiera ostatni element z kolekcji i go usuwa.
	PData pop() {
		PData d; //init on nullptr
		lock_guard<mutex> lock(m);   //TUTAJ ZMIANA
		if( ! v.empty() ) {
			d = v.back();
			v.pop_back();
		}
		return d;
	}
};

using Event = std::function<void(void)>;

class Socket {
public:
	Socket(boost::asio::io_service& io)
		: t(io, boost::posix_time::milliseconds(50) )
	{}

	void send(PData d) {
		cout << "send" << endl;
		t.wait();
	}
	void async_send(PData d, Event event) {
	 	t.async_wait( [&](const boost::system::error_code& ){ event();});
	}
	//inne metody sa nieistotne.
private:
	boost::asio::deadline_timer t;
};

class Thread {
public:
    Thread(boost::asio::io_service& i, Out& o) : io(i), out(o) { }
    void operator()() {
		Socket s(io); //local socket
		PData d; //init on nullptr
		do {
			d = out.pop();
			if(d != nullptr) {
				s.send(d);
			}
		} while( d != nullptr );
    }
private:
	boost::asio::io_service& io;
    Out& out;
};

int main() {
    Out out;
	for(int i=0;i<500;++i)
		out.v.push_back( PData(new Data()) ); //tworzenie paczki danych, tego nie poprawiamy

    boost::asio::io_service io;

    Thread t1(io, out);
    Thread t2(io, out);
    Thread t3(io, out);

    thread thrd1( ref(t1) );
    thread thrd2( ref(t2) );
    thread thrd3( ref(t3) );

    thrd1.join();
    thrd2.join();
    thrd3.join();

    return 0;
}

