#ifndef __TAGGEDCACHE__ #define __TAGGEDCACHE__ #include #include #include // This class implemented a cache and a map. The cache keeps objects alive // in the map. The map allows multiple code paths that reference objects // with the same tag to get the same actual object. template class TaggedCache { public: typedef c_Key key_type; typedef c_Data data_type; typedef boost::weak_ptr weak_data_ptr; typedef boost::shared_ptr data_ptr; typedef std::pair cache_entry; protected: mutable boost::recursive_mutex mLock; int mTargetSize, mTargetAge; std::map mCache; // Hold strong reference to recent objects time_t mLastSweep; std::map mMap; // Track stored objects public: TaggedCache(int size, int age) : mTargetSize(size), mTargetAge(age), mLastSweep(time(NULL)) { ; } int getTargetSize() const; int getTargetAge() const; int getCacheSize(); int getSweepAge(); void setTargetSize(int size); void setTargetAge(int age); void sweep(); bool touch(const key_type& key); bool del(const key_type& key); bool canonicalize(const key_type& key, boost::shared_ptr& data); boost::shared_ptr fetch(const key_type& key); boost::recursive_mutex& peekMutex() { return mLock; } }; template int TaggedCache::getTargetSize() const { mLock.lock(); int ret=mTargetSize; mLock.unlock(); return ret; } template int TaggedCache::getTargetAge() const { mLock.lock(); int ret=mTargetAge; mLock.unlock(); return ret; } template int TaggedCache::getCacheSize() { boost::recursive_mutex::scoped_lock sl(mLock); return mCache.size(); } template void TaggedCache::sweep() { boost::recursive_mutex::scoped_lock sl(mLock); if(mCache.size()::iterator cit=mCache.begin(); while(cit!=mCache.end()) { if(cit->second->second.first::iterator cit2=cit; ++cit; mCache.erase(cit2); } else ++cit; } // Pass 2, remove dead objects from map typename std::map::iterator mit=mMap.begin(); while(mit!=mMap.end()) { if(mit->second->expired()) { typename std::map::iterator mit2=mit; ++mit; mMap.erase(mit2); } else ++mit; } } template bool TaggedCache::touch(const key_type& key) { // If present, make current in cache boost::recursive_mutex::scoped_lock sl(mLock); // Is the object in the map? typename std::map::iterator mit=mMap.find(key); if(mit==mMap.end()) return false; if(mit->second->expired()) { // in map, but expired mMap.erase(mit); return false; } // Is the object in the cache? typename std::map::iterator cit=mCache.find(key); if(cit!=mCache.end()) { // in both map and cache cit->second->first=time(NULL); return true; } // In map but not cache, put in cache mCache.insert(std::make_pair(key, std::make_pair(time(NULL), weak_data_ptr(cit->second->second)))); return true; } template bool TaggedCache::del(const key_type& key) { // Remove from cache, map unaffected boost::recursive_mutex::scoped_lock sl(mLock); typename std::map::iterator cit=mCache.find(key); if(cit==mCache.end()) return false; mCache.erase(cit); return true; } template bool TaggedCache::canonicalize(const key_type& key, boost::shared_ptr& data) { // Return canonical value, store if needed, refresh in cache // Return values: true=we had the data already boost::recursive_mutex::scoped_lock sl(mLock); typename std::map::iterator mit=mMap.find(key); if(mit==mMap.end()) { // not in map mCache.insert(std::make_pair(key, std::make_pair(time(NULL), data))); mMap.insert(std::make_pair(key, data)); return false; } if(mit->second.expired()) { // in map, but expired mit->second=data; mCache.insert(std::make_pair(key, std::make_pair(time(NULL), data))); return false; } data=mit->second.lock(); assert(!!data); // Valid in map, is it in cache? typename std::map::iterator cit=mCache.find(key); if(cit!=mCache.end()) cit->second.first=time(NULL); // Yes, refesh else // no, add to cache mCache.insert(std::make_pair(key, std::make_pair(time(NULL), data))); return true; } template boost::shared_ptr TaggedCache::fetch(const key_type& key) { boost::recursive_mutex::scoped_lock sl(mLock); // Is it in the map? typename std::map::iterator mit=mMap.find(key); if(mit==mMap.end()) return data_ptr(); // No, we're done if(mit->second.expired()) { // in map, but expired mMap.erase(mit); return data_ptr(); } boost::shared_ptr data=mit->second.lock(); // Valid in map, is it in the cache? typename std::map::iterator cit=mCache.find(key); if(cit!=mCache.end()) cit->second.first=time(NULL); // Yes, refresh else // No, add to cache mCache.insert(std::make_pair(key, std::make_pair(time(NULL), data))); return data; } #endif