#include #include #include #include #include #include #include #include #include #include // // New Cache Implementation // template class CacheBase; template class CacheObject { friend class CacheBase; public: CacheObject() : pCache( NULL ), bChanged( false ) { } virtual ~CacheObject() { } typedef CacheBase CacheType; virtual keytype getKey() const=0; virtual int getPersistenceScore() const { return 0; } void changed( bool bChanged=true ) { if( this->bChanged == false && bChanged == true ) pCache->objectChanged( getKey() ); this->bChanged = bChanged; } private: void setCache( CacheType *pCache ) { this->pCache = pCache; } private: CacheType *pCache; bool bChanged; }; template class CachePtr { friend class CacheBase; private: CachePtr( CacheBase *pCache, obtype *pData, const keytype &kId ) : pCache( pCache ), kId( kId ), pData( pData ) { } CachePtr( CacheBase *pCache, const keytype &kId ) : pCache( pCache ), kId( kId ), pData( NULL ) { } public: CachePtr() : pCache( NULL ), pData( NULL ) { } virtual ~CachePtr() { } const keytype &getKey() const { return kId; } obtype &operator*() { checkRef(); return pData; } obtype *operator->() { checkRef(); return pData; } private: void checkRef() { if( pCache == NULL ) throw Bu::ExceptionBase("Invalid pointer"); if( !pData ) pData = pCache->getRef( kId ); } private: CacheBase *pCache; mutable keytype kId; mutable obtype *pData; }; template class CacheBase { friend class CachePtr; public: CacheBase() { } virtual ~CacheBase() { } typedef Bu::List KeyList; CachePtr insert( const obtype *pObject ) { CacheEntry *pEnt = addEntry( pObject ); pEnt->incRef(); return CachePtr( this, pEnt->getPtr(), pObject->getKey() ); } template CachePtr insert( const supertype *pObject ) { const obtype *pCast = dynamic_cast( pObject ); if( pCast == NULL ) throw std::bad_cast(); CacheEntry *pEnt = addEntry( pCast ); pEnt->incRef(); return CachePtr( this, pObject, pObject->getKey() ); } CachePtr get( const keytype &key ) { CacheEntry *pEnt = getEntry( key ); pEnt->incRef(); return CachePtr( this, pEnt->getPtr(), key ); } template CachePtr get( const keytype &key ) { CacheEntry *pEnt = getEntry( key ); pEnt->incRef(); supertype *pCast = dynamic_cast( pEnt->getPtr() ); if( pCast == NULL ) { pEnt->decRef(); throw std::bad_cast(); } return CachePtr( this, pCast, key ); } virtual KeyList getKeys() const=0; virtual void sync()=0; protected: obtype *getRef( const keytype &k ) { CacheEntry *pEnt = getEntry( k ); pEnt->incRef(); return pEnt->getPtr(); } void objectChanged( const keytype &k ) { } class CacheEntry { public: CacheEntry( obtype *pObject ) : iRefCount( 0 ), pObject( pObject ) { } void incRef() { mEntry.lock(); iRefCount++; mEntry.unlock(); } bool decRef() { mEntry.lock(); iRefCount--; bool bRet = iRefCount > 0; mEntry.unlock(); return bRet; } obtype *getPtr() { return pObject; } private: Bu::Mutex mEntry; int iRefCount; obtype *pObject; }; 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; private: CacheEntry *addEntry( const obtype *pObject ) { CacheEntry *pEnt = new CacheEntry( const_cast(pObject) ); mCacheEntry.lockWrite(); hCacheEntry.insert( pObject->getKey(), pEnt ); mCacheEntry.unlockWrite(); _create( pObject ); return pEnt; } CacheEntry *getEntry( const keytype &k ) { CacheEntry *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 CacheEntry( pObject ); Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry ); hCacheEntry.insert( k, pEnt ); } return pEnt; } private: typedef Bu::Hash CacheEntryHash; CacheEntryHash hCacheEntry; Bu::ReadWriteMutex mCacheEntry; }; template class MyriadCache : public CacheBase { public: MyriadCache( Bu::Stream &sStore, int iBlockSize=512, int iPreallocate=8 ) : sStore( sStore ), mStore( sStore, iBlockSize, iPreallocate ) { Bu::ReadWriteMutex::WriteLocker wl( rwStore ); try { Bu::MyriadStream ms = mStore.openStream( 1 ); Bu::Archive ar( ms, Bu::Archive::load ); ar >> hIndex; } catch(...) { if( mStore.createStreamWithId( 1 ) != 1 ) throw Bu::ExceptionBase("Error creating index stream."); Bu::MyriadStream ms = mStore.openStream( 1 ); Bu::Archive ar( ms, Bu::Archive::save ); ar << hIndex; } } virtual ~MyriadCache() { sync(); } using typename CacheBase::KeyList; virtual KeyList getKeys() const { Bu::ReadWriteMutex::ReadLocker rl( rwStore ); return hIndex.getKeys(); } virtual void sync() { Bu::ReadWriteMutex::ReadLocker wl( rwStore ); Bu::MyriadStream ms = mStore.openStream( 1 ); Bu::Archive ar( ms, Bu::Archive::save ); ar << hIndex; ar.close(); ms.setSize( ms.tell() ); } protected: virtual void _create( const obtype *o ) { hIndex.insert( o->getKey(), mStore.createStream() ); _save( o ); } virtual void _erase( const keytype &k ) { mStore.deleteStream( hIndex.get( k ) ); hIndex.erase( k ); } virtual obtype *_load( const keytype &k ) { Bu::MyriadStream ms = mStore.openStream( hIndex.get( k ) ); Bu::Archive ar( ms, Bu::Archive::load ); obtype *ret = new obtype(); ar >> *ret; return ret; } virtual void _save( const obtype *o ) { Bu::MyriadStream ms = mStore.openStream( hIndex.get( o->getKey() ) ); { Bu::Archive ar( ms, Bu::Archive::save ); ar << *o; } ms.setSize( ms.tell() ); } private: Bu::Stream &sStore; Bu::Myriad mStore; Bu::Hash hIndex; mutable Bu::ReadWriteMutex rwStore; }; // // Test // class Something : public CacheObject { friend Bu::ArchiveBase &operator>>( Bu::ArchiveBase &ar, Something &s ); friend Bu::ArchiveBase &operator<<( Bu::ArchiveBase &ar, const Something &s ); public: Something() { } Something( const Bu::String &sName ) : uId( Bu::Uuid::generate() ), sName( sName ) { } virtual ~Something() { } virtual Bu::Uuid getKey() const { return uId; } Bu::String getName() { return sName; } private: Bu::Uuid uId; Bu::String sName; }; Bu::ArchiveBase &operator>>( Bu::ArchiveBase &ar, Something &s ) { return ar >> s.uId >> s.sName; } Bu::ArchiveBase &operator<<( Bu::ArchiveBase &ar, const Something &s ) { return ar << s.uId << s.sName; } typedef CachePtr SomethingPtr; typedef MyriadCache SomethingCache; int main() { Bu::File fStore("test.myr", Bu::File::Create|Bu::File::ReadWrite); SomethingCache c( fStore ); SomethingPtr ptr = c.insert( new Something("Bob") ); Bu::println("Something[%1]: %2").arg( ptr.getKey() ).arg(ptr->getName()); SomethingCache::KeyList lKeys = c.getKeys(); for( SomethingCache::KeyList::iterator i = lKeys.begin(); i; i++ ) { Bu::println(" - %1").arg( *i ); } return 0; }