From 21a4337dc2f969dc3ec81e6ba3170119bcb67016 Mon Sep 17 00:00:00 2001
From: Mike Buland <eichlan@xagasoft.com>
Date: Tue, 22 Jul 2014 16:39:01 +0000
Subject: Deferred erase now works on cache entries.  You can erase a cache
 entry while it still has active references, and it will be safely cleaned up
 when the last reference is released.

---
 src/stable/array.h         |  24 ++++
 src/stable/file.cpp        |   9 +-
 src/stable/file.h          |   4 +
 src/tests/cachedel.cpp     | 288 +++++++++++++++++++++++++++++++++++++++++++++
 src/unstable/cachebase.h   |  76 ++++++++++--
 src/unstable/cacheobject.h |   3 +-
 6 files changed, 392 insertions(+), 12 deletions(-)
 create mode 100644 src/tests/cachedel.cpp

(limited to 'src')

diff --git a/src/stable/array.h b/src/stable/array.h
index a598865..a662705 100644
--- a/src/stable/array.h
+++ b/src/stable/array.h
@@ -297,6 +297,30 @@ namespace Bu
             return core->iSize;
         }
 
+        void setSize( long iNewLen, const value &initTo=value() )
+        {
+            if( core->iSize == iNewLen )
+                return;
+
+            _hardCopy();
+            if( iNewLen > core->iCapacity )
+            {
+                core->setCapacity( iNewLen );
+                for( int j = core->iSize; j < iNewLen; j++ )
+                {
+                    core->va.construct(
+                        &core->pData[j],
+                        initTo
+                        );
+                }
+                core->iSize = iNewLen;
+            }
+            else
+            {
+                core->iSize = iNewLen;
+            }
+        }
+
         /**
          * Get the capacity of the array.  This number will grow as data is
          * added, and is mainly for the curious, it doesn't really determine
diff --git a/src/stable/file.cpp b/src/stable/file.cpp
index b551d24..35933f1 100644
--- a/src/stable/file.cpp
+++ b/src/stable/file.cpp
@@ -240,7 +240,7 @@ void Bu::File::setSize( Bu::size iSize )
 
 Bu::size Bu::File::getSize() const
 {
-    struct stat st;
+    struct ::stat st;
     fstat( fd, &st );
     return st.st_size;
 }
@@ -250,7 +250,7 @@ Bu::size Bu::File::getBlockSize() const
 #ifdef WIN32
     return 4096;
 #else
-    struct stat st;
+    struct ::stat st;
     fstat( fd, &st );
     return st.st_blksize;
 #endif
@@ -261,6 +261,11 @@ Bu::String Bu::File::getLocation() const
     return "to be implemented";
 }
 
+void Bu::File::stat( struct ::stat *pStat )
+{
+    fstat( fd, pStat );
+}
+
 #ifndef WIN32
 void Bu::File::chmod( mode_t t )
 {
diff --git a/src/stable/file.h b/src/stable/file.h
index ad60e80..e3497d3 100644
--- a/src/stable/file.h
+++ b/src/stable/file.h
@@ -15,6 +15,8 @@
 #include "bu/string.h"
 #include "bu/exceptionbase.h"
 
+struct stat;
+
 namespace Bu
 {
     subExceptionDecl( FileException );
@@ -76,6 +78,8 @@ namespace Bu
         virtual size getBlockSize() const;
         virtual Bu::String getLocation() const;
 
+        void stat( struct ::stat *pStat );
+
         /**
          * Create a temp file and return its handle.  The file is opened 
          * Read/Write.
diff --git a/src/tests/cachedel.cpp b/src/tests/cachedel.cpp
new file mode 100644
index 0000000..817757c
--- /dev/null
+++ b/src/tests/cachedel.cpp
@@ -0,0 +1,288 @@
+#include "bu/myriadcache.h"
+#include "bu/uuid.h"
+#include "bu/string.h"
+#include "bu/sio.h"
+#include "bu/membuf.h"
+
+class Something : public Bu::CacheObject<Bu::Uuid, Something>
+{
+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() const
+    {
+        return sName;
+    }
+
+    void setName( const Bu::String &sNewName )
+    {
+        sName = sNewName;
+        changed();
+    }
+
+    virtual Bu::String toString() const=0;
+
+private:
+    Bu::Uuid uId;
+    Bu::String sName;
+};
+
+class SubSomethingA : public Something
+{
+friend Bu::ArchiveBase &operator>>( Bu::ArchiveBase &ar, SubSomethingA &s );
+friend Bu::ArchiveBase &operator<<( Bu::ArchiveBase &ar, const SubSomethingA &s );
+public:
+    SubSomethingA()
+    {
+    }
+
+    SubSomethingA( const Bu::String &sName, int iNumber ) :
+        Something( sName ),
+        iNumber( iNumber )
+    {
+    }
+    
+    virtual Bu::String toString() const
+    {
+        return Bu::String("[typeA] %1 (%2)").arg( getName() ).arg( iNumber );
+    }
+
+private:
+    int iNumber;
+};
+
+class SubSomethingB : public Something
+{
+friend Bu::ArchiveBase &operator>>( Bu::ArchiveBase &ar, SubSomethingB &s );
+friend Bu::ArchiveBase &operator<<( Bu::ArchiveBase &ar, const SubSomethingB &s );
+public:
+    SubSomethingB()
+    {
+    }
+
+    SubSomethingB( const Bu::String &sName, const Bu::String &sString ) :
+        Something( sName ),
+        sString( sString )
+    {
+    }
+    
+    virtual Bu::String toString() const
+    {
+        return Bu::String("[typeB] %1 (%2)").arg( getName() ).arg( sString );
+    }
+
+private:
+    Bu::String sString;
+};
+
+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;
+}
+
+Bu::ArchiveBase &operator>>( Bu::ArchiveBase &ar, SubSomethingA &s )
+{
+    return ar >> (Something &)s >> s.iNumber;
+}
+
+Bu::ArchiveBase &operator<<( Bu::ArchiveBase &ar, const SubSomethingA &s )
+{
+    return ar << (Something &)s << s.iNumber;
+}
+
+Bu::ArchiveBase &operator>>( Bu::ArchiveBase &ar, SubSomethingB &s )
+{
+    return ar >> (Something &)s >> s.sString;
+}
+
+Bu::ArchiveBase &operator<<( Bu::ArchiveBase &ar, const SubSomethingB &s )
+{
+    return ar << (Something &)s << s.sString;
+}
+    
+namespace Bu
+{
+    template<>
+    void _cacheObjectSave<const Something>( Bu::Stream &s, const Something *pObject )
+    {
+        Bu::Archive ar( s, Bu::Archive::save );
+        if( typeid(*pObject) == typeid(SubSomethingA) )
+        {
+            ar << (uint8_t)1 << (SubSomethingA &)*pObject;
+        }
+        else if( typeid(*pObject) == typeid(SubSomethingB) )
+        {
+            ar << (uint8_t)2 << (SubSomethingB &)*pObject;
+        }
+        else
+        {
+            Bu::println("Not a recognized type!");
+            throw Bu::ExceptionBase("Not recognized type!");
+        }
+    }
+
+    template<>
+    Something *_cacheObjectLoad<Bu::Uuid, Something>( Bu::CacheObject<Bu::Uuid, Something>::Initializer &initObj, const Bu::Uuid &rKey, Bu::Stream &s )
+    {
+        Bu::Archive ar( s, Bu::Archive::load );
+        uint8_t uType;
+        ar >> uType;
+        switch( uType )
+        {
+            case 1:
+                {
+                    SubSomethingA *ret = initObj(new SubSomethingA());
+                    ar >> *ret;
+                    return ret;
+                }
+                break;
+
+            case 2:
+                {
+                    SubSomethingB *ret = initObj(new SubSomethingB());
+                    ar >> *ret;
+                    return ret;
+                }
+                break;
+
+            default:
+                throw Bu::ExceptionBase("Flagrant error! Invalid type!");
+        }
+
+        return NULL;
+    }
+}
+
+typedef Bu::CachePtr<Bu::Uuid, Something> SomethingPtr;
+typedef Bu::CachePtr<Bu::Uuid, SubSomethingA, Something> SomethingAPtr;
+typedef Bu::CachePtr<Bu::Uuid, SubSomethingB, Something> SomethingBPtr;
+typedef Bu::MyriadCache<Bu::Uuid, Something> SomethingCache;
+
+int main( int, char *[] )
+{
+    Bu::MemBuf mbStore;
+    SomethingCache c( mbStore );
+
+    SomethingPtr ptr;
+    if( time(NULL)%2 )
+        ptr = c.insert( new SubSomethingA("Hello", 55) ).cast<Something>();
+    else
+        ptr = c.insert( new SubSomethingB("Goodbye", "Things") ).cast<Something>();
+
+    Bu::Uuid id = ptr.getKey();
+    Bu::println("Something[%1]: %2").
+        arg( ptr.getKey() ).
+        arg( ptr->getName() );
+   
+    Bu::println("has %1: %2").arg( id ).arg( c.has( id ) );
+    c.erase( ptr.getKey() );
+    Bu::println("has %1: %2").arg( id ).arg( c.has( id ) );
+    SomethingPtr b = ptr;
+
+    SomethingPtr p2 = c.insert( new SubSomethingA("new test", 123) ).cast<Something>();
+    id = p2.getKey();
+    p2.unbind();
+    c.erase( id );
+
+    return 0;
+}
+
+/*
+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();
+    Bu::println("Count: %1").arg( lKeys.getSize() );
+    int j = 0;
+    for( SomethingCache::KeyList::iterator i = lKeys.begin(); i; i++ )
+    {
+        Bu::println(" - %1: '%2'").arg( *i ).arg( c.get( *i )->getName() );
+        if( ((j++)%2) )
+            c.erase( *i );
+    }
+    Bu::println("Count: %1").arg( c.getSize() );
+
+    c._debug();
+
+    SomethingPtr p2;
+    SomethingPtr p1( c.get( lKeys.first() ) );
+
+    c._debug();
+
+    {
+        SomethingPtr p2( p1 );
+        c._debug();
+    }
+    
+    c._debug();
+
+    p2 = p1;
+    
+    c._debug();
+
+    p1.unbind();
+    
+    c._debug();
+
+    Bu::println("Name: %1").arg( p1->getName() );
+
+    p1.unbind();
+    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() );
+
+    p3->setName( p3->getName() + " - again" );
+    }
+
+    c.sync();
+
+    c._debug();
+
+    return 0;
+}
+*/
diff --git a/src/unstable/cachebase.h b/src/unstable/cachebase.h
index 1584246..b17d377 100644
--- a/src/unstable/cachebase.h
+++ b/src/unstable/cachebase.h
@@ -29,6 +29,7 @@ namespace Bu
     private:
         CacheEntry( obtype *pObject ) :
             iRefCount( 0 ),
+            bDeleted( false ),
             pObject( pObject )
         {
         }
@@ -78,10 +79,29 @@ namespace Bu
             return bRet;
         }
 
+        bool isDeleted()
+        {
+            mEntry.lock();
+            bool bRet = bDeleted;
+            mEntry.unlock();
+            return bRet;
+        }
+
+        // Indicates that the entry was both deleted
+        // and has a refcount of zero
+        bool isReadyForCleanup()
+        {
+            mEntry.lock();
+            bool bRet = bDeleted && iRefCount == 0;
+            mEntry.unlock();
+            return bRet;
+        }
+
     private:
         mutable Bu::Mutex mEntry;
         mutable Bu::Mutex mObject;
         int iRefCount;
+        bool bDeleted;
         obtype *pObject;
     };
 
@@ -104,7 +124,7 @@ namespace Bu
                 obtype * &rpData )
         {
             if( pCache == NULL )
-                throw Bu::ExceptionBase("Invalid pointer");
+                throw Bu::ExceptionBase("Invalid cache pointer");
 
             if( !rpData )
             {
@@ -241,6 +261,12 @@ namespace Bu
             return pCache->template cast<obtype, castto>( *this );
         }
         
+        template<typename castto>
+        CachePtr<keytype, castto, basetype> cast() const
+        {
+            return pCache->template cast<obtype, castto>( *this );
+        }
+        
         bool operator==( const MyType &rhs ) const
         {
             return pCache == rhs.pCache &&
@@ -410,19 +436,46 @@ namespace Bu
             else
                 return CachePtr<keytype, castto, obtype>( this, ptr.kId );
         }
+        
+        template<typename supertype, typename castto>
+        CachePtr<keytype, castto, obtype> cast( const CachePtr<keytype, supertype, obtype> &ptr )
+        {
+            if( ptr.pEnt )
+                return CachePtr<keytype, castto, obtype>( this, ptr.pEnt, ptr.kId );
+            else
+                return CachePtr<keytype, castto, obtype>( this, ptr.kId );
+        }
 
         /**
          * Removes an item from the cache.
-         * This routine calls eraseNow for now.  Later the intention is that
-         * this function will always succeed.  It will remove the object from
-         * the backing store and the active tables.  It will not be able to
-         * be requested again, and all changes will be ignored.  When the
-         * object's last references are released the memory will be freed
-         * automatically.
+         * The object in question can have references to it, and those
+         * references will still work after the erase is called.  When erase
+         * is called the object is immediately removed from the backing store
+         * as well as from the main index.  At that point existing references
+         * can be used as normal, but when the last reference is released
+         * the memory will be automatically cleaned up.
          */
         void erase( const keytype &key )
         {
-            eraseNow( key );
+            Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
+            if( hCacheEntry.has( key ) )
+            {
+                Entry *pEnt = hCacheEntry.get( key );
+                pEnt->mEntry.lock();
+                if( pEnt->iRefCount == 0 )
+                {
+                    pEnt->mEntry.unlock();
+                    delete pEnt->pObject;
+                    delete pEnt;
+                }
+                else
+                {
+                    pEnt->bDeleted = true;
+                    pEnt->mEntry.unlock();
+                }
+                hCacheEntry.erase( key );
+            }
+            _erase( key );
         }
 
         /**
@@ -467,8 +520,13 @@ namespace Bu
             return pEnt;
         }
 
-        void releaseRef( Entry * )//pEnt )
+        void releaseRef( Entry *pEnt )
         {
+            if( pEnt->isReadyForCleanup() )
+            {
+                delete pEnt->pObject;
+                delete pEnt;
+            }
         }
 
         void objectChanged( const keytype &k )
diff --git a/src/unstable/cacheobject.h b/src/unstable/cacheobject.h
index 6eb507b..47e09de 100644
--- a/src/unstable/cacheobject.h
+++ b/src/unstable/cacheobject.h
@@ -86,7 +86,8 @@ namespace Bu
     protected:
         void changed( bool bChanged=true )
         {
-            if( this->bChanged == false && bChanged == true && pCache )
+            if( this->bChanged == false && bChanged == true && pCache &&
+                    pEntry && !pEntry->isDeleted() )
                 pCache->objectChanged( getKey() );
 
             this->bChanged = bChanged;
-- 
cgit v1.2.3