From 21a4337dc2f969dc3ec81e6ba3170119bcb67016 Mon Sep 17 00:00:00 2001 From: Mike Buland 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 +{ +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( 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::CacheObject::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 SomethingPtr; +typedef Bu::CachePtr SomethingAPtr; +typedef Bu::CachePtr SomethingBPtr; +typedef Bu::MyriadCache 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(); + else + ptr = c.insert( new SubSomethingB("Goodbye", "Things") ).cast(); + + 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(); + 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( *this ); } + template + CachePtr cast() const + { + return pCache->template cast( *this ); + } + bool operator==( const MyType &rhs ) const { return pCache == rhs.pCache && @@ -410,19 +436,46 @@ namespace Bu else return CachePtr( this, ptr.kId ); } + + template + CachePtr cast( const CachePtr &ptr ) + { + if( ptr.pEnt ) + return CachePtr( this, ptr.pEnt, ptr.kId ); + else + return CachePtr( 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