From fb5176bbd5355b02b7d0e65da3ef3f0105824cd0 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Sun, 17 Mar 2013 23:45:21 +0000 Subject: The new cache system has been broken out into it's individual headers, and is now ready for actual use. --- src/unstable/cachebase.h | 514 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 514 insertions(+) create mode 100644 src/unstable/cachebase.h (limited to 'src/unstable/cachebase.h') diff --git a/src/unstable/cachebase.h b/src/unstable/cachebase.h new file mode 100644 index 0000000..807adf0 --- /dev/null +++ b/src/unstable/cachebase.h @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2007-2013 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_CACHE_BASE_H +#define BU_CACHE_BASE_H + +#include "bu/string.h" +#include "bu/archive.h" +#include "bu/hash.h" +#include "bu/readwritemutex.h" +#include "bu/mutexlocker.h" +#include "bu/cacheobject.h" + +namespace Bu +{ + template class CacheBase; + template class CacheEntry; + + template + class CacheEntry + { + friend class CacheBase; + private: + CacheEntry( obtype *pObject ) : + iRefCount( 0 ), + pObject( pObject ) + { + } + + public: + int getRefCount() const + { + mEntry.lock(); + int ret = iRefCount; + mEntry.unlock(); + return ret; + } + + obtype *getPtr() const + { + return pObject; + } + + void lock() const + { + mObject.lock(); + } + + void unlock() const + { + mObject.unlock(); + } + + Bu::Mutex &getMutex() + { + return mObject; + } + + void incRef() + { + mEntry.lock(); + iRefCount++; + mEntry.unlock(); + } + + bool decRef() + { + mEntry.lock(); + iRefCount--; + bool bRet = iRefCount > 0; + mEntry.unlock(); + return bRet; + } + + private: + mutable Bu::Mutex mEntry; + mutable Bu::Mutex mObject; + int iRefCount; + obtype *pObject; + }; + + template + class CachePtrInterface + { + protected: + CachePtrInterface() + { + } + + virtual ~CachePtrInterface() + { + } + + template + void checkRef( CacheBase *pCache, + const keytype &kId, + CacheEntry * &rpEnt, + obtype * &rpData ) + { + if( pCache == NULL ) + throw Bu::ExceptionBase("Invalid pointer"); + + if( !rpData ) + { + rpEnt = pCache->getRef( kId ); + rpEnt->incRef(); + rpData = dynamic_cast(rpEnt->getPtr()); + if( rpData == NULL ) + { + rpEnt->decRef(); + rpEnt = NULL; + throw std::bad_cast(); + } + } + } + + template + void releaseRef( CacheBase *pCache, + CacheEntry * &rpEnt, + obtype * &rpData ) + { + if( pCache == NULL ) + return; + + if( rpData == NULL ) + return; + + rpData = NULL; + rpEnt->decRef(); + pCache->releaseRef( rpEnt ); + rpEnt = NULL; + } + }; + + template + class CachePtr : protected CachePtrInterface + { + friend class CacheBase; + private: + typedef CachePtr MyType; + + CachePtr( CacheBase *pCache, + CacheEntry *pEnt, + const keytype &kId ) : + pCache( pCache ), + kId( kId ), + pEnt( pEnt ), + pData( NULL ) + { + pEnt->incRef(); + pData = dynamic_cast( pEnt->getPtr() ); + if( pData == NULL ) + { + pEnt->decRef(); + throw std::bad_cast(); + } + } + + CachePtr( CacheBase *pCache, const keytype &kId ) : + pCache( pCache ), + kId( kId ), + pEnt( NULL ), + pData( NULL ) + { + } + + public: + CachePtr() : + pCache( NULL ), + pEnt( NULL ), + pData( NULL ) + { + } + + CachePtr( const MyType &rhs ) : + pCache( rhs.pCache ), + kId( rhs.kId ), + pEnt( rhs.pEnt ), + pData( rhs.pData ) + { + pEnt->incRef(); + } + + virtual ~CachePtr() + { + unbind(); + } + + const keytype &getKey() const + { + return kId; + } + + obtype &operator*() + { + bind(); + return pData; + } + + const obtype &operator*() const + { + bind(); + return pData; + } + + obtype *operator->() + { + bind(); + return pData; + } + + const obtype *operator->() const + { + bind(); + return pData; + } + + MyType operator=( const MyType &rhs ) + { + unbind(); + pCache = rhs.pCache; + kId = rhs.kId; + pEnt = rhs.pEnt; + pData = rhs.pData; + pEnt->incRef(); + + return *this; + } + + template + CachePtr cast() + { + return pCache->cast( *this ); + } + + bool operator==( const MyType &rhs ) const + { + return pCache == rhs.pCache && + kId == rhs.kId; + } + + bool operator!=( const MyType &rhs ) const + { + return pCache != rhs.pCache || + kId != rhs.kId; + } + + void bind() + { + CachePtrInterface::checkRef( pCache, kId, pEnt, pData ); + } + + void unbind() + { + CachePtrInterface::releaseRef( pCache, pEnt, pData ); + } + + void lock() + { + bind(); + if( pEnt ) + pEnt->lock(); + } + + void unlock() + { + bind(); + if( pEnt ) + pEnt->unlock(); + } + + class Locker + { + public: + Locker( MyType &rPtr ) : + rPtr( rPtr ), + bLocked( true ) + { + rPtr.lock(); + } + + ~Locker() + { + unlock(); + } + + void unlock() + { + if( !bLocked ) + return; + rPtr.unlock(); + bLocked = false; + } + + private: + MyType &rPtr; + bool bLocked; + }; + + private: + CacheBase *pCache; + mutable keytype kId; + mutable CacheEntry *pEnt; + mutable obtype *pData; + }; + + template + class CacheBase + { + friend class CachePtrInterface; + friend class CacheObject; + public: + CacheBase() + { + } + + virtual ~CacheBase() + { + Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry ); + syncChanges(); + } + + typedef CacheEntry Entry; + typedef Bu::List KeyList; + + CachePtr insert( obtype *pObject ) + { + Entry *pEnt = addEntry( pObject ); + + return CachePtr( + this, pEnt, pObject->getKey() + ); + } + + template + CachePtr insert( supertype *pObject ) + { + obtype *pCast = dynamic_cast( pObject ); + if( pCast == NULL ) + throw std::bad_cast(); + + Entry *pEnt = addEntry( pCast ); + + return CachePtr( + this, pEnt, pObject->getKey() + ); + } + + CachePtr get( const keytype &key ) + { + Entry *pEnt = getEntry( key ); + return CachePtr( this, pEnt, key ); + } + + template + CachePtr get( const keytype &key ) + { + Entry *pEnt = getEntry( key ); + return CachePtr( this, pEnt, key ); + } + + CachePtr getLazy( const keytype &key ) + { + return CachePtr( this, key ); + } + + template + CachePtr getLazy( const keytype &key ) + { + return CachePtr( this, key ); + } + + template + CachePtr cast( CachePtr &ptr ) + { + if( ptr.pEnt ) + return CachePtr( this, ptr.pEnt, ptr.kId ); + else + return CachePtr( this, ptr.kId ); + } + + void erase( const keytype &key ) + { + Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry ); + if( hCacheEntry.has( key ) ) + { + Entry *pEnt = hCacheEntry.get( key ); + pEnt->mEntry.lock(); + if( pEnt->iRefCount > 0 ) + { + int iCount = pEnt->iRefCount; + pEnt->mEntry.unlock(); + throw Bu::ExceptionBase( Bu::String("Cache entry %1 cannot be erased, there are %2 active references.").arg( key ).arg( iCount ).end().getStr() ); + } + delete pEnt->pObject; + delete pEnt; + hCacheEntry.erase( key ); + } + _erase( key ); + } + + virtual KeyList getKeys() const=0; + virtual int getSize() const=0; + + void sync() + { + Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry ); + _sync(); + syncChanges(); + } + + protected: + Entry *getRef( const keytype &k ) + { + Entry *pEnt = getEntry( k ); + return pEnt; + } + + void releaseRef( Entry * )//pEnt ) + { + } + + void objectChanged( const keytype &k ) + { + Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry ); + hChanged.insert( k, true ); + } + + protected: + virtual void _create( const obtype *o )=0; + virtual void _erase( const keytype &k )=0; + + virtual obtype *_load( const keytype &k )=0; + virtual void _save( const obtype *o )=0; + + virtual void _sync()=0; + + private: + Entry *addEntry( obtype *pObject ) + { + Entry *pEnt = new Entry( pObject ); + mCacheEntry.lockWrite(); + hCacheEntry.insert( pObject->getKey(), pEnt ); + mCacheEntry.unlockWrite(); + _create( pObject ); + pObject->setCache( this, pEnt ); + + return pEnt; + } + + Entry *getEntry( const keytype &k ) + { + Entry *pEnt = NULL; + try + { + Bu::ReadWriteMutex::ReadLocker rl( mCacheEntry ); + pEnt = hCacheEntry.get( k ); + } + catch(...) + { + // try to load the object from the backing store + obtype *pObject = _load( k ); + pEnt = new Entry( pObject ); + pObject->setCache( this, pEnt ); + Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry ); + hCacheEntry.insert( k, pEnt ); + } + return pEnt; + } + + void syncChanges() + { + if( !hChanged.isEmpty() ) + { + for( typename CacheKeySet::iterator i = hChanged.begin(); i; i++ ) + { + Entry *pEnt = hCacheEntry.get( i.getKey() ); + Bu::MutexLocker ml( pEnt->getMutex() ); + _save( pEnt->getPtr() ); + } + hChanged.clear(); + } + } + + private: + typedef Bu::Hash CacheEntryHash; + typedef Bu::Hash CacheKeySet; + CacheEntryHash hCacheEntry; + CacheKeySet hChanged; + Bu::ReadWriteMutex mCacheEntry; + }; + + template + void _cacheObjectSave( Bu::Stream &s, obtype *pObject ) + { + Bu::Archive ar( s, Bu::Archive::save ); + ar << *pObject; + } + + template + obtype *_cacheObjectLoad( Bu::Stream &s ) + { + Bu::Archive ar( s, Bu::Archive::load ); + obtype *ret = new obtype(); + ar >> *ret; + return ret; + } +} + +#endif -- cgit v1.2.3