/*
	Ejemplo de programación multihilo con MFC

		Autor: Ariel Medina			18 de agosto del 2004

*/

#include <Afxwin.h>//tipos y funciones de Windows
#include <Afxmt.h>//soporte para multihilo
#include <iostream>//soporte para flujos
#include <deque>//contenedor para los objetos a coordinar mediante la programación multihilo
#include <string>//tipo para cadenas de caracteres

/*
Los objetos de la clase siguiente serán accesados en diferentes hilos de ejecución, por lo cual,
para mantener el acceso coordinado, utilizarán un objeto semáforo que permitirá el acceso exclusivo
de un hilo de ejecución a la vez.
*/
class A	{
int id;
public:
	mutable CSemaphore semaphore;//Semáforo para acceso al objeto
	bool stop;//Bandera para que le indiquen si debe terminar (debe estar sincronizada por el semáforo)
	bool active;//bandera para indicar si el hilo está activo

	A(int idT):
	semaphore(), stop(false), active(true), id(idT)//constructor
		{
		}
	A(const A &a):
	semaphore(), id(a.id)//constructor de copia
		{
		a.semaphore.Lock();
		stop=a.stop;
		active=a.active;
		a.semaphore.Unlock();
		}
	int getId() const//obtener el Id del objeto
		{
		return id;
		}

};

//Función para ejecutar en un hilo diferente por objeto de la clase A
unsigned int controllingFunction(LPVOID pObject)
{
A *pA=(A *)pObject;

while(true)
	{
	std::cerr<<"Hilo número: "<<pA->getId()<<'\n';

	pA->semaphore.Lock();
	if(pA->stop)//si hay indicación de detención
		{
		std::cerr<<"[[[ Hilo terminado: "<<pA->getId()<<" ]]]\n";
		pA->active=false;//indicamos que el hilo está inactivo y rompemos el ciclo
		pA->semaphore.Unlock();
		break;
		}
	pA->semaphore.Unlock();
	Sleep(2000);//para evitar una lluvia de mensajes en la consola
	}

return 0;//todo bien, hilo finalizado
}

//Revisa por hilos activos
bool someActive(const std::deque<A> &d)
{
for(std::deque<A>::const_iterator it=d.begin(); it!=d.end(); ++it)
	{
	it->semaphore.Lock();
	if(it->active)
		{
		it->semaphore.Unlock();
		return true;
		}
	it->semaphore.Unlock();
	}
return false;
}

//Indica a un hilo que debe detenerse
void stopThread(std::deque<A> &d, int aID)
{
for(std::deque<A>::iterator it=d.begin(); it!=d.end(); ++it)
	{
	if(it->getId()==aID)
		{
		it->semaphore.Lock();
		if(!it->stop)
			{
			it->stop=true;
			}
		it->semaphore.Unlock();
		}
	}
}

//Indica a todos los hilos que deben detenerse
void stopThreads(std::deque<A> &d)
{
for(std::deque<A>::iterator it=d.begin(); it!=d.end(); ++it)
	{
	it->semaphore.Lock();
	if(!it->stop)
		{
		it->stop=true;
		}
	it->semaphore.Unlock();
	}
}

//*** Programa principal para coordinar los objetos compartidos por los hilos de ejecución

void main()
{
std::deque<A> dA;
int id;
std::string s, squit("exit"), skill("kill");//órdenes para salir del programa y para detener uno o todos los hilos

dA.push_back(A(1));//agregamos un objeto al contenedor
AfxBeginThread(controllingFunction,(LPVOID)&dA[dA.size()-1]);//iniciamos un hilo con ese objeto
dA.push_back(A(2));//agregamos otro objeto al contenedor
AfxBeginThread(controllingFunction,(LPVOID)&dA[dA.size()-1]);//iniciamos otro hilo con el objeto anterior

while(true)
	{
	std::cerr<<':';//símbolo para indicar la espera de una orden
	std::cin>>s;//leemos la orden
	if(s==squit)//si se quiere salir del programa
		{
		stopThreads(dA);//detener los hilos en ejecución y salir
		break;
		}
	else if(s==skill)//si se quiere detener uno o todos los hilos
		{
		std::cin.get();
		if(std::cin.rdbuf()->in_avail())//si tenemos el número de hilo
			{
			id=0;
			std::cin>>id;
			stopThread(dA,id);//detenerlo
			}
		else
			stopThreads(dA);//detener todos
		}
	else
		{
		std::cerr<<"Orden desconocida\n";
		}
	}
while(someActive(dA))//esperar a que todos los hilos finalicen para salir del programa
	;

}