summaryrefslogtreecommitdiff
path: root/src/experimental/cache.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/experimental/cache.h')
-rw-r--r--src/experimental/cache.h437
1 files changed, 437 insertions, 0 deletions
diff --git a/src/experimental/cache.h b/src/experimental/cache.h
new file mode 100644
index 0000000..455cf1b
--- /dev/null
+++ b/src/experimental/cache.h
@@ -0,0 +1,437 @@
1/*
2 * Copyright (C) 2007-2011 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_H
9#define BU_CACHE_H
10
11// #include "bu/cptr.h"
12#include "bu/hash.h"
13#include "bu/list.h"
14#include "bu/cachestore.h"
15#include "bu/cachecalc.h"
16
17#include "bu/trace.h"
18
19namespace Bu
20{
21// template<class keytype, class obtype>
22// keytype __cacheGetKey( obtype *&pObj );
23 template<class keytype, class obtype>
24 keytype __cacheGetKey( const obtype *pObj )
25 {
26 return pObj->getKey();
27 }
28
29 template<class keytype, class obtype>
30 class Cache
31 {
32 public:
33 /**
34 * Cache Pointer - Provides access to data that is held within the
35 * cache. This provides safe, refcounting access to data stored in
36 * the cache, with support for lazy loading.
37 */
38 class Ptr
39 {
40 friend class Bu::Cache<keytype, obtype>;
41 private:
42 Ptr( Cache<keytype, obtype> *pCache, obtype *pData,
43 const keytype &kId ) :
44 pCache( pCache ),
45 pData( pData ),
46 kId( kId )
47 {
48 if( pCache )
49 pCache->incRef( kId );
50 }
51
52 Ptr( Cache<keytype, obtype> *pCache, const keytype &kId ) :
53 pCache( pCache ),
54 pData( NULL ),
55 kId( kId )
56 {
57 }
58
59 public:
60 Ptr( const Ptr &rSrc ) :
61 pCache( rSrc.pCache ),
62 pData( rSrc.pData ),
63 kId( rSrc.kId )
64 {
65 if( pCache && pData )
66 pCache->incRef( kId );
67 }
68
69 Ptr() :
70 pCache( 0 ),
71 pData( 0 )
72 {
73 }
74
75 virtual ~Ptr()
76 {
77 if( pCache && pData )
78 pCache->decRef( kId );
79 }
80
81 obtype &operator*()
82 {
83 checkPtr();
84 return *pData;
85 }
86
87 const obtype &operator*() const
88 {
89 checkPtr();
90 return *pData;
91 }
92
93 obtype *operator->()
94 {
95 checkPtr();
96 return pData;
97 }
98
99 const obtype *operator->() const
100 {
101 checkPtr();
102 return pData;
103 }
104
105 bool isValid() const
106 {
107 return pCache != NULL;
108 }
109
110 bool isBound() const
111 {
112 return pData != NULL;
113 }
114
115 bool isSet() const
116 {
117 return pCache != NULL;
118 }
119
120 const keytype &getKey() const
121 {
122 return kId;
123 }
124
125 void unbind()
126 {
127 if( pCache && pData )
128 pCache->decRef( kId );
129 pData = NULL;
130 }
131
132 void clear()
133 {
134 unbind();
135 pCache = NULL;
136 }
137
138 void unset()
139 {
140 clear();
141 }
142
143 Ptr &operator=( const Ptr &rRhs )
144 {
145 if( pCache && pData )
146 pCache->decRef( kId );
147 pCache = rRhs.pCache;
148 pData = rRhs.pData;
149 kId = rRhs.kId;
150 if( pCache && pData )
151 pCache->incRef( kId );
152 return *this;
153 }
154
155 bool operator==( const Ptr &rRhs ) const
156 {
157 return pCache == rRhs.pCache && kId == rRhs.kId;
158 }
159
160 bool operator!=( const Ptr &rRhs ) const
161 {
162 return pCache != rRhs.pCache || kId != rRhs.kId;
163 }
164
165 private:
166 void checkPtr() const
167 {
168 if( pCache && !pData )
169 {
170 pData = pCache->getRaw( kId );
171 pCache->incRef( kId );
172 }
173 }
174
175 private:
176 Bu::Cache<keytype, obtype> *pCache;
177 mutable obtype *pData;
178 mutable keytype kId;
179 };
180
181 private:
182 typedef Bu::CacheStore<keytype, obtype> Store;
183 typedef Bu::List<Store *> StoreList;
184 typedef Bu::CacheCalc<keytype, obtype> Calc;
185
186 typedef struct CacheEntry
187 {
188 obtype *pData;
189 int iRefs;
190 time_t tLastSync;
191 } CacheEntry;
192
193 typedef Bu::Hash<keytype, CacheEntry> CidHash;
194
195 public:
196 typedef keytype Key;
197 Cache( Calc *pCalc, Store *pStore ) :
198 pCalc( pCalc ),
199 pStore( pStore )
200 {
201 TRACE();
202 pCalc->setCache( this );
203 }
204
205 virtual ~Cache()
206 {
207 TRACE();
208
209 // Better safe than sorry, better try a sync before anything
210 // else happens.
211 sync();
212
213 // Cycle through and unload all objects from the system.
214 for( typename CidHash::iterator i = hEnt.begin();
215 i != hEnt.end(); i++ )
216 {
217 if( i.getValue().iRefs > 0 )
218 {
219 // TODO: Throw an error in this case? iRefs != 0 for an
220 // object when the Cache is destroyed.
221 throw Bu::ExceptionBase("iRefs not zero.");
222 }
223 pStore->unload(
224 i.getValue().pData,
225 i.getKey()
226 );
227 }
228 delete pCalc;
229 delete pStore;
230 }
231
232 Ptr insert( obtype *pData )
233 {
234 TRACE( pData );
235 if( pStore->has( __cacheGetKey<keytype, obtype>( pData ) ) )
236 throw Bu::ExceptionBase("Key already exists in cache.");
237 CacheEntry e = {pData, 0, 0};
238 keytype k = pStore->create( pData );
239 hEnt.insert( k, e );
240
241 pCalc->onLoad( pData, k );
242
243 pStore->sync();
244
245 return Ptr( this, pData, k );
246 }
247
248 bool has( const keytype &cId )
249 {
250 return hEnt.has( cId ) || pStore->has( cId );
251 }
252
253 /**
254 * Retrieve an object from the cache and return a pointer to it.
255 * The object returned may be loaded from backend storage if needed,
256 * or the currently live object will be returned.
257 *@param cId The id of the object to load.
258 *@returns A pointer to the object.
259 */
260 Ptr get( const keytype &cId )
261 {
262 TRACE( cId );
263 try {
264 return Ptr( this, hEnt.get( cId ).pData, cId );
265 }
266 catch( Bu::HashException &e ) {
267 CacheEntry e = {pStore->load( cId ), 0, time( NULL )};
268 pCalc->onLoad( e.pData, cId );
269 hEnt.insert( cId, e );
270 return Ptr( this, e.pData, cId );
271 }
272 }
273
274 /**
275 * Retrieve a handle to an object without loading it now. This function
276 * will return a pointer that has not yet been "realized" but can be
277 * used normally. Upon initial use in any way the object will be
278 * loaded from the cache, either linking against the already loaded
279 * object or loading it fresh from the backend storage. The advantage
280 * of this is that you recieve a usable handle to the data, but it
281 * does not count as a reference yet, meaning that the data is loaded
282 * when you need it, not before.
283 */
284 Ptr getLazy( const keytype &cId )
285 {
286 TRACE( cId );
287 return Ptr( this, cId );
288 }
289
290 int getRefCount( const keytype &cId )
291 {
292 TRACE( cId );
293 return hEnt.get( cId ).iRefs;
294 }
295
296 void unload( const keytype &cId )
297 {
298 TRACE( cId );
299 try {
300 if( hEnt.get( cId ).iRefs > 0 )
301 {
302 printf("Shouldn't unload, references still exist!\n");
303 return;
304 }
305 }
306 catch( Bu::HashException &e ) {
307 // It's not here? Eh, return.
308 return;
309 }
310 obtype *pObj = hEnt.get( cId ).pData;
311 pCalc->onUnload( pObj, cId );
312 hEnt.erase( cId );
313
314 // The unload has to happen last just in case cId is a reference
315 // to data that is about to be deleted from memory by the unload.
316 pStore->unload( pObj, cId );
317 }
318
319 void erase( const keytype &cId )
320 {
321 TRACE( cId );
322 try {
323 if( hEnt.get( cId ).iRefs > 0 )
324 {
325 printf("Shouldn't erase, references still exist!\n");
326 return;
327 }
328
329 obtype *pObj = hEnt.get( cId ).pData;
330 pCalc->onDestroy( pObj, cId );
331 hEnt.erase( cId );
332
333 pStore->destroy( pObj, cId );
334 pStore->sync();
335 }
336 catch( Bu::HashException &e ) {
337 pCalc->onDestroy( cId );
338
339 if( hEnt.has( cId ) )
340 {
341 // The object was loaded by onDestroy
342 erase( cId );
343 }
344 else
345 {
346 pStore->destroy( cId );
347 pStore->sync();
348 }
349 }
350 }
351
352 typedef Bu::List<keytype> KeyList;
353 KeyList getKeys()
354 {
355 return pStore->getKeys();
356 }
357
358 KeyList getActiveKeys()
359 {
360 return hEnt.getKeys();
361 }
362
363 int getSize()
364 {
365 return pStore->getSize();
366 }
367
368 /**
369 * Make sure all currently loaded but not-in-use objects are synced to
370 * the store.
371 */
372 void sync()
373 {
374 TRACE();
375 int iSynced = 0;
376 for( typename CidHash::iterator i = hEnt.begin();
377 i != hEnt.end(); i++ )
378 {
379 if( i.getValue().iRefs == 0 )
380 {
381 if( pCalc->shouldSync(
382 i.getValue().pData,
383 i.getKey(),
384 i.getValue().tLastSync
385 ) )
386 {
387 pStore->sync(
388 i.getValue().pData,
389 i.getKey()
390 );
391 iSynced++;
392 i.getValue().tLastSync = time( NULL );
393 }
394 }
395 }
396 if( iSynced > 0 )
397 {
398 pStore->sync();
399 }
400 }
401
402 private:
403 void incRef( const keytype &cId )
404 {
405 TRACE( cId );
406 hEnt.get( cId ).iRefs++;
407 }
408
409 void decRef( const keytype &cId )
410 {
411 TRACE( cId );
412 CacheEntry &e = hEnt.get( cId );
413 e.iRefs--;
414 }
415
416 obtype *getRaw( const keytype &cId )
417 {
418 TRACE( cId );
419 try {
420 return hEnt.get( cId ).pData;
421 }
422 catch( Bu::HashException &e ) {
423 CacheEntry e = {pStore->load( cId ), 0, time( NULL )};
424 pCalc->onLoad( e.pData, cId );
425 hEnt.insert( cId, e );
426 return e.pData;
427 }
428 }
429
430 private:
431 CidHash hEnt;
432 Calc *pCalc;
433 Store *pStore;
434 };
435};
436
437#endif