pool.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005-2008 by the FIFE team                              *
00003  *   http://www.fifengine.de                                               *
00004  *   This file is part of FIFE.                                            *
00005  *                                                                         *
00006  *   FIFE is free software; you can redistribute it and/or                 *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the                 *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
00020  ***************************************************************************/
00021 
00022 // Standard C++ library includes
00023 
00024 // 3rd party library includes
00025 
00026 // FIFE includes
00027 // These includes are split up in two parts, separated by one empty line
00028 // First block: files included from the FIFE root src directory
00029 // Second block: files included from the same folder
00030 #include "util/base/exception.h"
00031 #include "util/log/logger.h"
00032 
00033 #include "pool.h"
00034 
00035 namespace FIFE {
00036     static Logger _log(LM_POOL);
00037     
00038     Pool::Pool(const std::string& name): 
00039         m_entries(),
00040         m_location_to_entry(),
00041         m_loaders(),
00042         m_name(name)
00043     {
00044     }
00045 
00046     Pool::~Pool() {
00047         FL_LOG(_log, LMsg("Pool destroyed: ") << m_name);
00048         printStatistics();
00049         sanityCheck();
00050         reset();
00051         std::vector<ResourceLoader*>::iterator loader;
00052         for (loader = m_loaders.begin(); loader != m_loaders.end(); loader++) {
00053             delete (*loader);
00054         }
00055     }
00056     
00057     void Pool::reset() {
00058         std::vector<PoolEntry*>::iterator entry;
00059         for (entry = m_entries.begin(); entry != m_entries.end(); entry++) {
00060             // Warn about leaks, but at least display ALL of them
00061             // Instead of bailing out with an exception in the FifeClass destructor.
00062             if( (*entry)->resource && (*entry)->resource->getRefCount() > 0 ) {
00063                 FL_WARN(_log, LMsg(m_name + " leak: ") << (*entry)->location->getFilename());
00064                 (*entry)->resource = 0;
00065             }
00066             delete (*entry);
00067         }
00068         m_entries.clear();
00069         m_location_to_entry.clear();
00070     }
00071 
00072     int Pool::purgeLoadedResources() {
00073         int count = 0;
00074         std::vector<PoolEntry*>::iterator it;
00075         for (it = m_entries.begin(); it != m_entries.end(); it++) {
00076             PoolEntry* entry = *it;
00077             if( entry->resource && entry->resource->getRefCount() == 0 ) {
00078                 delete entry->resource;
00079                 entry->resource = 0;
00080                 ++count;
00081             }
00082         }
00083         return count;
00084     }
00085 
00086     void Pool::addResourceLoader(ResourceLoader* loader) {
00087         m_loaders.push_back(loader);
00088     }
00089 
00090     int Pool::addResourceFromLocation(ResourceLocation* loc) {
00091         ResourceLocationToEntry::const_iterator it = m_location_to_entry.find(loc);
00092         if (it != m_location_to_entry.end()) {
00093             return it->second;
00094         }
00095         
00096         PoolEntry* entry = new PoolEntry();
00097         entry->location = loc->clone();
00098         m_entries.push_back(entry);
00099         size_t index = m_entries.size() - 1;
00100         m_location_to_entry[entry->location] = index;
00101         return index;
00102     }
00103 
00104     int Pool::addResourceFromFile(const std::string& filename) {
00105         ResourceLocation r = ResourceLocation(filename);
00106         return addResourceFromLocation(&r);
00107     }
00108 
00109     IResource& Pool::get(unsigned int index, bool inc) {
00110         if (index >= m_entries.size()) {
00111             FL_ERR(_log, LMsg("Tried to get with index ") << index << ", only " << m_entries.size() << " items in pool " + m_name);
00112             throw IndexOverflow( __FUNCTION__ );
00113         }
00114         IResource* res = NULL;
00115         PoolEntry* entry = m_entries[index];
00116         if (entry->resource) {
00117             res = entry->resource;
00118         } else {
00119             if (!entry->loader) {
00120                 findAndSetProvider(*entry);
00121             } else {
00122                 entry->resource = entry->loader->loadResource(*entry->location);
00123             }
00124 
00125             if (!entry->loader) {
00126                 LMsg msg("No suitable loader was found for resource ");
00127                 msg << "#" << index << "<" << entry->location->getFilename()
00128                     << "> in pool " << m_name;
00129                 FL_ERR(_log, msg);
00130           
00131                 throw NotFound(msg.str);
00132             }
00133 
00134             // This branch will only be relevant if the resource has been
00135             // loaded successfully before, but for some reason the loader
00136             // can't load the resource anymore.
00137             // Maybe someone deleted a file under FIFE's hands?
00138             if (!entry->resource) {
00139                 LMsg msg("The loader was unable to load the resource ");
00140                 msg << "#" << index << "<" << entry->location->getFilename()
00141                     << "> in pool " << m_name;
00142                 FL_ERR(_log, msg);
00143                 throw NotFound(msg.str);
00144             }
00145             res = entry->resource;
00146         }
00147         if (inc) {
00148             res->addRef();
00149         }
00150         res->setPoolId(index);
00151         return *res;
00152     }
00153 
00154     void Pool::release(unsigned int index, bool dec) {
00155         if (index >= m_entries.size()) {
00156             throw IndexOverflow( __FUNCTION__ );
00157         }
00158 
00159         IResource* res = NULL;
00160         PoolEntry* entry = m_entries[index];
00161         if (entry->resource) {
00162             res = entry->resource;
00163             if (dec) {
00164                 res->decRef();
00165             }
00166             if(res->getRefCount() == 0) {
00167                 delete entry->resource;
00168                 entry->resource = 0;
00169             }
00170         }
00171     }
00172 
00173     int Pool::getResourceCount(int status) {
00174         int amount = 0;
00175         std::vector<PoolEntry*>::iterator entry;
00176         for (entry = m_entries.begin(); entry != m_entries.end(); entry++) {
00177             if (status & RES_LOADED) {
00178                 if ((*entry)->resource) {
00179                     amount++;
00180                 }
00181             }
00182             if (status & RES_NON_LOADED) {
00183                 if (!(*entry)->resource) {
00184                     amount++;
00185                 }
00186             }
00187         }
00188         return amount;
00189     }
00190 
00191     void Pool::findAndSetProvider(PoolEntry& entry) {
00192         std::vector<ResourceLoader*>::iterator it = m_loaders.begin();
00193         std::vector<ResourceLoader*>::iterator end = m_loaders.end();
00194         if( it == end ) {
00195             FL_PANIC(_log, "no loader constructors given for resource pool");
00196         }
00197         for(; it != end; ++it) {
00198             IResource* res = (*it)->loadResource(*entry.location);
00199             if (res) {
00200                 entry.resource = res;
00201                 entry.loader = *it;
00202                 return;
00203             }
00204         };
00205     }
00206 
00207     void Pool::printStatistics() {
00208         FL_LOG(_log, LMsg("Pool not loaded =") << getResourceCount(RES_NON_LOADED));
00209         FL_LOG(_log, LMsg("Pool loaded     =") << getResourceCount(RES_LOADED));
00210         int amount = 0;
00211         std::vector<PoolEntry*>::iterator entry;
00212         for (entry = m_entries.begin(); entry != m_entries.end(); entry++) {
00213             if ((*entry)->resource) {
00214                 if ((*entry)->resource->getRefCount() > 0) {
00215                     amount++;
00216                 }
00217             }
00218         }
00219         FL_LOG(_log, LMsg("Pool locked     =") << amount);
00220         FL_LOG(_log, LMsg("Pool total size =") << m_entries.size());
00221     }
00222 
00223     void Pool::sanityCheck() {
00224         // It is easy to mess up the important consistent
00225         // less-than operator for the location classes.
00226         // This will check if according to the '==' operator 
00227         // entries are duplicate (=memory leaks).
00228         // Slow and inaccurate. But should barf at real messups.
00229         for(unsigned i = 0; i != m_entries.size(); ++i) {
00230             int count = 0;
00231             for(unsigned j = i+1; j < m_entries.size(); ++j) {
00232                 if( *m_entries[i]->location == *m_entries[j]->location )
00233                     count ++;
00234             }
00235             if( 0 == count )
00236                 continue;
00237             FL_WARN(_log, LMsg("Multiple entries: ") << m_entries[i]->location->getFilename()
00238               << " #entries = " << (count+1) );
00239         }
00240     }
00241 
00242 }