/*************************************************************************
 * This file is part of the Distributed Computation feature
 *
 * Project created in conformity with the requirements for the Degree of
 * Master of Engineering in Software Engineering, Computing and Software
 * Department, McMaster University 2012
 *
 * Author:	David Kirby
 * Supervisor: Dr. Ryan Leduc
 ************************************************************************/
#include "HostList.h"

#ifdef __ENABLE_DIST__

namespace DESpot
{
	int					HostList::m_maxHosts	= -99;	// maximum number of hosts requested so far (-1 = unlimited)
	std::vector<Host*>	HostList::m_hosts		= std::vector<Host*>();
	std::deque<int>		HostList::m_freeHosts	= std::deque<int>();

	pthread_mutex_t		HostList::m_hostListMutex	= PTHREAD_MUTEX_INITIALIZER;
	pthread_cond_t		HostList::m_hostListCond	= PTHREAD_COND_INITIALIZER;

	HostList::HostList(int maxHosts)
	{
		// if we haven't requested unlimited hosts already (-1) and
		// we are now requesting either unlimited hosts (-1) or more than
		// previously requested (m_maxHosts < maxHosts)
		if (m_maxHosts != -1 && (maxHosts == -1 || m_maxHosts < maxHosts))
		{
			pthread_mutex_lock(&m_hostListMutex);

			// ensure that we still need to create more hosts
			if (m_maxHosts != -1 && (maxHosts == -1 || m_maxHosts < maxHosts))
			{
				m_maxHosts = maxHosts;

				// Get the number of hosts in the file
				int hostNum = HostFileManager::length();

				// If a maximum number of hosts was given, and it is less than the number of hosts in the file, use it
				if (maxHosts > 0 && maxHosts <= hostNum)
					hostNum = maxHosts;		
		
				// start the counter at the current size of the host list. This way, if a HostList is created
				// that needs to increase the number of hosts, we only spawn the number needed to reach the new
				// target number
				for (int i = m_hosts.size(); i < hostNum; i++)
				{
					m_freeHosts.push_back(i);
					m_hosts.push_back(new Host(i));
					SpawnWrapper::spawnSlave(m_hosts[i]);			
				}

			}

			pthread_mutex_unlock(&m_hostListMutex);
		}
	}

//______________________________________________________________________________________________

	Host* HostList::getHost()
	{

		pthread_mutex_lock(&m_hostListMutex);

		Host* result = NULL;

		// Wait until there is a free host
		while (m_freeHosts.size() == 0)
		{
			pthread_cond_wait(&m_hostListCond, &m_hostListMutex);
		}

		// get a free host
		int i = m_freeHosts.front();
		m_freeHosts.pop_front();

		m_hosts[i]->setLock(true);								
		result = m_hosts[i];

		pthread_mutex_unlock(&m_hostListMutex);

		return result;
	}

//______________________________________________________________________________________________

	void HostList::freeHost(Host* host)
	{
		pthread_mutex_lock(&m_hostListMutex);

		// unlock the host
		host->setLock(false);
		m_freeHosts.push_back(host->getID());

		// signal a waiting process that a host is free
		pthread_cond_signal(&m_hostListCond);
		pthread_mutex_unlock(&m_hostListMutex);
	}

//______________________________________________________________________________________________

	int HostList::count()
	{
		return m_hosts.size();
	}

//______________________________________________________________________________________________

	void HostList::clearHostList()
	{
		m_maxHosts = -99;
		m_hosts.clear();
		m_freeHosts.clear();
	}
}

#endif
