aboutsummaryrefslogtreecommitdiff
path: root/src/unstable/cachebase.h
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/unstable/cachebase.h
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/unstable/cachebase.h')
-rw-r--r--src/unstable/cachebase.h514
1 files changed, 514 insertions, 0 deletions
diff --git a/src/unstable/cachebase.h b/src/unstable/cachebase.h
new file mode 100644
index 0000000..807adf0
--- /dev/null
+++ b/src/unstable/cachebase.h
@@ -0,0 +1,514 @@
1/*
2 * Copyright (C) 2007-2013 Xagasoft, All rights reserved.
3 *
4 * This file is part of the libbu++ library and is released under the
5 * terms of the license contained in the file LICENSE.
6 */
7
8#ifndef BU_CACHE_BASE_H
9#define BU_CACHE_BASE_H
10
11#include "bu/string.h"
12#include "bu/archive.h"
13#include "bu/hash.h"
14#include "bu/readwritemutex.h"
15#include "bu/mutexlocker.h"
16#include "bu/cacheobject.h"
17
18namespace Bu
19{
20 template<typename keytype, typename obtype> class CacheBase;
21 template<typename keytype, typename obtype> class CacheEntry;
22
23 template<typename keytype, typename obtype>
24 class CacheEntry
25 {
26 friend class CacheBase<keytype, obtype>;
27 private:
28 CacheEntry( obtype *pObject ) :
29 iRefCount( 0 ),
30 pObject( pObject )
31 {
32 }
33
34 public:
35 int getRefCount() const
36 {
37 mEntry.lock();
38 int ret = iRefCount;
39 mEntry.unlock();
40 return ret;
41 }
42
43 obtype *getPtr() const
44 {
45 return pObject;
46 }
47
48 void lock() const
49 {
50 mObject.lock();
51 }
52
53 void unlock() const
54 {
55 mObject.unlock();
56 }
57
58 Bu::Mutex &getMutex()
59 {
60 return mObject;
61 }
62
63 void incRef()
64 {
65 mEntry.lock();
66 iRefCount++;
67 mEntry.unlock();
68 }
69
70 bool decRef()
71 {
72 mEntry.lock();
73 iRefCount--;
74 bool bRet = iRefCount > 0;
75 mEntry.unlock();
76 return bRet;
77 }
78
79 private:
80 mutable Bu::Mutex mEntry;
81 mutable Bu::Mutex mObject;
82 int iRefCount;
83 obtype *pObject;
84 };
85
86 template<typename keytype, typename basetype>
87 class CachePtrInterface
88 {
89 protected:
90 CachePtrInterface()
91 {
92 }
93
94 virtual ~CachePtrInterface()
95 {
96 }
97
98 template<typename obtype>
99 void checkRef( CacheBase<keytype, basetype> *pCache,
100 const keytype &kId,
101 CacheEntry<keytype, basetype> * &rpEnt,
102 obtype * &rpData )
103 {
104 if( pCache == NULL )
105 throw Bu::ExceptionBase("Invalid pointer");
106
107 if( !rpData )
108 {
109 rpEnt = pCache->getRef( kId );
110 rpEnt->incRef();
111 rpData = dynamic_cast<obtype *>(rpEnt->getPtr());
112 if( rpData == NULL )
113 {
114 rpEnt->decRef();
115 rpEnt = NULL;
116 throw std::bad_cast();
117 }
118 }
119 }
120
121 template<typename obtype>
122 void releaseRef( CacheBase<keytype, basetype> *pCache,
123 CacheEntry<keytype, basetype> * &rpEnt,
124 obtype * &rpData )
125 {
126 if( pCache == NULL )
127 return;
128
129 if( rpData == NULL )
130 return;
131
132 rpData = NULL;
133 rpEnt->decRef();
134 pCache->releaseRef( rpEnt );
135 rpEnt = NULL;
136 }
137 };
138
139 template<typename keytype, typename obtype, typename basetype=obtype>
140 class CachePtr : protected CachePtrInterface<keytype, basetype>
141 {
142 friend class CacheBase<keytype, basetype>;
143 private:
144 typedef CachePtr<keytype, obtype, basetype> MyType;
145
146 CachePtr( CacheBase<keytype, basetype> *pCache,
147 CacheEntry<keytype, basetype> *pEnt,
148 const keytype &kId ) :
149 pCache( pCache ),
150 kId( kId ),
151 pEnt( pEnt ),
152 pData( NULL )
153 {
154 pEnt->incRef();
155 pData = dynamic_cast<obtype *>( pEnt->getPtr() );
156 if( pData == NULL )
157 {
158 pEnt->decRef();
159 throw std::bad_cast();
160 }
161 }
162
163 CachePtr( CacheBase<keytype, basetype> *pCache, const keytype &kId ) :
164 pCache( pCache ),
165 kId( kId ),
166 pEnt( NULL ),
167 pData( NULL )
168 {
169 }
170
171 public:
172 CachePtr() :
173 pCache( NULL ),
174 pEnt( NULL ),
175 pData( NULL )
176 {
177 }
178
179 CachePtr( const MyType &rhs ) :
180 pCache( rhs.pCache ),
181 kId( rhs.kId ),
182 pEnt( rhs.pEnt ),
183 pData( rhs.pData )
184 {
185 pEnt->incRef();
186 }
187
188 virtual ~CachePtr()
189 {
190 unbind();
191 }
192
193 const keytype &getKey() const
194 {
195 return kId;
196 }
197
198 obtype &operator*()
199 {
200 bind();
201 return pData;
202 }
203
204 const obtype &operator*() const
205 {
206 bind();
207 return pData;
208 }
209
210 obtype *operator->()
211 {
212 bind();
213 return pData;
214 }
215
216 const obtype *operator->() const
217 {
218 bind();
219 return pData;
220 }
221
222 MyType operator=( const MyType &rhs )
223 {
224 unbind();
225 pCache = rhs.pCache;
226 kId = rhs.kId;
227 pEnt = rhs.pEnt;
228 pData = rhs.pData;
229 pEnt->incRef();
230
231 return *this;
232 }
233
234 template<typename castto>
235 CachePtr<keytype, castto, basetype> cast()
236 {
237 return pCache->cast<obtype, castto>( *this );
238 }
239
240 bool operator==( const MyType &rhs ) const
241 {
242 return pCache == rhs.pCache &&
243 kId == rhs.kId;
244 }
245
246 bool operator!=( const MyType &rhs ) const
247 {
248 return pCache != rhs.pCache ||
249 kId != rhs.kId;
250 }
251
252 void bind()
253 {
254 CachePtrInterface<keytype, basetype>::checkRef( pCache, kId, pEnt, pData );
255 }
256
257 void unbind()
258 {
259 CachePtrInterface<keytype, basetype>::releaseRef( pCache, pEnt, pData );
260 }
261
262 void lock()
263 {
264 bind();
265 if( pEnt )
266 pEnt->lock();
267 }
268
269 void unlock()
270 {
271 bind();
272 if( pEnt )
273 pEnt->unlock();
274 }
275
276 class Locker
277 {
278 public:
279 Locker( MyType &rPtr ) :
280 rPtr( rPtr ),
281 bLocked( true )
282 {
283 rPtr.lock();
284 }
285
286 ~Locker()
287 {
288 unlock();
289 }
290
291 void unlock()
292 {
293 if( !bLocked )
294 return;
295 rPtr.unlock();
296 bLocked = false;
297 }
298
299 private:
300 MyType &rPtr;
301 bool bLocked;
302 };
303
304 private:
305 CacheBase<keytype, basetype> *pCache;
306 mutable keytype kId;
307 mutable CacheEntry<keytype, basetype> *pEnt;
308 mutable obtype *pData;
309 };
310
311 template<typename keytype, typename obtype>
312 class CacheBase
313 {
314 friend class CachePtrInterface<keytype, obtype>;
315 friend class CacheObject<keytype, obtype>;
316 public:
317 CacheBase()
318 {
319 }
320
321 virtual ~CacheBase()
322 {
323 Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
324 syncChanges();
325 }
326
327 typedef CacheEntry<keytype, obtype> Entry;
328 typedef Bu::List<keytype> KeyList;
329
330 CachePtr<keytype, obtype> insert( obtype *pObject )
331 {
332 Entry *pEnt = addEntry( pObject );
333
334 return CachePtr<keytype, obtype>(
335 this, pEnt, pObject->getKey()
336 );
337 }
338
339 template<typename supertype>
340 CachePtr<keytype, supertype, obtype> insert( supertype *pObject )
341 {
342 obtype *pCast = dynamic_cast<obtype *>( pObject );
343 if( pCast == NULL )
344 throw std::bad_cast();
345
346 Entry *pEnt = addEntry( pCast );
347
348 return CachePtr<keytype, supertype, obtype>(
349 this, pEnt, pObject->getKey()
350 );
351 }
352
353 CachePtr<keytype, obtype> get( const keytype &key )
354 {
355 Entry *pEnt = getEntry( key );
356 return CachePtr<keytype, obtype>( this, pEnt, key );
357 }
358
359 template<typename supertype>
360 CachePtr<keytype, supertype, obtype> get( const keytype &key )
361 {
362 Entry *pEnt = getEntry( key );
363 return CachePtr<keytype, supertype, obtype>( this, pEnt, key );
364 }
365
366 CachePtr<keytype, obtype> getLazy( const keytype &key )
367 {
368 return CachePtr<keytype, obtype>( this, key );
369 }
370
371 template<typename supertype>
372 CachePtr<keytype, supertype, obtype> getLazy( const keytype &key )
373 {
374 return CachePtr<keytype, supertype, obtype>( this, key );
375 }
376
377 template<typename supertype, typename castto>
378 CachePtr<keytype, castto, obtype> cast( CachePtr<keytype, supertype, obtype> &ptr )
379 {
380 if( ptr.pEnt )
381 return CachePtr<keytype, castto, obtype>( this, ptr.pEnt, ptr.kId );
382 else
383 return CachePtr<keytype, castto, obtype>( this, ptr.kId );
384 }
385
386 void erase( const keytype &key )
387 {
388 Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
389 if( hCacheEntry.has( key ) )
390 {
391 Entry *pEnt = hCacheEntry.get( key );
392 pEnt->mEntry.lock();
393 if( pEnt->iRefCount > 0 )
394 {
395 int iCount = pEnt->iRefCount;
396 pEnt->mEntry.unlock();
397 throw Bu::ExceptionBase( Bu::String("Cache entry %1 cannot be erased, there are %2 active references.").arg( key ).arg( iCount ).end().getStr() );
398 }
399 delete pEnt->pObject;
400 delete pEnt;
401 hCacheEntry.erase( key );
402 }
403 _erase( key );
404 }
405
406 virtual KeyList getKeys() const=0;
407 virtual int getSize() const=0;
408
409 void sync()
410 {
411 Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
412 _sync();
413 syncChanges();
414 }
415
416 protected:
417 Entry *getRef( const keytype &k )
418 {
419 Entry *pEnt = getEntry( k );
420 return pEnt;
421 }
422
423 void releaseRef( Entry * )//pEnt )
424 {
425 }
426
427 void objectChanged( const keytype &k )
428 {
429 Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
430 hChanged.insert( k, true );
431 }
432
433 protected:
434 virtual void _create( const obtype *o )=0;
435 virtual void _erase( const keytype &k )=0;
436
437 virtual obtype *_load( const keytype &k )=0;
438 virtual void _save( const obtype *o )=0;
439
440 virtual void _sync()=0;
441
442 private:
443 Entry *addEntry( obtype *pObject )
444 {
445 Entry *pEnt = new Entry( pObject );
446 mCacheEntry.lockWrite();
447 hCacheEntry.insert( pObject->getKey(), pEnt );
448 mCacheEntry.unlockWrite();
449 _create( pObject );
450 pObject->setCache( this, pEnt );
451
452 return pEnt;
453 }
454
455 Entry *getEntry( const keytype &k )
456 {
457 Entry *pEnt = NULL;
458 try
459 {
460 Bu::ReadWriteMutex::ReadLocker rl( mCacheEntry );
461 pEnt = hCacheEntry.get( k );
462 }
463 catch(...)
464 {
465 // try to load the object from the backing store
466 obtype *pObject = _load( k );
467 pEnt = new Entry( pObject );
468 pObject->setCache( this, pEnt );
469 Bu::ReadWriteMutex::WriteLocker wl( mCacheEntry );
470 hCacheEntry.insert( k, pEnt );
471 }
472 return pEnt;
473 }
474
475 void syncChanges()
476 {
477 if( !hChanged.isEmpty() )
478 {
479 for( typename CacheKeySet::iterator i = hChanged.begin(); i; i++ )
480 {
481 Entry *pEnt = hCacheEntry.get( i.getKey() );
482 Bu::MutexLocker ml( pEnt->getMutex() );
483 _save( pEnt->getPtr() );
484 }
485 hChanged.clear();
486 }
487 }
488
489 private:
490 typedef Bu::Hash<keytype, Entry *> CacheEntryHash;
491 typedef Bu::Hash<keytype, bool> CacheKeySet;
492 CacheEntryHash hCacheEntry;
493 CacheKeySet hChanged;
494 Bu::ReadWriteMutex mCacheEntry;
495 };
496
497 template<typename obtype>
498 void _cacheObjectSave( Bu::Stream &s, obtype *pObject )
499 {
500 Bu::Archive ar( s, Bu::Archive::save );
501 ar << *pObject;
502 }
503
504 template<typename obtype>
505 obtype *_cacheObjectLoad( Bu::Stream &s )
506 {
507 Bu::Archive ar( s, Bu::Archive::load );
508 obtype *ret = new obtype();
509 ar >> *ret;
510 return ret;
511 }
512}
513
514#endif