summaryrefslogtreecommitdiff
path: root/src/tests/cache.cpp
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2013-03-17 23:45:21 +0000
committerMike Buland <eichlan@xagasoft.com>2013-03-17 23:45:21 +0000
commitfb5176bbd5355b02b7d0e65da3ef3f0105824cd0 (patch)
tree32a9d8114ce4852b901a35f6aec982e6071a3bac /src/tests/cache.cpp
parenta6d249cad214dc0baff0e80e56ffdec91d8a1cf0 (diff)
downloadlibbu++-fb5176bbd5355b02b7d0e65da3ef3f0105824cd0.tar.gz
libbu++-fb5176bbd5355b02b7d0e65da3ef3f0105824cd0.tar.bz2
libbu++-fb5176bbd5355b02b7d0e65da3ef3f0105824cd0.tar.xz
libbu++-fb5176bbd5355b02b7d0e65da3ef3f0105824cd0.zip
The new cache system has been broken out into it's individual headers, and is
now ready for actual use.
Diffstat (limited to 'src/tests/cache.cpp')
-rw-r--r--src/tests/cache.cpp719
1 files changed, 12 insertions, 707 deletions
diff --git a/src/tests/cache.cpp b/src/tests/cache.cpp
index 112d916..dc5dbd0 100644
--- a/src/tests/cache.cpp
+++ b/src/tests/cache.cpp
@@ -1,707 +1,9 @@
1#include <bu/archive.h> 1#include "bu/myriadcache.h"
2#include <bu/hash.h> 2#include "bu/uuid.h"
3#include <bu/uuid.h> 3#include "bu/string.h"
4#include <bu/myriad.h> 4#include "bu/sio.h"
5#include <bu/myriadstream.h>
6#include <bu/file.h>
7#include <bu/streamstack.h>
8#include <bu/readwritemutex.h>
9#include <bu/mutexlocker.h>
10
11#include <bu/sio.h>
12#include <bu/string.h>
13
14//
15// New Cache Implementation
16//
17
18template<typename keytype, typename obtype> class CacheBase;
19template<typename keytype, typename obtype> class CacheEntry;
20
21template<typename keytype, typename obtype>
22class CacheObject
23{
24friend class CacheBase<keytype, obtype>;
25public:
26 CacheObject() :
27 pCache( NULL ),
28 bChanged( false )
29 {
30 }
31
32 virtual ~CacheObject()
33 {
34 }
35
36 typedef CacheBase<keytype, obtype> CacheType;
37
38 virtual keytype getKey() const=0;
39 virtual int getPersistenceScore() const { return 0; }
40
41 void lock()
42 {
43 pEntry->lock();
44 }
45
46 void unlock()
47 {
48 pEntry->unlock();
49 }
50
51 int getRefCount() const
52 {
53 return pEntry->getRefCount();
54 }
55
56 bool hasChanged() const
57 {
58 return bChanged;
59 }
60
61protected:
62 void changed( bool bChanged=true )
63 {
64 if( this->bChanged == false && bChanged == true )
65 pCache->objectChanged( getKey() );
66
67 this->bChanged = bChanged;
68 }
69
70private:
71 typedef CacheEntry<keytype, obtype> Entry;
72 void setCache( CacheType *pCache, Entry *pEntry )
73 {
74 this->pCache = pCache;
75 this->pEntry = pEntry;
76 }
77
78private:
79 CacheType *pCache;
80 Entry *pEntry;
81 bool bChanged;
82};
83
84template<typename keytype, typename obtype>
85class CacheBase;
86
87template<typename keytype, typename obtype>
88class CacheEntry
89{
90friend class CacheBase<keytype, obtype>;
91private:
92 CacheEntry( obtype *pObject ) :
93 iRefCount( 0 ),
94 pObject( pObject )
95 {
96 }
97
98public:
99 int getRefCount() const
100 {
101 mEntry.lock();
102 int ret = iRefCount;
103 mEntry.unlock();
104 return ret;
105 }
106
107 obtype *getPtr() const
108 {
109 return pObject;
110 }
111
112 void lock() const
113 {
114 mObject.lock();
115 }
116
117 void unlock() const
118 {
119 mObject.unlock();
120 }
121
122 Bu::Mutex &getMutex()
123 {
124 return mObject;
125 }
126
127 void incRef()
128 {
129 mEntry.lock();
130 iRefCount++;
131 mEntry.unlock();
132 }
133
134 bool decRef()
135 {
136 mEntry.lock();
137 iRefCount--;
138 bool bRet = iRefCount > 0;
139 mEntry.unlock();
140 return bRet;
141 }
142
143private:
144 mutable Bu::Mutex mEntry;
145 mutable Bu::Mutex mObject;
146 int iRefCount;
147 obtype *pObject;
148};
149
150template<typename keytype, typename basetype>
151class CachePtrInterface
152{
153protected:
154 CachePtrInterface()
155 {
156 }
157
158 virtual ~CachePtrInterface()
159 {
160 }
161
162 template<typename obtype>
163 void checkRef( CacheBase<keytype, basetype> *pCache,
164 const keytype &kId,
165 CacheEntry<keytype, basetype> * &rpEnt,
166 obtype * &rpData )
167 {
168 if( pCache == NULL )
169 throw Bu::ExceptionBase("Invalid pointer");
170
171 if( !rpData )
172 {
173 rpEnt = pCache->getRef( kId );
174 rpEnt->incRef();
175 rpData = dynamic_cast<obtype *>(rpEnt->getPtr());
176 if( rpData == NULL )
177 {
178 rpEnt->decRef();
179 rpEnt = NULL;
180 throw std::bad_cast();
181 }
182 }
183 }
184
185 template<typename obtype>
186 void releaseRef( CacheBase<keytype, basetype> *pCache,
187 CacheEntry<keytype, basetype> * &rpEnt,
188 obtype * &rpData )
189 {
190 if( pCache == NULL )
191 return;
192
193 if( rpData == NULL )
194 return;
195
196 rpData = NULL;
197 rpEnt->decRef();
198 pCache->releaseRef( rpEnt );
199 rpEnt = NULL;
200 }
201};
202
203template<typename keytype, typename obtype, typename basetype=obtype>
204class CachePtr : protected CachePtrInterface<keytype, basetype>
205{
206friend class CacheBase<keytype, basetype>;
207private:
208 typedef CachePtr<keytype, obtype, basetype> MyType;
209
210 CachePtr( CacheBase<keytype, basetype> *pCache,
211 CacheEntry<keytype, basetype> *pEnt,
212 const keytype &kId ) :
213 pCache( pCache ),
214 kId( kId ),
215 pEnt( pEnt ),
216 pData( NULL )
217 {
218 pEnt->incRef();
219 pData = dynamic_cast<obtype *>( pEnt->getPtr() );
220 if( pData == NULL )
221 {
222 pEnt->decRef();
223 throw std::bad_cast();
224 }
225 }
226
227 CachePtr( CacheBase<keytype, basetype> *pCache, const keytype &kId ) :
228 pCache( pCache ),
229 kId( kId ),
230 pEnt( NULL ),
231 pData( NULL )
232 {
233 }
234
235public:
236 CachePtr() :
237 pCache( NULL ),
238 pEnt( NULL ),
239 pData( NULL )
240 {
241 }
242
243 CachePtr( const MyType &rhs ) :
244 pCache( rhs.pCache ),
245 kId( rhs.kId ),
246 pEnt( rhs.pEnt ),
247 pData( rhs.pData )
248 {
249 pEnt->incRef();
250 }
251
252 virtual ~CachePtr()
253 {
254 unbind();
255 }
256
257 const keytype &getKey() const
258 {
259 return kId;
260 }
261
262 obtype &operator*()
263 {
264 bind();
265 return pData;
266 }
267
268 const obtype &operator*() const
269 {
270 bind();
271 return pData;
272 }
273
274 obtype *operator->()
275 {
276 bind();
277 return pData;
278 }
279
280 const obtype *operator->() const
281 {
282 bind();
283 return pData;
284 }
285
286 MyType operator=( const MyType &rhs )
287 {
288 unbind();
289 pCache = rhs.pCache;
290 kId = rhs.kId;
291 pEnt = rhs.pEnt;
292 pData = rhs.pData;
293 pEnt->incRef();
294
295 return *this;
296 }
297
298 template<typename castto>
299 CachePtr<keytype, castto, basetype> cast()
300 {
301 return pCache->cast<obtype, castto>( *this );
302 }
303
304 bool operator==( const MyType &rhs ) const
305 {
306 return pCache == rhs.pCache &&
307 kId == rhs.kId;
308 }
309
310 bool operator!=( const MyType &rhs ) const
311 {
312 return pCache != rhs.pCache ||
313 kId != rhs.kId;
314 }
315
316 void bind()
317 {
318 CachePtrInterface<keytype, basetype>::checkRef( pCache, kId, pEnt, pData );
319 }
320
321 void unbind()
322 {
323 CachePtrInterface<keytype, basetype>::releaseRef( pCache, pEnt, pData );
324 }
325
326 void lock()
327 {
328 bind();
329 if( pEnt )
330 pEnt->lock();
331 }
332
333 void unlock()
334 {
335 bind();
336 if( pEnt )
337 pEnt->unlock();
338 }
339
340 class Locker
341 {
342 public:
343 Locker( MyType &rPtr ) :
344 rPtr( rPtr ),
345 bLocked( true )
346 {
347 rPtr.lock();
348 }
349
350 ~Locker()
351 {
352 unlock();
353 }
354
355 void unlock()
356 {
357 if( !bLocked )
358 return;
359 rPtr.unlock();
360 bLocked = false;
361 }
362
363 private:
364 MyType &rPtr;
365 bool bLocked;
366 };
367 5
368private: 6class Something : public Bu::CacheObject<Bu::Uuid, Something>
369 CacheBase<keytype, basetype> *pCache;
370 mutable keytype kId;
371 mutable CacheEntry<keytype, basetype> *pEnt;
372 mutable obtype *pData;
373};
374
375template<typename keytype, typename obtype>
376class CacheBase
377{
378friend class CachePtrInterface<keytype, obtype>;
379friend class CacheObject<keytype, obtype>;
380public:
381 CacheBase()
382 {
383 }
384
385 virtual ~CacheBase()
386 {
387 Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
388 syncChanges();
389 }
390
391 typedef CacheEntry<keytype, obtype> Entry;
392 typedef Bu::List<keytype> KeyList;
393
394 CachePtr<keytype, obtype> insert( obtype *pObject )
395 {
396 Entry *pEnt = addEntry( pObject );
397
398 return CachePtr<keytype, obtype>(
399 this, pEnt, pObject->getKey()
400 );
401 }
402
403 template<typename supertype>
404 CachePtr<keytype, supertype, obtype> insert( supertype *pObject )
405 {
406 obtype *pCast = dynamic_cast<obtype *>( pObject );
407 if( pCast == NULL )
408 throw std::bad_cast();
409
410 Entry *pEnt = addEntry( pCast );
411
412 return CachePtr<keytype, supertype, obtype>(
413 this, pEnt, pObject->getKey()
414 );
415 }
416
417 CachePtr<keytype, obtype> get( const keytype &key )
418 {
419 Entry *pEnt = getEntry( key );
420 return CachePtr<keytype, obtype>( this, pEnt, key );
421 }
422
423 template<typename supertype>
424 CachePtr<keytype, supertype, obtype> get( const keytype &key )
425 {
426 Entry *pEnt = getEntry( key );
427 return CachePtr<keytype, supertype, obtype>( this, pEnt, key );
428 }
429
430 CachePtr<keytype, obtype> getLazy( const keytype &key )
431 {
432 return CachePtr<keytype, obtype>( this, key );
433 }
434
435 template<typename supertype>
436 CachePtr<keytype, supertype, obtype> getLazy( const keytype &key )
437 {
438 return CachePtr<keytype, supertype, obtype>( this, key );
439 }
440
441 template<typename supertype, typename castto>
442 CachePtr<keytype, castto, obtype> cast( CachePtr<keytype, supertype, obtype> &ptr )
443 {
444 if( ptr.pEnt )
445 return CachePtr<keytype, castto, obtype>( this, ptr.pEnt, ptr.kId );
446 else
447 return CachePtr<keytype, castto, obtype>( this, ptr.kId );
448 }
449
450 void erase( const keytype &key )
451 {
452 Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
453 if( hCacheEntry.has( key ) )
454 {
455 Entry *pEnt = hCacheEntry.get( key );
456 pEnt->mEntry.lock();
457 if( pEnt->iRefCount > 0 )
458 {
459 int iCount = pEnt->iRefCount;
460 pEnt->mEntry.unlock();
461 throw Bu::ExceptionBase( Bu::String("Cache entry %1 cannot be erased, there are %2 active references.").arg( key ).arg( iCount ).end().getStr() );
462 }
463 delete pEnt->pObject;
464 delete pEnt;
465 hCacheEntry.erase( key );
466 }
467 _erase( key );
468 }
469
470 virtual KeyList getKeys() const=0;
471 virtual int getSize() const=0;
472
473 void sync()
474 {
475 Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
476 Bu::println("Syncing storage layer...");
477 _sync();
478 syncChanges();
479 }
480
481 void _debug()
482 {
483 Bu::ReadWriteMutex::ReadLocker rl( mCacheEntry );
484 Bu::println("Cache entries:");
485 for( typename CacheEntryHash::iterator i = hCacheEntry.begin(); i; i++ )
486 {
487 Bu::println(" %1: refs=%2").
488 arg( i.getKey() ).
489 arg( i.getValue()->getRefCount() );
490 }
491 }
492
493protected:
494 Entry *getRef( const keytype &k )
495 {
496 Entry *pEnt = getEntry( k );
497 return pEnt;
498 }
499
500 void releaseRef( Entry * )//pEnt )
501 {
502 }
503
504 void objectChanged( const keytype &k )
505 {
506 Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
507 hChanged.insert( k, true );
508 }
509
510protected:
511 virtual void _create( const obtype *o )=0;
512 virtual void _erase( const keytype &k )=0;
513
514 virtual obtype *_load( const keytype &k )=0;
515 virtual void _save( const obtype *o )=0;
516
517 virtual void _sync()=0;
518
519private:
520 Entry *addEntry( obtype *pObject )
521 {
522 Entry *pEnt = new Entry( pObject );
523 mCacheEntry.lockWrite();
524 hCacheEntry.insert( pObject->getKey(), pEnt );
525 mCacheEntry.unlockWrite();
526 _create( pObject );
527 pObject->setCache( this, pEnt );
528
529 return pEnt;
530 }
531
532 Entry *getEntry( const keytype &k )
533 {
534 Entry *pEnt = NULL;
535 try
536 {
537 Bu::ReadWriteMutex::ReadLocker rl( mCacheEntry );
538 pEnt = hCacheEntry.get( k );
539 }
540 catch(...)
541 {
542 // try to load the object from the backing store
543 obtype *pObject = _load( k );
544 pEnt = new Entry( pObject );
545 pObject->setCache( this, pEnt );
546 Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
547 hCacheEntry.insert( k, pEnt );
548 }
549 return pEnt;
550 }
551
552 void syncChanges()
553 {
554 Bu::println("Syncing: %1 entries changed.").arg( hChanged.getSize() );
555 if( !hChanged.isEmpty() )
556 {
557 for( typename CacheKeySet::iterator i = hChanged.begin(); i; i++ )
558 {
559 Entry *pEnt = hCacheEntry.get( i.getKey() );
560 Bu::MutexLocker ml( pEnt->getMutex() );
561 _save( pEnt->getPtr() );
562 }
563 hChanged.clear();
564 }
565 }
566
567private:
568 typedef Bu::Hash<keytype, Entry *> CacheEntryHash;
569 typedef Bu::Hash<keytype, bool> CacheKeySet;
570 CacheEntryHash hCacheEntry;
571 CacheKeySet hChanged;
572 Bu::ReadWriteMutex mCacheEntry;
573};
574
575template<typename obtype>
576void _cacheObjectSave( Bu::Stream &s, obtype *pObject )
577{
578 Bu::Archive ar( s, Bu::Archive::save );
579 ar << *pObject;
580}
581
582template<typename obtype>
583obtype *_cacheObjectLoad( Bu::Stream &s )
584{
585 Bu::Archive ar( s, Bu::Archive::load );
586 obtype *ret = new obtype();
587 ar >> *ret;
588 return ret;
589}
590
591template<typename keytype, typename obtype>
592class MyriadCache : public CacheBase<keytype, obtype>
593{
594public:
595 MyriadCache( Bu::Stream &sStore, int iBlockSize=512, int iPreallocate=8 ) :
596 sStore( sStore ),
597 mStore( sStore, iBlockSize, iPreallocate ),
598 bStructureChanged( false )
599 {
600 try
601 {
602 Bu::ReadWriteMutex::ReadLocker l( rwStore );
603 Bu::MyriadStream ms = mStore.openStream( 1 );
604 Bu::Archive ar( ms, Bu::Archive::load );
605 uint8_t uVer;
606 ar >> uVer;
607 switch( uVer )
608 {
609 case 0:
610 ar >> hIndex;
611 break;
612 }
613 }
614 catch(...)
615 {
616 if( mStore.createStreamWithId( 1 ) != 1 )
617 throw Bu::ExceptionBase("Error creating index stream.");
618
619 _sync();
620 }
621 }
622
623 virtual ~MyriadCache()
624 {
625 Bu::println("MyriadCache destroying.");
626 _sync();
627 }
628
629 using typename CacheBase<keytype,obtype>::KeyList;
630
631 virtual KeyList getKeys() const
632 {
633 Bu::ReadWriteMutex::ReadLocker rl( rwStore );
634 return hIndex.getKeys();
635 }
636
637 virtual int getSize() const
638 {
639 Bu::ReadWriteMutex::ReadLocker rl( rwStore );
640 return hIndex.getSize();
641 }
642
643protected:
644 virtual void _create( const obtype *o )
645 {
646 Bu::ReadWriteMutex::WriteLocker wl( rwStore );
647 hIndex.insert( o->getKey(), mStore.createStream() );
648 _save( o );
649
650 bStructureChanged = true;
651 }
652
653 virtual void _erase( const keytype &k )
654 {
655 Bu::ReadWriteMutex::WriteLocker wl( rwStore );
656 mStore.deleteStream( hIndex.get( k ) );
657 hIndex.erase( k );
658
659 bStructureChanged = true;
660 }
661
662 virtual obtype *_load( const keytype &k )
663 {
664 Bu::MyriadStream ms = mStore.openStream( hIndex.get( k ) );
665 return _cacheObjectLoad<obtype>( ms );
666 }
667
668 virtual void _save( const obtype *o )
669 {
670 Bu::MyriadStream ms = mStore.openStream( hIndex.get( o->getKey() ) );
671 _cacheObjectSave( ms, o );
672 ms.setSize( ms.tell() );
673 }
674
675 virtual void _sync()
676 {
677 Bu::println("Syncing myriad header.");
678 Bu::ReadWriteMutex::ReadLocker wl( rwStore );
679 if( !bStructureChanged )
680 return;
681
682 Bu::println(" --> Header changed, write required.");
683 Bu::MyriadStream ms = mStore.openStream( 1 );
684 Bu::Archive ar( ms, Bu::Archive::save );
685 ar << (uint8_t)0 << hIndex;
686 ar.close();
687 ms.setSize( ms.tell() );
688
689 bStructureChanged = false;
690 }
691
692private:
693 Bu::Stream &sStore;
694 Bu::Myriad mStore;
695 Bu::Hash<keytype, int> hIndex;
696 mutable Bu::ReadWriteMutex rwStore;
697 bool bStructureChanged;
698};
699
700//
701// Test
702//
703
704class Something : public CacheObject<Bu::Uuid, Something>
705{ 7{
706friend Bu::ArchiveBase &operator>>( Bu::ArchiveBase &ar, Something &s ); 8friend Bu::ArchiveBase &operator>>( Bu::ArchiveBase &ar, Something &s );
707friend Bu::ArchiveBase &operator<<( Bu::ArchiveBase &ar, const Something &s ); 9friend Bu::ArchiveBase &operator<<( Bu::ArchiveBase &ar, const Something &s );
@@ -821,6 +123,8 @@ Bu::ArchiveBase &operator<<( Bu::ArchiveBase &ar, const SubSomethingB &s )
821 return ar << (Something &)s << s.sString; 123 return ar << (Something &)s << s.sString;
822} 124}
823 125
126namespace Bu
127{
824template<> 128template<>
825void _cacheObjectSave<const Something>( Bu::Stream &s, const Something *pObject ) 129void _cacheObjectSave<const Something>( Bu::Stream &s, const Something *pObject )
826{ 130{
@@ -868,11 +172,12 @@ Something *_cacheObjectLoad<Something>( Bu::Stream &s )
868 throw Bu::ExceptionBase("Flagrant error! Invalid type!"); 172 throw Bu::ExceptionBase("Flagrant error! Invalid type!");
869 } 173 }
870} 174}
175}
871 176
872typedef CachePtr<Bu::Uuid, Something> SomethingPtr; 177typedef Bu::CachePtr<Bu::Uuid, Something> SomethingPtr;
873typedef CachePtr<Bu::Uuid, SubSomethingA, Something> SomethingAPtr; 178typedef Bu::CachePtr<Bu::Uuid, SubSomethingA, Something> SomethingAPtr;
874typedef CachePtr<Bu::Uuid, SubSomethingB, Something> SomethingBPtr; 179typedef Bu::CachePtr<Bu::Uuid, SubSomethingB, Something> SomethingBPtr;
875typedef MyriadCache<Bu::Uuid, Something> SomethingCache; 180typedef Bu::MyriadCache<Bu::Uuid, Something> SomethingCache;
876 181
877int main( int argc, char *argv[] ) 182int main( int argc, char *argv[] )
878{ 183{