1//
2// AbstractCache.h
3//
4// Library: Foundation
5// Package: Cache
6// Module: AbstractCache
7//
8// Definition of the AbstractCache class.
9//
10// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
11// and Contributors.
12//
13// SPDX-License-Identifier: BSL-1.0
14//
15
16
17#ifndef Foundation_AbstractCache_INCLUDED
18#define Foundation_AbstractCache_INCLUDED
19
20
21#include "Poco/KeyValueArgs.h"
22#include "Poco/ValidArgs.h"
23#include "Poco/Mutex.h"
24#include "Poco/Exception.h"
25#include "Poco/FIFOEvent.h"
26#include "Poco/EventArgs.h"
27#include "Poco/Delegate.h"
28#include "Poco/SharedPtr.h"
29#include <map>
30#include <set>
31#include <cstddef>
32
33
34namespace Poco {
35
36
37template <class TKey, class TValue, class TStrategy, class TMutex = FastMutex, class TEventMutex = FastMutex>
38class AbstractCache
39 /// An AbstractCache is the interface of all caches.
40{
41public:
42 FIFOEvent<const KeyValueArgs<TKey, TValue >, TEventMutex > Add;
43 FIFOEvent<const KeyValueArgs<TKey, TValue >, TEventMutex > Update;
44 FIFOEvent<const TKey, TEventMutex> Remove;
45 FIFOEvent<const TKey, TEventMutex> Get;
46 FIFOEvent<const EventArgs, TEventMutex> Clear;
47
48 typedef std::map<TKey, SharedPtr<TValue > > DataHolder;
49 typedef typename DataHolder::iterator Iterator;
50 typedef typename DataHolder::const_iterator ConstIterator;
51 typedef std::set<TKey> KeySet;
52
53 AbstractCache()
54 {
55 initialize();
56 }
57
58 AbstractCache(const TStrategy& strat): _strategy(strat)
59 {
60 initialize();
61 }
62
63 virtual ~AbstractCache()
64 {
65 try
66 {
67 uninitialize();
68 }
69 catch (...)
70 {
71 poco_unexpected();
72 }
73 }
74
75 void add(const TKey& key, const TValue& val)
76 /// Adds the key value pair to the cache.
77 /// If for the key already an entry exists, it will be overwritten.
78 {
79 typename TMutex::ScopedLock lock(_mutex);
80 doAdd(key, val);
81 }
82
83 void update(const TKey& key, const TValue& val)
84 /// Adds the key value pair to the cache. Note that adding a NULL SharedPtr will fail!
85 /// If for the key already an entry exists, it will be overwritten.
86 /// The difference to add is that no remove or add events are thrown in this case,
87 /// just a simply silent update is performed
88 /// If the key does not exist the behavior is equal to add, ie. an add event is thrown
89 {
90 typename TMutex::ScopedLock lock(_mutex);
91 doUpdate(key, val);
92 }
93
94 void add(const TKey& key, SharedPtr<TValue > val)
95 /// Adds the key value pair to the cache. Note that adding a NULL SharedPtr will fail!
96 /// If for the key already an entry exists, it will be overwritten, ie. first a remove event
97 /// is thrown, then a add event
98 {
99 typename TMutex::ScopedLock lock(_mutex);
100 doAdd(key, val);
101 }
102
103 void update(const TKey& key, SharedPtr<TValue > val)
104 /// Adds the key value pair to the cache. Note that adding a NULL SharedPtr will fail!
105 /// If for the key already an entry exists, it will be overwritten.
106 /// The difference to add is that no remove or add events are thrown in this case,
107 /// just an Update is thrown
108 /// If the key does not exist the behavior is equal to add, ie. an add event is thrown
109 {
110 typename TMutex::ScopedLock lock(_mutex);
111 doUpdate(key, val);
112 }
113
114 void remove(const TKey& key)
115 /// Removes an entry from the cache. If the entry is not found,
116 /// the remove is ignored.
117 {
118 typename TMutex::ScopedLock lock(_mutex);
119 Iterator it = _data.find(key);
120 doRemove(it);
121 }
122
123 bool has(const TKey& key) const
124 /// Returns true if the cache contains a value for the key.
125 {
126 typename TMutex::ScopedLock lock(_mutex);
127 return doHas(key);
128 }
129
130 SharedPtr<TValue> get(const TKey& key)
131 /// Returns a SharedPtr of the value. The SharedPointer will remain valid
132 /// even when cache replacement removes the element.
133 /// If for the key no value exists, an empty SharedPtr is returned.
134 {
135 typename TMutex::ScopedLock lock(_mutex);
136 return doGet (key);
137 }
138
139 void clear()
140 /// Removes all elements from the cache.
141 {
142 typename TMutex::ScopedLock lock(_mutex);
143 doClear();
144 }
145
146 std::size_t size()
147 /// Returns the number of cached elements
148 {
149 typename TMutex::ScopedLock lock(_mutex);
150 doReplace();
151 return _data.size();
152 }
153
154 void forceReplace()
155 /// Forces cache replacement. Note that Poco's cache strategy use for efficiency reason no background thread
156 /// which periodically triggers cache replacement. Cache Replacement is only started when the cache is modified
157 /// from outside, i.e. add is called, or when a user tries to access an cache element via get.
158 /// In some cases, i.e. expire based caching where for a long time no access to the cache happens,
159 /// it might be desirable to be able to trigger cache replacement manually.
160 {
161 typename TMutex::ScopedLock lock(_mutex);
162 doReplace();
163 }
164
165 std::set<TKey> getAllKeys()
166 /// Returns a copy of all keys stored in the cache
167 {
168 typename TMutex::ScopedLock lock(_mutex);
169 doReplace();
170 ConstIterator it = _data.begin();
171 ConstIterator itEnd = _data.end();
172 std::set<TKey> result;
173 for (; it != itEnd; ++it)
174 result.insert(it->first);
175
176 return result;
177 }
178
179protected:
180 mutable FIFOEvent<ValidArgs<TKey> > IsValid;
181 mutable FIFOEvent<KeySet> Replace;
182
183 void initialize()
184 /// Sets up event registration.
185 {
186 Add += Delegate<TStrategy, const KeyValueArgs<TKey, TValue> >(&_strategy, &TStrategy::onAdd);
187 Update += Delegate<TStrategy, const KeyValueArgs<TKey, TValue> >(&_strategy, &TStrategy::onUpdate);
188 Remove += Delegate<TStrategy, const TKey>(&_strategy, &TStrategy::onRemove);
189 Get += Delegate<TStrategy, const TKey>(&_strategy, &TStrategy::onGet);
190 Clear += Delegate<TStrategy, const EventArgs>(&_strategy, &TStrategy::onClear);
191 IsValid += Delegate<TStrategy, ValidArgs<TKey> >(&_strategy, &TStrategy::onIsValid);
192 Replace += Delegate<TStrategy, KeySet>(&_strategy, &TStrategy::onReplace);
193 }
194
195 void uninitialize()
196 /// Reverts event registration.
197 {
198 Add -= Delegate<TStrategy, const KeyValueArgs<TKey, TValue> >(&_strategy, &TStrategy::onAdd );
199 Update -= Delegate<TStrategy, const KeyValueArgs<TKey, TValue> >(&_strategy, &TStrategy::onUpdate);
200 Remove -= Delegate<TStrategy, const TKey>(&_strategy, &TStrategy::onRemove);
201 Get -= Delegate<TStrategy, const TKey>(&_strategy, &TStrategy::onGet);
202 Clear -= Delegate<TStrategy, const EventArgs>(&_strategy, &TStrategy::onClear);
203 IsValid -= Delegate<TStrategy, ValidArgs<TKey> >(&_strategy, &TStrategy::onIsValid);
204 Replace -= Delegate<TStrategy, KeySet>(&_strategy, &TStrategy::onReplace);
205 }
206
207 void doAdd(const TKey& key, const TValue& val)
208 /// Adds the key value pair to the cache.
209 /// If for the key already an entry exists, it will be overwritten.
210 {
211 Iterator it = _data.find(key);
212 doRemove(it);
213
214 KeyValueArgs<TKey, TValue> args(key, val);
215 Add.notify(this, args);
216 _data.insert(std::make_pair(key, SharedPtr<TValue>(new TValue(val))));
217
218 doReplace();
219 }
220
221 void doAdd(const TKey& key, SharedPtr<TValue>& val)
222 /// Adds the key value pair to the cache.
223 /// If for the key already an entry exists, it will be overwritten.
224 {
225 Iterator it = _data.find(key);
226 doRemove(it);
227
228 KeyValueArgs<TKey, TValue> args(key, *val);
229 Add.notify(this, args);
230 _data.insert(std::make_pair(key, val));
231
232 doReplace();
233 }
234
235 void doUpdate(const TKey& key, const TValue& val)
236 /// Adds the key value pair to the cache.
237 /// If for the key already an entry exists, it will be overwritten.
238 {
239 KeyValueArgs<TKey, TValue> args(key, val);
240 Iterator it = _data.find(key);
241 if (it == _data.end())
242 {
243 Add.notify(this, args);
244 _data.insert(std::make_pair(key, SharedPtr<TValue>(new TValue(val))));
245 }
246 else
247 {
248 Update.notify(this, args);
249 it->second = SharedPtr<TValue>(new TValue(val));
250 }
251
252 doReplace();
253 }
254
255 void doUpdate(const TKey& key, SharedPtr<TValue>& val)
256 /// Adds the key value pair to the cache.
257 /// If for the key already an entry exists, it will be overwritten.
258 {
259 KeyValueArgs<TKey, TValue> args(key, *val);
260 Iterator it = _data.find(key);
261 if (it == _data.end())
262 {
263 Add.notify(this, args);
264 _data.insert(std::make_pair(key, val));
265 }
266 else
267 {
268 Update.notify(this, args);
269 it->second = val;
270 }
271
272 doReplace();
273 }
274
275 void doRemove(Iterator it)
276 /// Removes an entry from the cache. If the entry is not found
277 /// the remove is ignored.
278 {
279 if (it != _data.end())
280 {
281 Remove.notify(this, it->first);
282 _data.erase(it);
283 }
284 }
285
286 bool doHas(const TKey& key) const
287 /// Returns true if the cache contains a value for the key
288 {
289 // ask the strategy if the key is valid
290 ConstIterator it = _data.find(key);
291 bool result = false;
292
293 if (it != _data.end())
294 {
295 ValidArgs<TKey> args(key);
296 IsValid.notify(this, args);
297 result = args.isValid();
298 }
299
300 return result;
301 }
302
303 SharedPtr<TValue> doGet(const TKey& key)
304 /// Returns a SharedPtr of the cache entry, returns 0 if for
305 /// the key no value was found
306 {
307 Iterator it = _data.find(key);
308 SharedPtr<TValue> result;
309
310 if (it != _data.end())
311 {
312 // inform all strategies that a read-access to an element happens
313 Get.notify(this, key);
314 // ask all strategies if the key is valid
315 ValidArgs<TKey> args(key);
316 IsValid.notify(this, args);
317
318 if (!args.isValid())
319 {
320 doRemove(it);
321 }
322 else
323 {
324 result = it->second;
325 }
326 }
327
328 return result;
329 }
330
331 void doClear()
332 {
333 static EventArgs _emptyArgs;
334 Clear.notify(this, _emptyArgs);
335 _data.clear();
336 }
337
338 void doReplace()
339 {
340 std::set<TKey> delMe;
341 Replace.notify(this, delMe);
342 // delMe contains the to be removed elements
343 typename std::set<TKey>::const_iterator it = delMe.begin();
344 typename std::set<TKey>::const_iterator endIt = delMe.end();
345
346 for (; it != endIt; ++it)
347 {
348 Iterator itH = _data.find(*it);
349 doRemove(itH);
350 }
351 }
352
353 TStrategy _strategy;
354 mutable DataHolder _data;
355 mutable TMutex _mutex;
356
357private:
358 AbstractCache(const AbstractCache& aCache);
359 AbstractCache& operator = (const AbstractCache& aCache);
360};
361
362
363} // namespace Poco
364
365
366#endif // Foundation_AbstractCache_INCLUDED
367