source: branches/stable/TEMPLATES/cache.h

Last change on this file was 16766, checked in by westram, 6 years ago
File size: 10.0 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : cache.h                                            //
4//   Purpose   : Cache for SmartPtrs                                //
5//                                                                  //
6//   Coded by Ralf Westram (coder@reallysoft.de) in November 2012   //
7//   Institute of Microbiology (Technical University Munich)        //
8//   http://www.arb-home.de/                                        //
9//                                                                  //
10// ================================================================ //
11
12#ifndef CACHE_H
13#define CACHE_H
14
15#ifndef SMARTPTR_H
16#include "smartptr.h"
17#endif
18#ifndef _GLIBCXX_LIST
19#include <list>
20#endif
21
22#if defined(DEBUG)
23#define DUMP_CACHE_STAT
24#endif
25
26#define ca_assert(cond)           arb_assert(cond)
27#define ca_assert_expensive(cond) // arb_assert(cond) // uncomment to enable expensive assertions
28
29// unittests for Cache are in ../PROBE/PT_io.cxx@TEST_CachedPtr
30
31namespace cache {
32    template <typename SMARTPTR> class Cache;
33    template <typename SMARTPTR> class CacheHandle;
34
35    template <typename SMARTPTR> class CacheEntry {
36        typedef CacheEntry<SMARTPTR>* EntryPtr;
37        typedef typename std::list< CacheEntry<SMARTPTR> >::iterator EntryIter;
38
39        static EntryPtr& no_handle() {
40            static EntryPtr none = NULp;
41            return none;
42        }
43
44        EntryPtr& my_handle;
45        SMARTPTR  data;
46        EntryIter iter;
47
48        CacheEntry(EntryPtr& handle)
49            : my_handle(handle)
50        {}
51        CacheEntry(EntryPtr& handle, SMARTPTR& init_data)
52            : my_handle(handle),
53              data(init_data)
54        {}
55        void setClientPtr() { my_handle = this; }
56        void clearClientPtr() { my_handle = NULp; }
57        void setData(SMARTPTR& new_data) { data = new_data; }
58
59        EntryIter getIterator() {
60            ca_assert(data.isSet()); // otherwise iterator may be invalid
61            return iter;
62        }
63        void setIterator(EntryIter newIter) { iter = newIter; }
64
65        friend class Cache<SMARTPTR>;
66
67    public:
68        ~CacheEntry() { ca_assert(!my_handle); } // release before destruction
69        SMARTPTR get_data() const { return data; }
70    };
71
72
73    /*! @class Cache
74     *  @brief Cache for any SmartPtr type
75     */
76    template <typename SMARTPTR> class Cache {
77        typedef CacheEntry<SMARTPTR> Entry;
78        typedef Entry*               EntryPtr;
79        typedef std::list<Entry>     Entries;
80
81        typedef typename Entries::iterator EntryIter;
82
83        Entries cached;
84
85        size_t curr_size; // always == cached.size()
86        size_t max_size;
87
88#if defined(DUMP_CACHE_STAT)
89        size_t insert_count;
90        size_t access_count;
91#endif
92
93#if defined(ASSERTION_USED)
94        bool curr_size_valid() const { return cached.size() == curr_size; }
95#endif
96
97        void keep(size_t kept_elems) {
98            ca_assert(kept_elems <= max_size);
99            size_t count = entries();
100            if (count>kept_elems) {
101                size_t    toRemove = count-kept_elems;
102                EntryIter e        = cached.begin();
103
104                while (toRemove--) e++->clearClientPtr();
105                cached.erase(cached.begin(), e);
106                curr_size = kept_elems;
107                ca_assert_expensive(curr_size_valid());
108                ca_assert(entries() == kept_elems);
109            }
110        }
111
112        EntryIter top() { return --cached.end(); }
113
114#if defined(ASSERTION_USED)
115        bool is_member(EntryIter iter) {
116            for (EntryIter i = cached.begin(); i != cached.end(); ++i) {
117                if (i == iter) return true;
118            }
119            return false;
120        }
121        static bool is_valid_size(size_t Size) { return Size>0; }
122#endif
123
124        void insert(EntryPtr& my_handle, SMARTPTR& data) {
125            ca_assert(data.isSet());
126            if (my_handle) { // re-assign
127                my_handle->setData(data);
128                touch(*my_handle);
129#if defined(DUMP_CACHE_STAT)
130                --access_count; // do not count above touch as access
131#endif
132            }
133            else { // initialization
134                cached.push_back(CacheEntry<SMARTPTR>(my_handle, data));
135                ++curr_size;
136                ca_assert_expensive(curr_size_valid());
137
138                cached.back().setClientPtr();
139                ca_assert(&cached.back() == my_handle);
140
141                my_handle->setIterator(top());
142
143                keep(max_size);
144                ca_assert(my_handle);
145            }
146#if defined(DUMP_CACHE_STAT)
147            ++insert_count;
148#endif
149        }
150        void touch(Entry& entry) {
151            EntryIter iter = entry.getIterator();
152            ca_assert_expensive(is_member(iter));
153            if (iter != top()) { // not LRU entry
154                cached.splice(cached.end(), cached, iter);
155                ca_assert_expensive(is_member(iter));
156                ca_assert(iter == top());
157            }
158#if defined(DUMP_CACHE_STAT)
159            ++access_count;
160#endif
161        }
162        void remove(EntryPtr& my_handle) {
163            ca_assert(my_handle);
164            EntryIter iter = my_handle->getIterator();
165            ca_assert_expensive(is_member(iter));
166            my_handle->clearClientPtr();
167            cached.erase(iter);
168            --curr_size;
169            ca_assert_expensive(curr_size_valid());
170            ca_assert(!my_handle);
171        }
172
173        friend class CacheHandle<SMARTPTR>;
174
175    public:
176
177        /*! create a new Cache
178         *
179         * @param Size specifies how many entries the Cache will hold at a time.
180         * Can be redefined later using resize().
181         *
182         * Each cache entry is a SmartPtr (any flavour) which is represented by a CacheHandle.
183         */
184        Cache(size_t Size) : curr_size(0), max_size(Size) {
185            ca_assert(is_valid_size(Size));
186#if defined(DUMP_CACHE_STAT)
187            access_count = 0;
188            insert_count = 0;
189#endif
190        }
191        /*! destroy the Cache
192         *
193         * Will invalidate all @ref CacheHandle "CacheHandles" assigned to this Cache.
194         *
195         */
196        ~Cache() {
197#if defined(DUMP_CACHE_STAT)
198            printf("Cache-Stat: max_size=%zu inserts=%zu accesses=%zu\n", size(), insert_count, access_count);
199#endif
200            flush();
201        }
202
203        //! @return the number of currently cached CacheHandles
204        size_t entries() const {
205            ca_assert_expensive(curr_size_valid());
206            return curr_size;
207        }
208        //! @return the maximum number of cached CacheHandles
209        size_t size() const { return max_size; }
210        /*! change the size of the Cache.
211         *
212         * @param new_size specifies the number of cache entries (as in constructor)
213         */
214        void resize(size_t new_size) {
215            ca_assert(is_valid_size(new_size));
216            if (new_size<max_size) keep(new_size);
217            max_size = new_size;
218        }
219        //! flush the cache, i.e. uncache all elements
220        void flush() { keep(0); }
221    };
222
223    /*! @class CacheHandle
224     *  @brief Handle for Cache entries.
225     */
226    template <typename SMARTPTR> class CacheHandle : virtual Noncopyable {
227        CacheEntry<SMARTPTR> *entry;
228    public:
229        /*! create a new, unbound CacheHandle
230         *
231         * Each (potential) cache entry needs a CacheHandle on client side.
232         */
233        CacheHandle() : entry(NULp) {}
234
235        /*! destroys a CacheHandle
236         *
237         * Before destrying it, you need to either call release() or Cache::flush().
238         */
239        ~CacheHandle() { ca_assert(!is_cached()); }
240
241        /*! assign data to a CacheHandle
242         *
243         * @param data SmartPtr containing client data (may not be NULp)
244         * @param cache Cache in which data shall be stored
245         *
246         * The assigned data is stored in the Cache.
247         *
248         * When the cache is filled, calls to assign() will invalidate other
249         * cache entries and their CacheHandles.
250         */
251        void assign(SMARTPTR data, Cache<SMARTPTR>& cache) {
252            ca_assert(data.isSet()); // use release() to set a CacheHandle to NULp
253            cache.insert(entry, data);
254            ca_assert(is_cached());
255        }
256        /*! read data from cache
257         *
258         *  @param cache Cache to which data has been assigned earlier
259         *  @return SmartPtr containing data (as used in assign())
260         *
261         *  You need to check whether the assigned data still is cached by calling is_cached()
262         *  before you can use this function. e.g. like follows
263         *
264         *  @code
265         *  if (!handle.is_cached()) handle.assign(generateYourData(), cache);
266         *  smartptr = handle.access(cache);
267         *  @endcode
268         *
269         *  The data behind the returned smartptr will stay valid until its destruction,
270         *  even if the handle is invalidated by other code.
271         *
272         *  Calling access() will also make this CacheHandle the "newest" handle in cache,
273         *  i.e. the one with the longest lifetime.
274         */
275        SMARTPTR access(Cache<SMARTPTR>& cache) {
276            ca_assert(entry); // you did not check whether entry still is_cached()
277            cache.touch(*entry);
278            return entry->get_data();
279        }
280        /*! actively release data from cache
281         *
282         *  @param cache Cache to which data has been assigned earlier
283         *
284         * Call this either
285         * - before destroying the CacheHandle or
286         * - when you know the data will no longer be needed, to safe cache-space for other entries.
287         */
288        void release(Cache<SMARTPTR>& cache) {
289            if (entry) {
290                cache.remove(entry);
291                ca_assert(!entry); // should be NULLed by cache.remove
292                ca_assert(!is_cached());
293            }
294        }
295        /*! check status of CacheHandle
296         * @return true if CacheHandle (still) contains data
297         */
298        bool is_cached() const { return entry; }
299    };
300
301};
302
303#else
304#error cache.h included twice
305#endif // CACHE_H
Note: See TracBrowser for help on using the repository browser.