Blob Blame Raw


//Qt includes
#include <QMap>
#include <QSettings>
#include <QDate>
#include <QDir>
#include <QFileInfo>
#include <QFileInfoList>

//#define USE_SQLITE_HDPOOL

#ifdef USE_SQLITE_HDPOOL
//SQLite include
#include "sqlite/sqlite3.h"
#endif

#include "tcacheresourcepool.h"

//Debug
//#define DIAGNOSTICS
//#include "diagnostics.h"

//******************************************************************************************
//    Cache Resource Pool BACKED ON DISK
//******************************************************************************************

//STILL UNDER DEVELOPMENT...
class THDCacheResourcePool
{
public:
	THDCacheResourcePool() {}
	~THDCacheResourcePool() {}
};

//*************************************************************************************
//    Cache resource pool methods involved with HD Pool management
//*************************************************************************************

inline bool TCacheResourcePool::isHDActive()
{
#ifdef USE_SQLITE_HDPOOL
	return m_hdPool && m_hdPool->isActive();
#else
	return false;
#endif
}

//-----------------------------------------------------------------------------------

void TCacheResourcePool::reset()
{
	setPath("", "", "");
}

//----------------------------------------------------------------------

// Prevents the resources in memory from backing to disk. Observe that
// the actual content of the resource is NOT invalidated - since resources
// are intended as 'reference-protected' material which is expected to last
// as long as references are held.
void TCacheResourcePool::invalidateAll()
{
	QMutexLocker locker(&m_memMutex);

	MemResources::iterator it;
	for (it = m_memResources.begin(); it != m_memResources.end(); ++it)
		it->second->invalidate();
}

//----------------------------------------------------------------------

inline QString TCacheResourcePool::getPoolRoot(QString cacheRoot, QString projectName, QString sceneName)
{
	return QString(cacheRoot + "/render/" + projectName + "/" + sceneName + "/");
}

//----------------------------------------------------------------------

//! Connects to the pool associated to the given project/scene pair.
//! \warning As this closes the current connection before opening a new one,
//! make sure that no pool access happens at this point. You should also
//! verify that no resource from the old pair still exists.
void TCacheResourcePool::setPath(QString cacheRoot, QString projectName, QString sceneName)
{
	//There should be no resource in memory.
	assert(m_memResources.empty());

	//However, just in case, invalidate all resources so that no more resource backing
	//operation take place for current resources, from now on.
	//No care is paid as to whether active transactions currently exist. You
	//have been warned by the way....
	invalidateAll();

	delete m_hdPool;
	m_hdPool = 0;
	m_path = TFilePath();

#ifdef USE_SQLITE_HDPOOL

	if (!(cacheRoot.isEmpty() || projectName.isEmpty() || sceneName.isEmpty())) {
		QString hdPoolRoot(getPoolRoot(cacheRoot, projectName, sceneName));
		m_hdPool = new THDCacheResourcePool(hdPoolRoot);
		m_path = m_hdPool->getResourcesFilePath();
	}

#endif
}

//-----------------------------------------------------------------------------------

void TCacheResourcePool::startBacking(TCacheResource *resource)
{
	assert(isHDActive());
	if (!isHDActive())
		return;

#ifdef USE_SQLITE_HDPOOL

	resource->m_backEnabled = true;

	m_hdPool->buildBackingPath(resource);

#endif
}

//******************************************************************************************
//    Cache resource pool implementation
//******************************************************************************************

TCacheResourcePool *TCacheResourcePool::instance()
{
	static TCacheResourcePool theInstance;
	return &theInstance;
}

//----------------------------------------------------------------------

TCacheResourcePool::TCacheResourcePool()
	: m_memMutex(QMutex::Recursive), m_searchCount(0), m_foundIterator(false), m_searchIterator(m_memResources.end()), m_hdPool(0), m_path()
{
	//Open the settings for cache retrieval
}

//----------------------------------------------------------------------

TCacheResourcePool::~TCacheResourcePool()
{
	//Temporary
	//performAutomaticCleanup();

	delete m_hdPool;
}

//----------------------------------------------------------------------

const TFilePath &TCacheResourcePool::getPath() const
{
	return m_path;
}

//----------------------------------------------------------------------

//! Initializes an optimized search on the pool for a specific resource, caching successive
//! results.
//! \note Pool searches are serialized, and calls to this method lock the pool's mutex until a
//! corresponding number of endCachedSearch() methods are invoked.
void TCacheResourcePool::beginCachedSearch()
{
	m_memMutex.lock();
	m_searchCount++;
}

//----------------------------------------------------------------------

//! The inverse to beginCachedSearch(). This method \b MUST be called in correspondence to
//! beginCachedSearch() calls.
void TCacheResourcePool::endCachedSearch()
{
	if (--m_searchCount <= 0) {
		m_foundIterator = false;
		m_searchIterator = m_memResources.end();
	}
	m_memMutex.unlock();
}

//----------------------------------------------------------------------

//! Attempts retrieval of the resource with specified name, and eventually creates it if
//! the createIfNone parameter is set.
TCacheResource *TCacheResourcePool::getResource(const std::string &name, bool createIfNone)
{
	//DIAGNOSTICS_TIMER("#times.txt | getResource Overall time");
	//DIAGNOSTICS_MEANTIMER("#times.txt | getResource Mean time");

	TCacheResource *result = 0;

	//NOTA: Passa ad un oggetto lockatore. Quello e' in grado di gestire i casi di eccezioni ecc..
	beginCachedSearch();

	//Search for an already allocated resource
	if (m_searchIterator == m_memResources.end()) {
		m_searchIterator = m_memResources.lower_bound(name);
		if (m_searchIterator != m_memResources.end())
			if (!(name < m_searchIterator->first))
				m_foundIterator = true;
			else if (m_searchIterator != m_memResources.begin())
				m_searchIterator--;
	}

	if (m_foundIterator) {
		result = m_searchIterator->second;

		endCachedSearch();
		return result;
	}

	{
		QString resourcePath;
		QString resourceFlags;

		if (isHDActive()) {
#ifdef USE_SQLITE_HDPOOL

			//DIAGNOSTICS_TIMER("#times.txt | HDPOOL getResource Overall time");
			//DIAGNOSTICS_MEANTIMER("#times.txt | HDPOOL getResource Mean time");

			//Search in the HD pool
			ReadQuery query(m_hdPool);

			bool ret = query.prepare(
				"SELECT Path, Flags FROM Resources WHERE Name = '" + QString::fromStdString(name) + "';");

			//If an error occurred, assume the resource does not exist. Doing nothing works fine.
			assert(ret);

			if (query.step()) {
				resourcePath = query.value(0);
				resourceFlags = query.value(1);
			}

#endif
		}

		if (!resourcePath.isEmpty() || createIfNone) {
			TCacheResource *result = new TCacheResource;
			result->m_pos = m_searchIterator =
				m_memResources.insert(m_searchIterator, std::make_pair(name, result));

//DIAGNOSTICS_STRSET("#resources.txt | RISORSE | " + QString::number((UINT) result) + " | Name",
//QString::fromStdString(name).left(70));

#ifdef USE_SQLITE_HDPOOL
			if (isHDActive())
				m_hdPool->loadResourceInfos(result, resourcePath);
#endif

			m_foundIterator = true;
			endCachedSearch();

			return result;
		}
	}

	endCachedSearch();
	return 0;
}

//----------------------------------------------------------------------

void TCacheResourcePool::releaseResource(TCacheResource *resource)
{
	QMutexLocker locker(&m_memMutex);

	//Re-check the resource's reference count. This is necessary since a concurrent
	//thread may have locked the memMutex for resource retrieval BEFORE this one.
	//If that is the case, the resource's refCount has increased back above 0.
	if (resource->m_refCount > 0)
		return;

#ifdef USE_SQLITE_HDPOOL
	QMutexLocker flushLocker(isHDActive() ? &m_hdPool->m_flushMutex : 0);

	if (isHDActive()) {
		//Flush all resource updates as this resource is being destroyed
		m_hdPool->flushResources();

		//Save the resource infos
		m_hdPool->saveResourceInfos(resource);
	}
#endif

	m_memResources.erase(resource->m_pos);
	delete resource;
}