view engine/core/util/resource/pool.cpp @ 148:72c25cc27d8b

For your convenience pools now have a function called purgeLoadedResources exposed to python which will delete all loaded resources that have a ref count of zero. You can use this after closing a map or something alike. It may however impact performance, as the next map may very well reload the same resources. Just be aware of that. :-)
author phoku@33b003aa-7bff-0310-803a-e67f0ece8222
date Sat, 11 Oct 2008 06:05:11 +0000
parents fb6ccb367dd1
children 823544621b5b
line wrap: on
line source

/***************************************************************************
 *   Copyright (C) 2005-2008 by the FIFE team                              *
 *   http://www.fifengine.de                                               *
 *   This file is part of FIFE.                                            *
 *                                                                         *
 *   FIFE is free software; you can redistribute it and/or                 *
 *   modify it under the terms of the GNU Lesser General Public            *
 *   License as published by the Free Software Foundation; either          *
 *   version 2.1 of the License, or (at your option) any later version.    *
 *                                                                         *
 *   This library is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

// Standard C++ library includes

// 3rd party library includes

// FIFE includes
// These includes are split up in two parts, separated by one empty line
// First block: files included from the FIFE root src directory
// Second block: files included from the same folder
#include "util/base/exception.h"
#include "util/log/logger.h"

#include "pool.h"

namespace FIFE {
	static Logger _log(LM_POOL);
	
	Pool::Pool(const std::string& name): 
		m_entries(),
		m_location_to_entry(),
		m_listeners(),
		m_loaders(),
		m_curind(0),
		m_name(name)
	{
	}

	Pool::~Pool() {
		FL_LOG(_log, LMsg("Pool destroyed: ") << m_name);
		printStatistics();
		sanityCheck();
		reset();
		std::vector<ResourceLoader*>::iterator loader;
		for (loader = m_loaders.begin(); loader != m_loaders.end(); loader++) {
			delete (*loader);
		}
	}
	
	void Pool::reset() {
		std::vector<IPoolListener*>::iterator listener;
		for (listener = m_listeners.begin(); listener != m_listeners.end(); listener++) {
			(*listener)->poolCleared();
		}
		std::vector<PoolEntry*>::iterator entry;
		for (entry = m_entries.begin(); entry != m_entries.end(); entry++) {
			// Warn about leaks, but at least display ALL of them
			// Instead of bailing out with an exception in the FifeClass destructor.
			if( (*entry)->resource && (*entry)->resource->getRefCount() > 0 ) {
				FL_WARN(_log, LMsg(m_name + " leak: ") << (*entry)->location->getFilename());
				(*entry)->resource = 0;
			}
			delete (*entry);
		}
		m_entries.clear();
		m_location_to_entry.clear();
	}

	int Pool::purgeLoadedResources() {
		std::vector<IPoolListener*>::iterator listener;
		for (listener = m_listeners.begin(); listener != m_listeners.end(); listener++) {
			(*listener)->poolCleared();
		}
		int count = 0;
		std::vector<PoolEntry*>::iterator it;
		for (it = m_entries.begin(); it != m_entries.end(); it++) {
			PoolEntry* entry = *it;
			if( entry->resource && entry->resource->getRefCount() == 0 ) {
				delete entry->resource;
				entry->resource = 0;
				++count;
			}
		}
		return count;
	}

	void Pool::addResourceLoader(ResourceLoader* loader) {
		m_loaders.push_back(loader);
	}

	int Pool::addResourceFromLocation(const ResourceLocation& loc) {
		ResourceLocationToEntry::const_iterator it = m_location_to_entry.find(loc);
		if (it != m_location_to_entry.end()) {
			return (*it).second;
		}
		
		PoolEntry* entry = new PoolEntry();
		entry->location = loc.clone();
		m_entries.push_back(entry);
		size_t index = m_entries.size() - 1;
		m_location_to_entry[loc] = index;
		return index;
	}

	int Pool::addResourceFromFile(const std::string& filename) {
		return addResourceFromLocation(ResourceLocation(filename));
	}

	IResource& Pool::get(unsigned int index, bool inc) {
		if (index >= m_entries.size()) {
			FL_ERR(_log, LMsg("Tried to get with index ") << index << ", only " << m_entries.size() << " items in pool " + m_name);
			throw IndexOverflow( __FUNCTION__ );
		}
		IResource* res = NULL;
		PoolEntry* entry = m_entries[index];
		if (entry->resource) {
			res = entry->resource;
		} else {
			if (!entry->loader) {
				findAndSetProvider(*entry);
			} else {
				entry->resource = entry->loader->loadResource(*entry->location);
			}

			if (!entry->loader) {
				LMsg msg("No suitable loader was found for resource ");
				msg << "#" << index << "<" << entry->location->getFilename()
				    << "> in pool " << m_name;
				FL_ERR(_log, msg);
	      
				throw NotFound(msg.str);
			}

			if (!entry->resource) {
				LMsg msg("No loader was able to load the requested resource ");
				msg << "#" << index << "<" << entry->location->getFilename()
				    << "> in pool " << m_name;
				FL_ERR(_log, msg);
				throw NotFound(msg.str);
			}
			res = entry->resource;
		}
		if (inc) {
			res->addRef();
		}
		res->setPoolId(index);
		return *res;
	}
	
	int Pool::getIndex(const std::string& filename) {
		// create resource
		return addResourceFromFile(filename);
	}

	void Pool::release(unsigned int index, bool dec) {
		if (index >= m_entries.size()) {
			throw IndexOverflow( __FUNCTION__ );
		}

		IResource* res = NULL;
		PoolEntry* entry = m_entries[index];
		if (entry->resource) {
			res = entry->resource;
			if (dec) {
				res->decRef();
			}
			if(res->getRefCount() == 0) {
				delete entry->resource;
				entry->resource = 0;
			}
		}
	}

	int Pool::getResourceCount(int status) {
		int amount = 0;
		std::vector<PoolEntry*>::iterator entry;
		for (entry = m_entries.begin(); entry != m_entries.end(); entry++) {
			if (status & RES_LOADED) {
				if ((*entry)->resource) {
					amount++;
				}
			}
			if (status & RES_NON_LOADED) {
				if (!(*entry)->resource) {
					amount++;
				}
			}
		}
		return amount;
	}

	void Pool::addPoolListener(IPoolListener* listener) {
		m_listeners.push_back(listener);
	}

	void Pool::removePoolListener(IPoolListener* listener) {
		std::vector<IPoolListener*>::iterator i = m_listeners.begin();
		while (i != m_listeners.end()) {
			if ((*i) == listener) {
				m_listeners.erase(i);
				return;
			}
			++i;
		}
	}

	void Pool::findAndSetProvider(PoolEntry& entry) {
		std::vector<ResourceLoader*>::iterator it = m_loaders.begin();
		std::vector<ResourceLoader*>::iterator end = m_loaders.end();
		if( it == end ) {
			FL_PANIC(_log, "no loader constructors given for resource pool");
		}
		for(; it != end; ++it) {
			IResource* res = (*it)->loadResource(*entry.location);
			if (res) {
				entry.resource = res;
				entry.loader = *it;
				return;
			}
		};
	}

	void Pool::printStatistics() {
		FL_LOG(_log, LMsg("Pool not loaded =") << getResourceCount(RES_NON_LOADED));
		FL_LOG(_log, LMsg("Pool loaded     =") << getResourceCount(RES_LOADED));
		int amount = 0;
		std::vector<PoolEntry*>::iterator entry;
		for (entry = m_entries.begin(); entry != m_entries.end(); entry++) {
			if ((*entry)->resource) {
				if ((*entry)->resource->getRefCount() > 0) {
					amount++;
				}
			}
		}
		FL_LOG(_log, LMsg("Pool locked     =") << amount);
		FL_LOG(_log, LMsg("Pool total size =") << m_entries.size());
	}

	void Pool::sanityCheck() {
		// It is easy to mess up the important consistent
		// less-than operator for the location classes.
		// This will check if according to the '==' operator 
		// entries are duplicate (=memory leaks).
		// Slow and inaccurate. But should barf at real messups.
		for(unsigned i = 0; i != m_entries.size(); ++i) {
			int count = 0;
			for(unsigned j = i+1; j < m_entries.size(); ++j) {
				if( *m_entries[i]->location == *m_entries[j]->location )
					count ++;
			}
			if( 0 == count )
				continue;
			FL_WARN(_log, LMsg("Multiple entries: ") << m_entries[i]->location->getFilename()
			  << " #entries = " << (count+1) );
		}
	}

}