summaryrefslogtreecommitdiff
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
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.
-rw-r--r--src/experimental/cache.h437
-rw-r--r--src/experimental/cachecalc.h63
-rw-r--r--src/experimental/cachestore.h46
-rw-r--r--src/experimental/cachestorefiles.cpp9
-rw-r--r--src/experimental/cachestorefiles.h211
-rw-r--r--src/experimental/cachestoremyriad.cpp9
-rw-r--r--src/experimental/cachestoremyriad.h158
-rw-r--r--src/tests/cache.cpp719
-rw-r--r--src/unstable/cachebase.cpp (renamed from src/experimental/cache.cpp)2
-rw-r--r--src/unstable/cachebase.h514
-rw-r--r--src/unstable/cacheobject.cpp (renamed from src/experimental/cachestore.cpp)3
-rw-r--r--src/unstable/cacheobject.h84
-rw-r--r--src/unstable/myriadcache.cpp (renamed from src/experimental/cachecalc.cpp)2
-rw-r--r--src/unstable/myriadcache.h126
14 files changed, 739 insertions, 1644 deletions
diff --git a/src/experimental/cache.h b/src/experimental/cache.h
deleted file mode 100644
index b9d1b7a..0000000
--- a/src/experimental/cache.h
+++ /dev/null
@@ -1,437 +0,0 @@
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_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
diff --git a/src/experimental/cachecalc.h b/src/experimental/cachecalc.h
deleted file mode 100644
index c6cf33a..0000000
--- a/src/experimental/cachecalc.h
+++ /dev/null
@@ -1,63 +0,0 @@
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_CALC_H
9#define BU_CACHE_CALC_H
10
11#include "bu/trace.h"
12
13#include <time.h>
14
15namespace Bu
16{
17 template<class keytype, class obtype> class Cache;
18
19 template<class keytype, class obtype>
20 class CacheCalc
21 {
22 friend class Cache<keytype, obtype>;
23 private:
24 typedef Cache<keytype, obtype> MyCache;
25 public:
26 CacheCalc() :
27 pCache( (MyCache *)0 )
28 {
29 TRACE();
30 }
31
32 virtual ~CacheCalc()
33 {
34 TRACE();
35 }
36
37 virtual void onLoad( obtype *pSrc, const keytype &key )=0;
38 virtual void onUnload( obtype *pSrc, const keytype &key )=0;
39 virtual void onDestroy( obtype *pSrc, const keytype &key )=0;
40 virtual void onDestroy( const keytype &key )=0;
41 virtual bool shouldSync( obtype *pSrc, const keytype &key,
42 time_t tLastSync )=0;
43 virtual void onTick() { };
44
45 protected:
46 MyCache *getCache()
47 {
48 TRACE();
49 return pCache;
50 }
51
52 private:
53 void setCache( MyCache *pCache )
54 {
55 TRACE();
56 this->pCache = pCache;
57 }
58
59 MyCache *pCache;
60 };
61};
62
63#endif
diff --git a/src/experimental/cachestore.h b/src/experimental/cachestore.h
deleted file mode 100644
index 48a84ad..0000000
--- a/src/experimental/cachestore.h
+++ /dev/null
@@ -1,46 +0,0 @@
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_STORE_H
9#define BU_CACHE_STORE_H
10
11#include "bu/list.h"
12
13namespace Bu
14{
15 /**
16 * Handles I/O for data in the cache. This also assigns ID's to the newly
17 * created objects that are requested through this system.
18 */
19 template<class keytype, class obtype>
20 class CacheStore
21 {
22 public:
23 CacheStore()
24 {
25 }
26
27 virtual ~CacheStore()
28 {
29 }
30
31 virtual obtype *load( const keytype &key )=0;
32 virtual void unload( obtype *pObj, const keytype &key )=0;
33 virtual keytype create( obtype *pSrc )=0;
34 virtual void sync()=0;
35 virtual void sync( obtype *pObj, const keytype &key )=0;
36 virtual void destroy( obtype *pObj, const keytype &key )=0;
37 virtual void destroy( const keytype &key )=0;
38 virtual bool has( const keytype &key )=0;
39 virtual Bu::List<keytype> getKeys() { return Bu::List<keytype>(); }
40 virtual int getSize() { return -1; }
41
42 private:
43 };
44};
45
46#endif
diff --git a/src/experimental/cachestorefiles.cpp b/src/experimental/cachestorefiles.cpp
deleted file mode 100644
index bf65660..0000000
--- a/src/experimental/cachestorefiles.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
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#include "bu/cachestorefiles.h"
9
diff --git a/src/experimental/cachestorefiles.h b/src/experimental/cachestorefiles.h
deleted file mode 100644
index 90b0a00..0000000
--- a/src/experimental/cachestorefiles.h
+++ /dev/null
@@ -1,211 +0,0 @@
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_STORE_FILES_H
9#define BU_CACHE_STORE_FILES_H
10
11#include "bu/string.h"
12#include "bu/file.h"
13#include "bu/cachestore.h"
14#include "bu/archive.h"
15#include "bu/membuf.h"
16#include "bu/formatter.h"
17#include "bu/sio.h"
18
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <dirent.h>
22#include <unistd.h>
23
24namespace Bu
25{
26 template<class keytype, class obtype>
27 keytype __cacheGetKey( const obtype *pObj );
28
29 template<class keytype, class obtype>
30 obtype *__cacheStoreFilesAlloc( const keytype &key )
31 {
32 return new obtype();
33 }
34
35 template<class keytype, class obtype>
36 void __cacheStoreFilesStore( Bu::Stream &s, obtype &rObj,
37 const keytype & )
38 {
39 Bu::Archive ar( s, Bu::Archive::save );
40 ar << rObj;
41 }
42
43 template<class keytype, class obtype>
44 obtype *__cacheStoreFilesLoad( Bu::Stream &s, const keytype &key )
45 {
46 obtype *pObj = __cacheStoreFilesAlloc<keytype, obtype>( key );
47 Bu::Archive ar( s, Bu::Archive::load );
48 ar >> (*pObj);
49 return pObj;
50 }
51
52 template<class keytype, class obtype>
53 class CacheStoreFiles : public CacheStore<keytype, obtype>
54 {
55 public:
56 CacheStoreFiles( const Bu::String &sPrefix ) :
57 sPrefix( sPrefix )
58 {
59 if( access( sPrefix.getStr(), W_OK|R_OK|X_OK ) )
60 {
61#ifdef WIN32
62 mkdir( sPrefix.getStr() );
63#else
64 mkdir( sPrefix.getStr(), 0755 );
65#endif
66 }
67 }
68
69 virtual ~CacheStoreFiles()
70 {
71 }
72
73 virtual obtype *load( const keytype &key )
74 {
75// try
76// {
77 Bu::MemBuf mb;
78 Bu::Formatter f( mb );
79 f << sPrefix << "/" << key;
80 Bu::File fIn( mb.getString(), Bu::File::Read );
81 obtype *pOb = __cacheStoreFilesLoad<keytype, obtype>( fIn, key );
82 return pOb;
83// }
84// catch( std::exception &e )
85// {
86// throw Bu::HashException( e.what() );
87// }
88 }
89
90 virtual void unload( obtype *pObj, const keytype & )
91 {
92 delete pObj;
93 }
94
95 virtual keytype create( obtype *pSrc )
96 {
97 keytype key = __cacheGetKey<keytype, obtype>( pSrc );
98 Bu::MemBuf mb;
99 Bu::Formatter f( mb );
100 f << sPrefix << "/" << key;
101
102 Bu::File fTouch( mb.getString(), Bu::File::WriteNew );
103
104 return key;
105 }
106
107 virtual void sync()
108 {
109 }
110
111 virtual void sync( obtype *pSrc, const keytype &key )
112 {
113 Bu::MemBuf mb;
114 Bu::Formatter f( mb );
115 f << sPrefix << "/" << key;
116
117 Bu::File fOut( mb.getString(), Bu::File::WriteNew );
118 __cacheStoreFilesStore<keytype, obtype>( fOut, *pSrc, key );
119 }
120
121 virtual void destroy( obtype *pObj, const keytype &key )
122 {
123 Bu::MemBuf mb;
124 Bu::Formatter f( mb );
125 f << sPrefix << "/" << key;
126
127 unlink( mb.getString().getStr() );
128 delete pObj;
129 }
130
131 virtual void destroy( const keytype &key )
132 {
133 Bu::MemBuf mb;
134 Bu::Formatter f( mb );
135 f << sPrefix << "/" << key;
136
137 unlink( mb.getString().getStr() );
138 }
139
140 virtual bool has( const keytype &key )
141 {
142 Bu::MemBuf mb;
143 Bu::Formatter f( mb );
144 f << sPrefix << "/";
145 Bu::String sBase = mb.getString();
146 f << key;
147
148 if( sBase == mb.getString() )
149 return false;
150
151 return access( mb.getString().getStr(), F_OK ) == 0;
152 }
153
154 virtual Bu::List<keytype> getKeys()
155 {
156 DIR *dir = opendir( sPrefix.getStr() );
157 struct dirent *de;
158 Bu::List<keytype> lKeys;
159
160 while( (de = readdir( dir ) ) )
161 {
162 if( de->d_type != DT_REG )
163 continue;
164
165 keytype tmp;
166 Bu::MemBuf mb( de->d_name );
167 Bu::Formatter f( mb );
168 try
169 {
170 Fmt fm;
171 fm.tokenize( false );
172 f << fm;
173 f >> tmp;
174 }
175 catch( Bu::ExceptionBase &e )
176 {
177 Bu::sio << "Parse error in dir-scan: " << e.what()
178 << Bu::sio.nl;
179 }
180 lKeys.append( tmp );
181 }
182 closedir( dir );
183
184 return lKeys;
185 }
186
187 virtual int getSize()
188 {
189 DIR *dir = opendir( sPrefix.getStr() );
190 struct dirent *de;
191 int iCount = 0;
192
193 while( (de = readdir( dir ) ) )
194 {
195 if( de->d_type != DT_REG )
196 continue;
197
198 iCount++;
199 }
200 closedir( dir );
201
202 return iCount;
203 }
204
205 private:
206 Bu::String sPrefix;
207 };
208
209};
210
211#endif
diff --git a/src/experimental/cachestoremyriad.cpp b/src/experimental/cachestoremyriad.cpp
deleted file mode 100644
index f6edc7a..0000000
--- a/src/experimental/cachestoremyriad.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
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#include "bu/cachestoremyriad.h"
9
diff --git a/src/experimental/cachestoremyriad.h b/src/experimental/cachestoremyriad.h
deleted file mode 100644
index 798d205..0000000
--- a/src/experimental/cachestoremyriad.h
+++ /dev/null
@@ -1,158 +0,0 @@
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_STORE_MYRIAD_H
9#define BU_CACHE_STORE_MYRIAD_H
10
11#include "bu/string.h"
12#include "bu/stream.h"
13#include "bu/myriad.h"
14#include "bu/cachestore.h"
15#include "bu/myriadstream.h"
16
17#include "bu/archive.h"
18
19namespace Bu
20{
21 template<class keytype, class obtype>
22 keytype __cacheGetKey( const obtype *pObj );
23
24 template<class keytype, class obtype>
25 obtype *__cacheStoreMyriadAlloc( const keytype &key )
26 {
27 return new obtype();
28 }
29
30 template<class keytype, class obtype>
31 void __cacheStoreMyriadStore( Bu::Stream &s, obtype &rObj,
32 const keytype & )
33 {
34 Bu::Archive ar( s, Bu::Archive::save );
35 ar << rObj;
36 }
37
38 template<class keytype, class obtype>
39 obtype *__cacheStoreMyriadLoad( Bu::Stream &s, const keytype &key )
40 {
41 obtype *pObj = __cacheStoreMyriadAlloc<keytype, obtype>( key );
42 Bu::Archive ar( s, Bu::Archive::load );
43 ar >> (*pObj);
44 return pObj;
45 }
46
47 template<class keytype, class obtype>
48 class CacheStoreMyriad : public CacheStore<keytype, obtype>
49 {
50 public:
51 CacheStoreMyriad( Bu::Stream &sArch,
52 int iBlockSize=512, int iPreAllocate=8 ) :
53 mStore( sArch, iBlockSize, iPreAllocate )
54 {
55 try
56 {
57 MyriadStream ns = mStore.openStream( 1 );
58 Bu::Archive ar( ns, Bu::Archive::load );
59 ar >> hId;
60 }
61 catch( Bu::MyriadException &e )
62 {
63 int iStream = mStore.createStream();
64 if( iStream != 1 )
65 throw Bu::ExceptionBase("That's...horrible...id = %d.\n\n",
66 iStream );
67 MyriadStream ns = mStore.openStream( 1 );
68 Bu::Archive ar( ns, Bu::Archive::save );
69 ar << hId;
70 }
71 }
72
73 virtual ~CacheStoreMyriad()
74 {
75 MyriadStream ns = mStore.openStream( 1 );
76 Bu::Archive ar( ns, Bu::Archive::save );
77 ar << hId;
78 }
79
80 virtual obtype *load( const keytype &key )
81 {
82 int iStream = hId.get( key );
83 MyriadStream ns = mStore.openStream( iStream );
84 obtype *pOb = __cacheStoreMyriadLoad<keytype, obtype>( ns, key );
85 return pOb;
86 }
87
88 virtual void unload( obtype *pObj, const keytype & )
89 {
90 delete pObj;
91 }
92
93 virtual keytype create( obtype *pSrc )
94 {
95 keytype key = __cacheGetKey<keytype, obtype>( pSrc );
96 int iStream = mStore.createStream();
97 hId.insert( key, iStream );
98 MyriadStream ns = mStore.openStream( iStream );
99 __cacheStoreMyriadStore<keytype, obtype>( ns, *pSrc, key );
100 ns.setSize( ns.tell() );
101 return key;
102 }
103
104 virtual void sync()
105 {
106 MyriadStream ns = mStore.openStream( 1 );
107 Bu::Archive ar( ns, Bu::Archive::save );
108 ar << hId;
109 ns.setSize( ns.tell() );
110 mStore.sync();
111 }
112
113 virtual void sync( obtype *pSrc, const keytype &key )
114 {
115 int iStream = hId.get( key );
116 MyriadStream ns = mStore.openStream( iStream );
117 __cacheStoreMyriadStore<keytype, obtype>( ns, *pSrc, key );
118 ns.setSize( ns.tell() );
119 }
120
121 virtual void destroy( obtype *pObj, const keytype &key )
122 {
123 int iStream = hId.get( key );
124 mStore.deleteStream( iStream );
125 hId.erase( key );
126 delete pObj;
127 }
128
129 virtual void destroy( const keytype &key )
130 {
131 int iStream = hId.get( key );
132 mStore.deleteStream( iStream );
133 hId.erase( key );
134 }
135
136 virtual bool has( const keytype &key )
137 {
138 return hId.has( key );
139 }
140
141 virtual Bu::List<keytype> getKeys()
142 {
143 return hId.getKeys();
144 }
145
146 virtual int getSize()
147 {
148 return hId.getSize();
149 }
150
151 private:
152 Myriad mStore;
153 typedef Bu::Hash<keytype, long> StreamHash;
154 StreamHash hId;
155 };
156};
157
158#endif
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{
diff --git a/src/experimental/cache.cpp b/src/unstable/cachebase.cpp
index 840682a..69b378e 100644
--- a/src/experimental/cache.cpp
+++ b/src/unstable/cachebase.cpp
@@ -5,4 +5,4 @@
5 * terms of the license contained in the file LICENSE. 5 * terms of the license contained in the file LICENSE.
6 */ 6 */
7 7
8#include "bu/cache.h" 8#include "bu/cachebase.h"
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
diff --git a/src/experimental/cachestore.cpp b/src/unstable/cacheobject.cpp
index 2082a50..eedb645 100644
--- a/src/experimental/cachestore.cpp
+++ b/src/unstable/cacheobject.cpp
@@ -5,5 +5,4 @@
5 * terms of the license contained in the file LICENSE. 5 * terms of the license contained in the file LICENSE.
6 */ 6 */
7 7
8#include "bu/cachestore.h" 8#include "bu/cacheobject.h"
9
diff --git a/src/unstable/cacheobject.h b/src/unstable/cacheobject.h
new file mode 100644
index 0000000..0088685
--- /dev/null
+++ b/src/unstable/cacheobject.h
@@ -0,0 +1,84 @@
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_OBJECT_H
9#define BU_CACHE_OBJECT_H
10
11#ifndef NULL
12#define NULL 0
13#endif
14
15namespace Bu
16{
17 template<typename keytype, typename obtype> class CacheBase;
18 template<typename keytype, typename obtype> class CacheEntry;
19
20 template<typename keytype, typename obtype>
21 class CacheObject
22 {
23 friend class CacheBase<keytype, obtype>;
24 public:
25 CacheObject() :
26 pCache( NULL ),
27 bChanged( false )
28 {
29 }
30
31 virtual ~CacheObject()
32 {
33 }
34
35 typedef CacheBase<keytype, obtype> CacheType;
36
37 virtual keytype getKey() const=0;
38 virtual int getPersistenceScore() const { return 0; }
39
40 void lock()
41 {
42 pEntry->lock();
43 }
44
45 void unlock()
46 {
47 pEntry->unlock();
48 }
49
50 int getRefCount() const
51 {
52 return pEntry->getRefCount();
53 }
54
55 bool hasChanged() const
56 {
57 return bChanged;
58 }
59
60 protected:
61 void changed( bool bChanged=true )
62 {
63 if( this->bChanged == false && bChanged == true )
64 pCache->objectChanged( getKey() );
65
66 this->bChanged = bChanged;
67 }
68
69 private:
70 typedef CacheEntry<keytype, obtype> Entry;
71 void setCache( CacheType *pCache, Entry *pEntry )
72 {
73 this->pCache = pCache;
74 this->pEntry = pEntry;
75 }
76
77 private:
78 CacheType *pCache;
79 Entry *pEntry;
80 bool bChanged;
81 };
82}
83
84#endif
diff --git a/src/experimental/cachecalc.cpp b/src/unstable/myriadcache.cpp
index 7e2940c..0a997f8 100644
--- a/src/experimental/cachecalc.cpp
+++ b/src/unstable/myriadcache.cpp
@@ -5,4 +5,4 @@
5 * terms of the license contained in the file LICENSE. 5 * terms of the license contained in the file LICENSE.
6 */ 6 */
7 7
8#include "bu/cachecalc.h" 8#include "bu/myriadcache.h"
diff --git a/src/unstable/myriadcache.h b/src/unstable/myriadcache.h
new file mode 100644
index 0000000..f8c0e00
--- /dev/null
+++ b/src/unstable/myriadcache.h
@@ -0,0 +1,126 @@
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_MYRIAD_CACHE_H
9#define BU_MYRIAD_CACHE_H
10
11#include "bu/cachebase.h"
12#include "bu/myriad.h"
13#include "bu/myriadstream.h"
14#include "bu/file.h"
15#include "bu/streamstack.h"
16
17namespace Bu
18{
19 template<typename keytype, typename obtype>
20 class MyriadCache : public Bu::CacheBase<keytype, obtype>
21 {
22 public:
23 MyriadCache( Bu::Stream &sStore, int iBlockSize=512, int iPreallocate=8 ) :
24 sStore( sStore ),
25 mStore( sStore, iBlockSize, iPreallocate ),
26 bStructureChanged( false )
27 {
28 try
29 {
30 Bu::ReadWriteMutex::ReadLocker l( rwStore );
31 Bu::MyriadStream ms = mStore.openStream( 1 );
32 Bu::Archive ar( ms, Bu::Archive::load );
33 uint8_t uVer;
34 ar >> uVer;
35 switch( uVer )
36 {
37 case 0:
38 ar >> hIndex;
39 break;
40 }
41 }
42 catch(...)
43 {
44 if( mStore.createStreamWithId( 1 ) != 1 )
45 throw Bu::ExceptionBase("Error creating index stream.");
46
47 _sync();
48 }
49 }
50
51 virtual ~MyriadCache()
52 {
53 _sync();
54 }
55
56 using typename Bu::CacheBase<keytype,obtype>::KeyList;
57
58 virtual KeyList getKeys() const
59 {
60 Bu::ReadWriteMutex::ReadLocker rl( rwStore );
61 return hIndex.getKeys();
62 }
63
64 virtual int getSize() const
65 {
66 Bu::ReadWriteMutex::ReadLocker rl( rwStore );
67 return hIndex.getSize();
68 }
69
70 protected:
71 virtual void _create( const obtype *o )
72 {
73 Bu::ReadWriteMutex::WriteLocker wl( rwStore );
74 hIndex.insert( o->getKey(), mStore.createStream() );
75 _save( o );
76
77 bStructureChanged = true;
78 }
79
80 virtual void _erase( const keytype &k )
81 {
82 Bu::ReadWriteMutex::WriteLocker wl( rwStore );
83 mStore.deleteStream( hIndex.get( k ) );
84 hIndex.erase( k );
85
86 bStructureChanged = true;
87 }
88
89 virtual obtype *_load( const keytype &k )
90 {
91 Bu::MyriadStream ms = mStore.openStream( hIndex.get( k ) );
92 return _cacheObjectLoad<obtype>( ms );
93 }
94
95 virtual void _save( const obtype *o )
96 {
97 Bu::MyriadStream ms = mStore.openStream( hIndex.get( o->getKey() ) );
98 _cacheObjectSave( ms, o );
99 ms.setSize( ms.tell() );
100 }
101
102 virtual void _sync()
103 {
104 Bu::ReadWriteMutex::ReadLocker wl( rwStore );
105 if( !bStructureChanged )
106 return;
107
108 Bu::MyriadStream ms = mStore.openStream( 1 );
109 Bu::Archive ar( ms, Bu::Archive::save );
110 ar << (uint8_t)0 << hIndex;
111 ar.close();
112 ms.setSize( ms.tell() );
113
114 bStructureChanged = false;
115 }
116
117 private:
118 Bu::Stream &sStore;
119 Bu::Myriad mStore;
120 Bu::Hash<keytype, int> hIndex;
121 mutable Bu::ReadWriteMutex rwStore;
122 bool bStructureChanged;
123 };
124}
125
126#endif