/* Copyright (C) 2000 Stefan Westerfeld stefan@space.twc.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "cache.h" #include "debug.h" #include "iomanager.h" #include "dispatcher.h" #include #include using namespace std; using namespace Arts; bool CachedObject::isValid() { return true; } void CachedObject::setKey(string key) { _object_key = key; } string CachedObject::getKey() { return _object_key; } void CachedObject::decRef() { _ref_cnt--; time(&_lastAccess); } void CachedObject::incRef() { _ref_cnt++; } int CachedObject::refCnt() { return _ref_cnt; } time_t CachedObject::lastAccess() { return(_lastAccess); } CachedObject::CachedObject(Cache *cache) { _ref_cnt = 1; cache->add(this); } CachedObject::~CachedObject() { assert(_ref_cnt == 0); } //------------------------- Cache implementation --------------------------- long Cache::memused = 0; CachedObject *Cache::get(string key) { list::iterator i; for(i=objects.begin();i != objects.end(); i++) { if((*i)->getKey() == key && (*i)->isValid()) { (*i)->incRef(); return(*i); } } return 0; } void Cache::add(CachedObject *object) { objects.push_back(object); } long Cache::cleanUp(long cacheLimit) { time_t lastAccess; list::iterator i; long memory = 0; // delete all invalid unused entries (invalid entries that are still // in use, e.g. cached wav files which have changed on disk but are // still played can't be deleted!) for(i=objects.begin();i != objects.end(); i++) { CachedObject *co = (*i); if(co->refCnt() == 0 && !co->isValid()) { objects.remove(co); delete co; i = objects.begin(); } } for(i=objects.begin();i != objects.end(); i++) { memory += (*i)->memoryUsage(); } bool freeok = true; while(memory > cacheLimit && freeok) { CachedObject *freeme; freeok = false; // only start freeing objects which have not been accessed // in the last 5 seconds time(&lastAccess); lastAccess -= 5; for(i=objects.begin();!freeok && (i != objects.end()); i++) { CachedObject *co = (*i); assert(co->refCnt() >= 0); if(co->refCnt() == 0 && (co->lastAccess() < lastAccess)) { lastAccess = co->lastAccess(); freeme = co; freeok = true; } else { //artsdebug("%d => %ld\n",co->refCnt(),co->lastAccess()); } } if(freeok) { memory -= freeme->memoryUsage(); objects.remove(freeme); delete(freeme); } else { //artsdebug("cache problem: memory overused, but nothing there to free\n"); } } memused = memory/1024; return(memory); } Cache *Cache::_instance = 0; Cache::Cache() { assert(!_instance); _instance = this; } Cache::~Cache() { list::iterator i; for(i=objects.begin(); i != objects.end(); i++) delete (*i); objects.clear(); assert(_instance); _instance = 0; } Cache *Cache::the() { if(!_instance) _instance = new Cache(); return _instance; } void Cache::shutdown() { if(_instance) { list::iterator i; long rcnt = 0; for(i=_instance->objects.begin(); i != _instance->objects.end(); i++) rcnt += (*i)->refCnt(); if(rcnt == 0) { delete _instance; _instance = 0; } else { arts_warning("cache shutdown while still active objects in cache"); } } } namespace Arts { // internal helpers // periodic cache clean class CacheClean : public TimeNotify { public: CacheClean(); void notifyTime(); virtual ~CacheClean(); }; // cache startup & shutdown class CacheStartup :public StartupClass { public: void startup(); void shutdown(); private: CacheClean *cacheClean; }; } CacheClean::CacheClean() { Dispatcher::the()->ioManager()->addTimer(5000, this); } void CacheClean::notifyTime() { // TODO: make this configurable Cache::the()->cleanUp(8192*1024); } CacheClean::~CacheClean() { Dispatcher::the()->ioManager()->removeTimer(this); } void CacheStartup::startup() { cacheClean = new CacheClean; } void CacheStartup::shutdown() { delete cacheClean; Cache::shutdown(); } static CacheStartup cacheStartup;