diff options
Diffstat (limited to '')
-rw-r--r-- | src/unstable/cachebase.h | 514 |
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 | |||
18 | namespace 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 | ||