From 0ac226c809b6495d5dbc09c2f847638e10aca315 Mon Sep 17 00:00:00 2001
From: Mike Buland <eichlan@xagasoft.com>
Date: Wed, 13 Mar 2013 02:50:06 +0000
Subject: The new caching system is almost there.  It's more elaborate
 internally, and it works very, very differently, but the interface is proving
 much easier to use and it should be much faster, not to mention thread-safe.

---
 src/tests/cache.cpp | 419 +++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 335 insertions(+), 84 deletions(-)

(limited to 'src')

diff --git a/src/tests/cache.cpp b/src/tests/cache.cpp
index 6fc4e4a..15662af 100644
--- a/src/tests/cache.cpp
+++ b/src/tests/cache.cpp
@@ -55,22 +55,148 @@ private:
     bool bChanged;
 };
 
+template<typename keytype, typename obtype>
+class CacheBase;
+    
+template<typename keytype, typename obtype>
+class CacheEntry
+{
+friend class CacheBase<keytype, obtype>;
+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<typename keytype, typename basetype>
+class CachePtrInterface
+{
+protected:
+    CachePtrInterface()
+    {
+    }
+
+    virtual ~CachePtrInterface()
+    {
+    }
+    
+    template<typename obtype>
+    void checkRef( CacheBase<keytype, basetype> *pCache,
+            const keytype &kId,
+            CacheEntry<keytype, basetype> * &rpEnt,
+            obtype * &rpData )
+    {
+        if( pCache == NULL )
+            throw Bu::ExceptionBase("Invalid pointer");
+
+        if( !rpData )
+        {
+            rpEnt = pCache->getRef( kId );
+            rpEnt->incRef();
+            rpData = dynamic_cast<obtype *>(rpEnt->getPtr());
+            if( rpData == NULL )
+            {
+                rpEnt->decRef();
+                rpEnt = NULL;
+                throw std::bad_cast();
+            }
+        }
+    }
+
+    template<typename obtype>
+    void releaseRef( CacheBase<keytype, basetype> *pCache,
+            CacheEntry<keytype, basetype> * &rpEnt,
+            obtype * &rpData )
+    {
+        if( pCache == NULL )
+            return;
+
+        if( rpData == NULL )
+            return;
+
+        rpData = NULL;
+        rpEnt->decRef();
+        pCache->releaseRef( rpEnt );
+        rpEnt = NULL;
+    }
+};
+
 template<typename keytype, typename obtype, typename basetype=obtype>
-class CachePtr
+class CachePtr : protected CachePtrInterface<keytype, basetype>
 {
 friend class CacheBase<keytype, basetype>;
 private:
-    CachePtr( CacheBase<keytype, basetype> *pCache, obtype *pData,
+    typedef CachePtr<keytype, obtype, basetype> MyType;
+
+    CachePtr( CacheBase<keytype, basetype> *pCache,
+            CacheEntry<keytype, basetype> *pEnt,
             const keytype &kId ) :
         pCache( pCache ),
         kId( kId ),
-        pData( pData )
+        pEnt( pEnt ),
+        pData( NULL )
     {
+        pEnt->incRef();
+        pData = dynamic_cast<obtype *>( pEnt->getPtr() );
+        if( pData == NULL )
+        {
+            pEnt->decRef();
+            throw std::bad_cast();
+        }
     }
 
     CachePtr( CacheBase<keytype, basetype> *pCache, const keytype &kId ) :
         pCache( pCache ),
         kId( kId ),
+        pEnt( NULL ),
         pData( NULL )
     {
     }
@@ -78,12 +204,23 @@ private:
 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
@@ -96,34 +233,113 @@ public:
         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<keytype, basetype>::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()
     {
-        if( pCache == NULL )
-            throw Bu::ExceptionBase("Invalid pointer");
-
-        if( !pData )
-            pData = pCache->getRef( kId );
+        CachePtrInterface<keytype, basetype>::checkRef( pCache, kId, pEnt, pData );
     }
 
 private:
     CacheBase<keytype, basetype> *pCache;
     mutable keytype kId;
+    mutable CacheEntry<keytype, basetype> *pEnt;
     mutable obtype *pData;
-
 };
 
 template<typename keytype, typename obtype>
 class CacheBase
 {
-friend class CachePtr<keytype, obtype>;
+friend class CachePtrInterface<keytype, obtype>;
 public:
     CacheBase()
     {
@@ -133,14 +349,17 @@ public:
     {
     }
 
+    typedef CacheEntry<keytype, obtype> Entry;
     typedef Bu::List<keytype> KeyList;
 
+    
     CachePtr<keytype, obtype> insert( const obtype *pObject )
     {
-        CacheEntry *pEnt = addEntry( pObject );
+        Entry *pEnt = addEntry( pObject );
 
-        pEnt->incRef();
-        return CachePtr<keytype, obtype>( this, pEnt->getPtr(), pObject->getKey() );
+        return CachePtr<keytype, obtype>(
+            this, pEnt, pObject->getKey()
+            );
     }
 
     template<typename supertype>
@@ -150,84 +369,68 @@ public:
         if( pCast == NULL )
             throw std::bad_cast();
 
-        CacheEntry *pEnt = addEntry( pCast );
+        Entry *pEnt = addEntry( pCast );
 
-        pEnt->incRef();
-        return CachePtr<keytype, supertype, obtype>( this, pObject, pObject->getKey() );
+        return CachePtr<keytype, supertype, obtype>(
+            this, pEnt, pObject->getKey()
+            );
     }
 
     CachePtr<keytype, obtype> get( const keytype &key )
     {
-        CacheEntry *pEnt = getEntry( key );
-        pEnt->incRef();
-        return CachePtr<keytype, obtype>( this, pEnt->getPtr(), key );
+        Entry *pEnt = getEntry( key );
+        return CachePtr<keytype, obtype>( this, pEnt, key );
     }
 
     template<typename supertype>
     CachePtr<keytype, supertype, obtype> get( const keytype &key )
     {
-        CacheEntry *pEnt = getEntry( key );
-        pEnt->incRef();
-        supertype *pCast = dynamic_cast<supertype *>( pEnt->getPtr() );
-        if( pCast == NULL )
-        {
-            pEnt->decRef();
-            throw std::bad_cast();
-        }
-        return CachePtr<keytype, supertype, obtype>( this, pCast, key );
+        Entry *pEnt = getEntry( key );
+        return CachePtr<keytype, supertype, obtype>( this, pEnt, key );
     }
 
-    virtual KeyList getKeys() const=0;
-
-    virtual void sync()=0;
-
-protected:
-    obtype *getRef( const keytype &k )
+    CachePtr<keytype, obtype> getLazy( const keytype &key )
     {
-        CacheEntry *pEnt = getEntry( k );
-        pEnt->incRef();
-        return pEnt->getPtr();
+        return CachePtr<keytype, obtype>( this, key );
     }
 
-    void objectChanged( const keytype &k )
+    template<typename supertype>
+    CachePtr<keytype, supertype, obtype> getLazy( const keytype &key )
     {
+        return CachePtr<keytype, supertype, obtype>( this, key );
     }
 
-    class CacheEntry
+    virtual KeyList getKeys() const=0;
+
+    virtual void sync()=0;
+
+    void _debug()
     {
-    public:
-        CacheEntry( obtype *pObject ) :
-            iRefCount( 0 ),
-            pObject( pObject )
+        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() );
         }
+    }
 
-        void incRef()
-        {
-            mEntry.lock();
-            iRefCount++;
-            mEntry.unlock();
-        }
+protected:
+    Entry *getRef( const keytype &k )
+    {
+        Entry *pEnt = getEntry( k );
+        return pEnt;
+    }
 
-        bool decRef()
-        {
-            mEntry.lock();
-            iRefCount--;
-            bool bRet = iRefCount > 0;
-            mEntry.unlock();
-            return bRet;
-        }
+    void releaseRef( Entry * )//pEnt )
+    {
+    }
 
-        obtype *getPtr()
-        {
-            return pObject;
-        }
+    void objectChanged( const keytype &k )
+    {
+    }
 
-    private:
-        Bu::Mutex mEntry;
-        int iRefCount;
-        obtype *pObject;
-    };
 
 protected:
     virtual void _create( const obtype *o )=0;
@@ -237,9 +440,9 @@ protected:
     virtual void _save( const obtype *o )=0;
 
 private:
-    CacheEntry *addEntry( const obtype *pObject )
+    Entry *addEntry( const obtype *pObject )
     {
-        CacheEntry *pEnt = new CacheEntry( const_cast<obtype *>(pObject) );
+        Entry *pEnt = new Entry( const_cast<obtype *>(pObject) );
         mCacheEntry.lockWrite();
         hCacheEntry.insert( pObject->getKey(), pEnt );
         mCacheEntry.unlockWrite();
@@ -248,9 +451,9 @@ private:
         return pEnt;
     }
 
-    CacheEntry *getEntry( const keytype &k )
+    Entry *getEntry( const keytype &k )
     {
-        CacheEntry *pEnt = NULL;
+        Entry *pEnt = NULL;
         try
         {
             Bu::ReadWriteMutex::ReadLocker rl( mCacheEntry );
@@ -260,7 +463,7 @@ private:
         {
             // try to load the object from the backing store
             obtype *pObject = _load( k );
-            pEnt = new CacheEntry( pObject );
+            pEnt = new Entry( pObject );
             Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
             hCacheEntry.insert( k, pEnt );
         }
@@ -268,10 +471,9 @@ private:
     }
 
 private:
-    typedef Bu::Hash<keytype, CacheEntry *> CacheEntryHash;
+    typedef Bu::Hash<keytype, Entry *> CacheEntryHash;
     CacheEntryHash hCacheEntry;
     Bu::ReadWriteMutex mCacheEntry;
-
 };
 
 template<typename keytype, typename obtype>
@@ -282,21 +484,26 @@ public:
         sStore( sStore ),
         mStore( sStore, iBlockSize, iPreallocate )
     {
-        Bu::ReadWriteMutex::WriteLocker wl( rwStore );
         try
         {
+            Bu::ReadWriteMutex::ReadLocker l( rwStore );
             Bu::MyriadStream ms = mStore.openStream( 1 );
             Bu::Archive ar( ms, Bu::Archive::load );
-            ar >> hIndex;
+            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.");
 
-            Bu::MyriadStream ms = mStore.openStream( 1 );
-            Bu::Archive ar( ms, Bu::Archive::save );
-            ar << hIndex;
+            sync();
         }
     }
 
@@ -318,7 +525,7 @@ public:
         Bu::ReadWriteMutex::ReadLocker wl( rwStore );
         Bu::MyriadStream ms = mStore.openStream( 1 );
         Bu::Archive ar( ms, Bu::Archive::save );
-        ar << hIndex;
+        ar << (uint8_t)0 << hIndex;
         ar.close();
         ms.setSize( ms.tell() );
     }
@@ -413,21 +620,65 @@ Bu::ArchiveBase &operator<<( Bu::ArchiveBase &ar, const Something &s )
 typedef CachePtr<Bu::Uuid, Something> SomethingPtr;
 typedef MyriadCache<Bu::Uuid, Something> SomethingCache;
 
-int main()
+int main( int argc, char *argv[] )
 {
     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());
+    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").arg( *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;
 }
-- 
cgit v1.2.3