/*	Karol Grydziuszko H1ISI
	
	Synchronizacja pokazana jest na przykładzie zapisywania i odczytywania danych z bufora	
*/

#include <boost/thread.hpp>
#include <iostream>

const unsigned int MAXBUF = 9;
char bufor[MAXBUF];
unsigned int start=0;
unsigned int finish=0;
unsigned int elems=0;

boost::recursive_mutex rmtx;//mutex do sesji krytycznej zapisu i odczytu z bufora

boost::condition empty;//warunek zatrzymujący czytelników
boost::condition full;//warunek zatrymujący pisarzy

/*
	klasa CZYTELNIK czyta daną z bufor[start]
	posiada numer id po czym można rozpoznać czytelnika
	wskaźnik na wątek jest przydatny do usypiania go	
*/
class reader
{
	private:
		int id;
		boost::thread *threadptr;
	public:
	
	reader(int id, boost::thread *tptr) : id(id) , threadptr(tptr) {};

	void operator() ()
	{
		boost::xtime sleeptime;//zmienna do obliczania czasu usypiania wątku

		while (1)
		{
			{//sekcja krytyczna	
			boost::recursive_mutex::scoped_lock lock(rmtx);//opuszczenie na muteksie rmtx
				if (elems == 0)
				{				
					std::cout << "czytelnik czeka \n";
					empty.wait(lock);//zawieszenie na warunku
					std::cout << "pisarz poscił czytelnika\n";
				}
				std::cout << "Czytanie"  << id <<": " <<  bufor[start] << "\t\t" << elems-1 << "\n";//odczyt z bufora		
				start = (start + 1)%9;
				elems--;		
				full.notify_one();//puszczenie pisarza jeśli jakiś czeka
			}

			boost::xtime_get(&sleeptime, boost::TIME_UTC);//ustawienie aktualnego czasu
			sleeptime.nsec += 200000000; // X*100 mili sec
			threadptr->sleep(sleeptime);//uśpienie wątku
		}
	}
};



/*
	klasa PISARZ pisze na pozycji bufor[finish] 
	posiada numer id po czym można rozpoznać pisarza
	wskaźnik na wątek jest przydatny do usypiania go	
*/
class writer
{
	private:
		int id;
		boost::thread *threadptr;
	public:
	
	writer(int id, boost::thread *tptr) : id(id) , threadptr(tptr) {};
	
	void operator() ()
	{
		static int i = 0;//zapewnia że pisarze piszą kolejne litery z alfabetu
		boost::xtime sleeptime;
	
		while (1)
		{
			i++;
	
			{//sekcja krytyczna
			boost::recursive_mutex::scoped_lock lock(rmtx);//opuszczenie na muteksie rmtx
				if (elems == MAXBUF)
				{
					std::cout << "pisarz czeka \n";
					full.wait(lock);//zawieszenie na warunku
					std::cout << "czytelnik poscił pisarza\n";
				}
				bufor[finish] = (char) ((i%25)+97);//zapis do bufora
				std::cout << "Pisanie" << id <<": " << bufor[finish] << "\t\t" << elems+1 << "\n";
				finish = (finish + 1)%9;
				elems++;		
				empty.notify_one();//puszczenie czytelnika jeśli jakiś czeka
			}
	
			boost::xtime_get(&sleeptime, boost::TIME_UTC);//ustawienie aktualnego czasu
			sleeptime.nsec += 100000000;
			threadptr->sleep(sleeptime);
		}
	}
};

int main (int argc, char * argv[])
{
	std::cout << "*********************************\n";
	std::cout << "*********Start Program***********\n";
	std::cout << "*********************************\n";
	//uruchomienie wątków
	boost::thread td1(reader(1,&td1));//czytelnik
	boost::thread td2(reader(2,&td2));
	boost::thread td3(writer(1,&td3));//pisarz
	boost::thread td4(writer(2,&td4));

	//funkcja main poczeka na zakończenie wątków
 	td1.join();
  	td2.join();
	td3.join();
	td4.join();
	return 0;
}

