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 | |
34 | namespace Poco { |
35 | |
36 | |
37 | template <class TKey, class TValue, class TStrategy, class TMutex = FastMutex, class TEventMutex = FastMutex> |
38 | class AbstractCache |
39 | /// An AbstractCache is the interface of all caches. |
40 | { |
41 | public: |
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 | |
179 | protected: |
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 | |
357 | private: |
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 | |