source: tags/ms_r16q3/TEMPLATES/cache.h

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