#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 CacheBase; 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(); } 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() { release(); } const keytype &getKey() const { return kId; } obtype &operator*() { checkRef(); return pData; } const obtype &operator*() const { checkRef(); return pData; } obtype *operator->() { checkRef(); return pData; } const obtype *operator->() const { checkRef(); return pData; } MyType operator=( const MyType &rhs ) { release(); pCache = rhs.pCache; kId = rhs.kId; pEnt = rhs.pEnt; pData = rhs.pData; pEnt->incRef(); return *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 release() { CachePtrInterface::releaseRef( pCache, pEnt, pData ); } void lock() { checkRef(); if( pEnt ) pEnt->lock(); } void unlock() { checkRef(); 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: void checkRef() { CachePtrInterface::checkRef( pCache, kId, pEnt, pData ); } private: CacheBase *pCache; mutable keytype kId; mutable CacheEntry *pEnt; mutable obtype *pData; }; template class CacheBase { friend class CachePtrInterface; public: CacheBase() { } virtual ~CacheBase() { } typedef CacheEntry Entry; typedef Bu::List KeyList; CachePtr insert( const obtype *pObject ) { Entry *pEnt = addEntry( pObject ); return CachePtr( this, pEnt, pObject->getKey() ); } template CachePtr insert( const supertype *pObject ) { const 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 ); } virtual KeyList getKeys() const=0; virtual void sync()=0; void _debug() { Bu::ReadWriteMutex::ReadLocker rl( mCacheEntry ); Bu::println("Cache entries:"); for( typename CacheEntryHash::iterator i = hCacheEntry.begin(); i; i++ ) { Bu::println(" %1: refs=%2"). arg( i.getKey() ). arg( i.getValue()->getRefCount() ); } } protected: Entry *getRef( const keytype &k ) { Entry *pEnt = getEntry( k ); return pEnt; } void releaseRef( Entry * )//pEnt ) { } void objectChanged( const keytype &k ) { } 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: Entry *addEntry( const obtype *pObject ) { Entry *pEnt = new Entry( const_cast(pObject) ); mCacheEntry.lockWrite(); hCacheEntry.insert( pObject->getKey(), pEnt ); mCacheEntry.unlockWrite(); _create( pObject ); 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 ); 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 ) { try { Bu::ReadWriteMutex::ReadLocker l( rwStore ); Bu::MyriadStream ms = mStore.openStream( 1 ); Bu::Archive ar( ms, Bu::Archive::load ); uint8_t uVer; ar >> uVer; switch( uVer ) { case 0: ar >> hIndex; break; } } catch(...) { if( mStore.createStreamWithId( 1 ) != 1 ) throw Bu::ExceptionBase("Error creating index stream."); sync(); } } 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 << (uint8_t)0 << 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( int argc, char *argv[] ) { Bu::File fStore("test.myr", Bu::File::Create|Bu::File::ReadWrite); SomethingCache c( fStore ); for( int j = 1; j < argc; j++ ) { SomethingPtr ptr = c.insert( new Something(argv[j]) ); 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: '%2'").arg( *i ).arg( c.get( *i )->getName() ); } c._debug(); SomethingPtr p2; SomethingPtr p1( c.get( lKeys.first() ) ); c._debug(); { SomethingPtr p2( p1 ); c._debug(); } c._debug(); p2 = p1; c._debug(); p1.release(); c._debug(); Bu::println("Name: %1").arg( p1->getName() ); p1.release(); p1.lock(); p1.unlock(); c._debug(); SomethingPtr p3 = c.getLazy( lKeys.first() ); c._debug(); SomethingPtr::Locker l( p3 ); Bu::println("Name again: %1").arg( p3->getName() ); c._debug(); return 0; }