/*** Marcin Krzewski
/*** Grupa: N1-IBERE2

/*** Boost::Interprocess::SharedMemory
/*** Przykład pokazujący użycie pamięci dzielonej do wymiany danych pomiędzy wątkami

Pamięć dzielona jest najszybszym sposobem komunikacji międzyprocesowej. System operacyjny mapuje
segment pamięci w przestrzeni adresowej szeregu procesów, dzięki czemu wiele procesów może czytać 
oraz zapisywać ten segment pamięci bez potrzeby wołania funkcji systemu operacyjnego. Jednakże, 
potrzebny jest sposób synchronizacji pomiędzy procesami, które operują na tym samym obszarze pamięci
dzielonej. Dzięki zastosowaniu pamięci dzielonej, możemy uniknąć tworzenia dwóch kopii danych w systemie.

Pamięć dzielona może być wykorzystywana przez różne procesy, wątki pochodzące od jednego procesu oraz
wątki, które zostały stworzone w różnych procesach.

W pamięci dzielonej możemy przechowywać podstawowe typy danych, jak również typy i obiekty
zdefiniowane przez programistę. ***/

// Nagłówki umożliwiające korzystanie z pamięci dzielonej
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>

#include <boost/thread.hpp>
#include <iostream>

// Przestrzeń nazw komunikacji międzyprocesowej, w tym pamięci dzielonej
using namespace boost::interprocess;
using namespace std;

// Wykonywanie operacji na danych z pamięci dzielonej
void shmem(const int offset_) {
	try {
		// Otwieranie istniejącego obszaru pamięci dzielonej
		shared_memory_object shm (
			open_only,			// otwieranie istniejącego bloku
			"shared_memory",	// nawa bloku
			read_write);		// z opcjami odczytu/zapisu

		// Mapowanie pamięci dzielonej
		mapped_region region(shm, read_write);

		// Pobieranie adresy do mapowanego obszaru pamięci
		void* addr = region.get_address();

		// Rzutowanie wskaźnika adresu na typu int*
		int* data = static_cast<int*>(addr);

		// Obliczanie nowej wartości danej oraz umieszczenie jej z powrotem w pamięci dzielonej
		data[offset_] *= data[offset_];
	}
	catch(interprocess_exception &ex) {
		std::cout << "Unexpected exception: " << ex.what() << std::endl;
		shared_memory_object::remove("shared_memory");
	}
}

void namedShm(const char* name) {
	try{
		// Specjalna pamięć dzielona, gdzie możemy konstruować obiekty skojarzone z nazwami
		managed_shared_memory segment(open_only, "shared_memory");

		// Szukanie odpowiedniego obiektu w pamięci
		// Funkcja managed_shared_memory::find zwraca kontener std::pair, gdzie pierwszą
		// wartością jest wskaźnik do danych zawartych w pamięci, a drugim wielkość 
		// zarezerwowanej pamięci
		pair<int*, size_t> res1 = segment.find<int>(name);
		if(res1.second != 0) {
			int* ptr = res1.first;
			for(unsigned int i = 0; i < res1.second; i++)
				cout << ptr[i] << ' ';

			cout << endl;
			return;
		}

		// Szukanie odpowiedniego obiektu w pamięci
		pair<int*, size_t> res2 = segment.find<int>(name);   
		if(res2.second != 0) {
			int* ptr = res2.first;
			for(unsigned int i = 0; i < res2.second; i++)
				cout << ptr[i] << ' ';

			cout << endl;
			return;
		}
	}
	catch(interprocess_exception &ex) {
		std::cout << "Unexpected exception: " << ex.what() << std::endl;
		shared_memory_object::remove("shared_memory");
	}
}

int main ()
{
	// Przykład pokazuje powierzanie wykonywania operacji dodatkowym wątkom na danych umieszczonych 
	// w pamięci dzielonej przez wątek główny.
	try {
		// Usuwanie poprzedniej pamięci dzielonej
		shared_memory_object::remove("shared_memory");

		// Tworzenie obiektu pamięci dzielonej
		shared_memory_object shm(create_only,	// tworzenie bloku
							"shared_memory",	// nazwa bloku pamięci
							read_write);		// opcje dostępu odczyt/zapis

		// Ustawienie wielkości pamięci
		shm.truncate(3);

		// Mapowanie całej pamięci dzielonej dla tego procesu
		mapped_region region(shm, read_write);
		int* mem = static_cast<int*>(region.get_address());

		// Zapis różnych wartości do pamięci
		for(unsigned int i = 0; i < region.get_size(); i++) {
			mem[i] = i * 10 + 5;
			cout << mem[i] << ' ';
		}
		cout << endl;

		// Tworzenie nowych wątków, które będą operowały na danych zawartych w pamięci dzielonej
		boost::thread thread1(shmem, 0);
		boost::thread thread2(shmem, 1);
		boost::thread thread3(shmem, 2);

		// Oczekiwanie na zakończenie poszczególnych wątków
		thread1.join();
		thread2.join();
		thread3.join();

		// Wyświetlanie wartości zmienionej przez wątki
		cout << "Obliczone wartosci: ";
		cout << mem[0] << ' ' << mem[1] << ' ' << mem[2] << endl;

		// Usuwanie pamięci dzielone
		shared_memory_object::remove("shared_memory");
	}
	catch(interprocess_exception &ex) {
		shared_memory_object::remove("shared_memory");
		cout << ex.what() << std::endl;
		return 1;
	}

	// Przykład pokazuje użycie specjalnej pamięci dzielonej, w której możemy do skonstruowanego obiektu
	// przypisać nazwę.
	try {
		// Usuwanie starej pamięci dzielonej o takiej samej nazwie, tworzenie nowego bloku
		// oraz zarezerwowanie wymaganych zasobów
		shared_memory_object::remove("shared_memory");
		managed_shared_memory segment(create_only, "shared_memory", 65536);

		// Tworzy tablice 4 elementów typu int oraz inicjalizuje wszystkie wartością 255
		int *array_int = segment.construct<int>("first int array")[4](255);

		// Tworzy tablice 3 elementów typu int oraz inicjalizuje kolejne z nich, 
		// wartością zwracaną przez wyrażenie *tab_int++
		int tab_int[3] = {12, 34, 56};
		int *array_int2 = segment.construct_it<int>("second int array")[3](&tab_int[0]); 

		boost::thread thread1(namedShm, "first int array");
		boost::thread thread2(namedShm, "second int array");
		thread1.join();
		thread2.join();
		shared_memory_object::remove("shared_memory");
	}
	catch(interprocess_exception &ex) {
		std::cout << "Unexpected exception: " << ex.what() << std::endl;
		shared_memory_object::remove("shared_memory");
	}

	getchar();
	return 0;
}