Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
TimedCache.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/Containers/Mapping.h"
7#include "Stroika/Foundation/Execution/Common.h"
8
10
11 /*
12 ********************************************************************************
13 ************************* TimedCache<KEY,VALUE,TRAITS> *************************
14 ********************************************************************************
15 */
16 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
17 template <typename K>
18 inline auto TimedCache<KEY, VALUE, TRAITS>::MyResult_::MakeCacheElement (const K& key) const -> CacheElement
19 {
20 if constexpr (kTrackExpiration) {
21 if constexpr (same_as<void, VALUE>) {
22 return CacheElement{.fKey = key, .fExpiresAt = fExpiresAt};
23 }
24 else {
25 return CacheElement{.fKey = key, .fValue = fResult, .fExpiresAt = fExpiresAt};
26 }
27 }
28 else if constexpr (kTrackFreshness) {
29 if constexpr (same_as<void, VALUE>) {
30 return CacheElement{.fKey = key, .fLastRefreshedAt = fLastRefreshedAt};
31 }
32 else {
33 return CacheElement{.fKey = key, .fValue = fResult, .fLastRefreshedAt = fLastRefreshedAt};
34 }
35 }
36 }
37
38 /*
39 ********************************************************************************
40 ************************* TimedCache<KEY,VALUE,TRAITS> *************************
41 ********************************************************************************
42 */
43 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
45 requires (not is_empty_v<decltype (TRAITS::kDefaultMaxAge)>)
46 {
47 if constexpr (TRAITS::kPerCacheMaxAge) {
48 fMaxAge_ = TRAITS::kDefaultMaxAge;
49 }
50 if constexpr (TRAITS::kAutomaticPurgeFrequency != TimeStampDifferenceType{TimedCacheSupport::kNoAutomaticPurgeSentinal}) {
51 fNextAutoClearAt_ = TRAITS::GetCurrentTimestamp () + TRAITS::kAutomaticPurgeFrequency;
52 }
53 }
54 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
55 TimedCache<KEY, VALUE, TRAITS>::TimedCache (TimeStampDifferenceType maxAge)
56 requires (TRAITS::kPerCacheMaxAge)
57 : fMaxAge_{maxAge}
58 , fNextAutoClearAt_{TRAITS::GetCurrentTimestamp () + TRAITS::kAutomaticPurgeFrequency} // hopefully optimized away if kNoAutomaticPurgeSentinal
59 {
60 Require (fMaxAge_ > 0.0s);
61 }
62 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
63 inline TimedCache<KEY, VALUE, TRAITS>::TimedCache (TimedCache&& src) noexcept
64 : fMaxAge_{src.fMaxAge_}
65 , fNextAutoClearAt_{src.fNextAutoClearAt_}
66 {
67 [[maybe_unused]] auto&& srcLock = scoped_lock{src.fMaybeMutex_};
68 fData_ = move (src.fData_);
69 fStats_ = move (src.fStats_);
70 }
71 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
72 inline TimedCache<KEY, VALUE, TRAITS>::TimedCache (const TimedCache& src)
73 : fMaxAge_{src.fMaxAge_}
74 , fNextAutoClearAt_{src.fNextAutoClearAt_}
75 {
76 [[maybe_unused]] auto&& srcLock = shared_lock{src.fMaybeMutex_};
77 fData_ = src.fData_;
78 fStats_ = src.fStats_;
79 }
80 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
81 inline auto TimedCache<KEY, VALUE, TRAITS>::operator= (TimedCache&& rhs) noexcept -> TimedCache&
82 {
83 [[maybe_unused]] auto&& locks = scoped_lock{rhs.fMaybeMutex_, fMaybeMutex_};
84 fMaxAge_ = rhs.fMaxAge_;
85 fNextAutoClearAt_ = rhs.fNextAutoClearAt_;
86 fData_ = move (rhs.fData_);
87 fStats_ = move (rhs.fStats_);
88 return *this;
89 }
90 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
91 inline auto TimedCache<KEY, VALUE, TRAITS>::operator= (const TimedCache& rhs) -> TimedCache&
92 {
93 [[maybe_unused]] auto&& locks = scoped_lock{rhs.fMaybeMutex_, fMaybeMutex_};
94 fMaxAge_ = rhs.fMaxAge_;
95 fNextAutoClearAt_ = rhs.fNextAutoClearAt_;
96 fData_ = rhs.fData_;
97 fStats_ = rhs.fStats_;
98 return *this;
99 }
100 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
101 inline bool TimedCache<KEY, VALUE, TRAITS>::Expired_ (const MyResult_& r, TimeStampType now) const
102 {
103 qStroika_ATTRIBUTE_INDETERMINATE TimeStampType expiresAt;
104 if constexpr (kTrackExpiration) {
105 expiresAt = r.fExpiresAt;
106 }
107 else if constexpr (kTrackFreshness) {
108 expiresAt = r.fLastRefreshedAt + GetMaxAge_ ();
109 }
110 return now > expiresAt;
111 }
112 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
113 constexpr auto TimedCache<KEY, VALUE, TRAITS>::GetMaxAge_ () const -> TimeStampDifferenceType
114 {
115 if constexpr (TRAITS::kPerCacheMaxAge) {
116 return fMaxAge_;
117 }
118 else {
119 return TRAITS::kDefaultMaxAge;
120 }
121 }
122 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
123 inline auto TimedCache<KEY, VALUE, TRAITS>::GetMaxAge () const -> TimeStampDifferenceType
124 {
125 if constexpr (TRAITS::kPerCacheMaxAge) {
126 shared_lock critSec{fMaybeMutex_};
127 return GetMaxAge_ ();
128 }
129 else {
130 return TRAITS::kDefaultMaxAge;
131 }
132 }
133 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
134 void TimedCache<KEY, VALUE, TRAITS>::SetMaxAge (TimeStampDifferenceType maxAge)
135 requires (TRAITS::kPerCacheMaxAge)
136 {
137 Require (maxAge > 0.0s);
138 scoped_lock critSec{fMaybeMutex_};
139 if (fMaxAge_ != maxAge) {
140 fMaxAge_ = maxAge;
141 ClearExpired_ (); // ClearExpired_ not AutomaticallyClearExpiredDataSometimes_ to force auto-update of fNextAutoClearAt_, and cuz moderately likely items interestingly out of date after adjust of min allowed freshness
142 }
143 }
144 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
145 auto TimedCache<KEY, VALUE, TRAITS>::Elements () const -> Traversal::Iterable<CacheElement>
146 {
147 shared_lock critSec{fMaybeMutex_};
148 vector<CacheElement> r;
149 r.reserve (fData_.size ());
150 TimeStampType now = TRAITS::GetCurrentTimestamp ();
151 for (const auto& i : fData_) {
152 if (not Expired_ (i.second, now)) {
153 r.push_back (i.second.MakeCacheElement (i.first));
154 }
155 }
156 return Traversal::Iterable<CacheElement>{move (r)};
157 }
158 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
159 template <typename K>
160 auto TimedCache<KEY, VALUE, TRAITS>::Keys () const -> Traversal::Iterable<K>
161 requires (IKeyedCache<K>)
162 {
163 vector<KEY> r;
164 shared_lock critSec{fMaybeMutex_};
165 r.reserve (fData_.size ());
166 TimeStampType now = TRAITS::GetCurrentTimestamp ();
167 for (const auto& i : fData_) {
168 if (not Expired_ (i.second, now)) {
169 r.push_back (i.first);
170 }
171 }
172 return Traversal::Iterable<KEY>{move (r)};
173 }
174 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
175 template <typename K>
176 requires (IKeyedCache<K>)
177 optional<VALUE> TimedCache<KEY, VALUE, TRAITS>::Lookup (typename Common::ArgByValueType<K> key) const
178 {
179 shared_lock critSec{fMaybeMutex_};
180 typename MyMapType_::const_iterator i = fData_.find (key);
181 if (i == fData_.end ()) {
182 fStats_.IncrementMisses ();
183 return nullopt;
184 }
185 else {
186 if (Expired_ (i->second)) {
187 /*
188 * Cannot update fData_ to indicate item expired const constant overload
189 */
190 fStats_.IncrementMisses ();
191 return nullopt;
192 }
193 fStats_.IncrementHits ();
194 return i->second.fResult;
195 }
196 }
197 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
198 template <typename K>
199 requires (IKeyedCache<K>)
200 optional<VALUE> TimedCache<KEY, VALUE, TRAITS>::Lookup (typename Common::ArgByValueType<K> key)
201 {
202 shared_lock critSec{fMaybeMutex_};
203 typename MyMapType_::iterator i = fData_.find (key);
204 if (i == fData_.end ()) {
205 fStats_.IncrementMisses ();
206 return nullopt;
207 }
208 else {
209 if (Expired_ (i->second)) {
210 /*
211 * since expired, remove from cache
212 */
213 (void)fData_.erase (i);
214 fStats_.IncrementMisses ();
215 return nullopt;
216 }
217 // unclear if we should do this test before or after expired check above???
218 if constexpr (TRAITS::kAutomaticallyMarkDataAsRefreshedEachTimeAccessed) {
219 i->second.fLastRefreshedAt = TRAITS::GetCurrentTimestamp ();
220 }
221 fStats_.IncrementHits ();
222 return i->second.fResult;
223 }
224 }
225 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
226 template <typename K>
227 requires (IKeyedCache<K> and TRAITS::kTrackFreshness)
228 optional<VALUE> TimedCache<KEY, VALUE, TRAITS>::Lookup (typename Common::ArgByValueType<K> key, TimeStampDifferenceType maxAge) const
229 {
230 shared_lock critSec{fMaybeMutex_};
231 typename MyMapType_::const_iterator i = fData_.find (key);
232 TimeStampType now = TRAITS::GetCurrentTimestamp ();
233 if (i == fData_.end ()) {
234 fStats_.IncrementMisses ();
235 return nullopt;
236 }
237 else {
238 if (i->second.fLastRefreshedAt + maxAge <= now) {
239 /*
240 * Cannot update fData_ to indicate item expired const constant overload
241 */
242 fStats_.IncrementMisses ();
243 return nullopt;
244 }
245 fStats_.IncrementHits ();
246 return i->second.fResult;
247 }
248 }
249 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
250 template <typename K>
251 requires (not IKeyedCache<K>)
252 optional<VALUE> TimedCache<KEY, VALUE, TRAITS>::Lookup () const
253 {
254 shared_lock critSec{fMaybeMutex_};
255 if (optional<VALUE> ov = fData_) {
256 if (Expired_ (*ov)) {
257 /*
258 * Cannot update fData_ to indicate item expired const constant overload
259 */
260 fStats_.IncrementMisses ();
261 return nullopt;
262 }
263 fStats_.IncrementHits ();
264 return ov->fResult;
265 }
266 return nullopt;
267 }
268 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
269 template <typename K>
270 requires (not IKeyedCache<K>)
271 optional<VALUE> TimedCache<KEY, VALUE, TRAITS>::Lookup ()
272 {
273 scoped_lock critSec{fMaybeMutex_};
274 if (optional<VALUE> ov = fData_) {
275 if (Expired_ (*ov)) {
276 /*
277 * since expired, remove from cache
278 */
279 fData_ = nullopt;
280 fStats_.IncrementMisses ();
281 return nullopt;
282 }
283 fStats_.IncrementHits ();
284 return ov->fResult;
285 }
286 return nullopt;
287 }
288 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
289 template <typename K>
290 requires (not IKeyedCache<K> and TRAITS::kTrackFreshness)
291 inline optional<VALUE> TimedCache<KEY, VALUE, TRAITS>::Lookup (TimeStampDifferenceType maxAge) const
292 {
293 if (optional<MyResult_> ov = fData_) {
294 TimeStampType now = TRAITS::GetCurrentTimestamp ();
295 if (ov->fLastRefreshedAt + maxAge <= now) {
296 /*
297 * Cannot update fData_ to indicate item expired const constant overload
298 */
299 fStats_.IncrementMisses ();
300 return nullopt;
301 }
302 fStats_.IncrementHits ();
303 return ov->fResult;
304 }
305 return nullopt;
306 }
307 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
308 template <typename K, typename V>
309 requires (IKeyedCache<K> and IValuelessCache<V>)
310 optional<KEY> TimedCache<KEY, VALUE, TRAITS>::Lookup (typename Common::ArgByValueType<K> key) const
311 {
312 static_assert (not IKeyedCache<KEY>); // cuz cannot be both unkeyed and valueless
313 shared_lock critSec{fMaybeMutex_};
314 typename MyMapType_::const_iterator i = fData_.find (key);
315 if (i == fData_.end ()) {
316 fStats_.IncrementMisses ();
317 return nullopt;
318 }
319 else {
320 if (Expired_ (*i)) {
321 /*
322 * Cannot update fData_ to indicate item expired const constant overload
323 */
324 fStats_.IncrementMisses ();
325 return nullopt;
326 }
327 fStats_.IncrementHits ();
328 // return the STORED key object, which could contain more fields/data than the argument KEY
329 return i->first;
330 }
331 }
332 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
333 template <typename K>
334 auto TimedCache<KEY, VALUE, TRAITS>::LookupDetails (typename Common::ArgByValueType<K> key) const -> optional<tuple<VALUE, TimeStampType>>
335 requires (kTrackExpiration and IKeyedCache<K>)
336 {
337 shared_lock critSec{fMaybeMutex_};
338 typename MyMapType_::const_iterator i = fData_.find (key);
339 if (i == fData_.end ()) {
340 fStats_.IncrementMisses ();
341 return nullopt;
342 }
343 else {
344 if (Expired_ (i->second)) {
345 /*
346 * Cannot update fData_ to indicate item expired const constant overload
347 */
348 fStats_.IncrementMisses ();
349 return nullopt;
350 }
351 fStats_.IncrementHits ();
352 return make_tuple (i->second.fResult, i->second.fExpiresAt);
353 }
354 }
355 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
356 template <typename K>
357 auto TimedCache<KEY, VALUE, TRAITS>::LookupDetails (typename Common::ArgByValueType<K> key) -> optional<tuple<VALUE, TimeStampType>>
358 requires (kTrackExpiration and IKeyedCache<K>)
359 {
360 shared_lock critSec{fMaybeMutex_};
361 typename MyMapType_::iterator i = fData_.find (key);
362 if (i == fData_.end ()) {
363 fStats_.IncrementMisses ();
364 return nullopt;
365 }
366 else {
367 if (Expired_ (i->second)) {
368 /*
369 * since expired, remove from cache
370 */
371 (void)fData_.erase (i);
372 fStats_.IncrementMisses ();
373 return nullopt;
374 }
375 fStats_.IncrementHits ();
376 return make_tuple (i->second.fResult, i->second.fExpiresAt);
377 }
378 }
379 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
380 template <typename K>
381 auto TimedCache<KEY, VALUE, TRAITS>::LookupDetails (typename Common::ArgByValueType<K> key) const -> optional<tuple<VALUE, TimeStampType>>
382 requires (kTrackFreshness and IKeyedCache<K>)
383 {
384 shared_lock critSec{fMaybeMutex_};
385 typename MyMapType_::const_iterator i = fData_.find (key);
386 if (i == fData_.end ()) {
387 fStats_.IncrementMisses ();
388 return nullopt;
389 }
390 else {
391 if (Expired_ (i->second)) {
392 /*
393 * Cannot update fData_ to indicate item expired const constant overload
394 */
395 fStats_.IncrementMisses ();
396 return nullopt;
397 }
398 fStats_.IncrementHits ();
399 return make_tuple (i->second.fResult, i->second.fLastRefreshedAt);
400 }
401 }
402 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
403 template <typename K>
404 inline auto TimedCache<KEY, VALUE, TRAITS>::LookupDetails (typename Common::ArgByValueType<K> key) -> optional<tuple<VALUE, TimeStampType>>
405 requires (kTrackFreshness and IKeyedCache<K>)
406 {
407 shared_lock critSec{fMaybeMutex_};
408 typename MyMapType_::iterator i = fData_.find (key);
409 if (i == fData_.end ()) {
410 fStats_.IncrementMisses ();
411 return nullopt;
412 }
413 else {
414 if (Expired_ (i->second)) {
415 /*
416 * since expired, remove from cache
417 */
418 (void)fData_.erase (i);
419 fStats_.IncrementMisses ();
420 return nullopt;
421 }
422 // unclear if we should do this test before or after expired check above???
423 if constexpr (TRAITS::kAutomaticallyMarkDataAsRefreshedEachTimeAccessed) {
424 i->second.fLastRefreshedAt = TRAITS::GetCurrentTimestamp ();
425 }
426 fStats_.IncrementHits ();
427 return make_tuple (i->second.fResult, i->second.fLastRefreshedAt);
428 }
429 }
430 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
431 template <typename K>
432 auto TimedCache<KEY, VALUE, TRAITS>::LookupDetails (typename Common::ArgByValueType<K> key, TimeStampDifferenceType maxAge) const
433 -> optional<tuple<VALUE, TimeStampType>>
434 requires (kTrackFreshness and IKeyedCache<K>)
435 {
436 shared_lock critSec{fMaybeMutex_};
437 typename MyMapType_::const_iterator i = fData_.find (key);
438 TimeStampType now = TRAITS::GetCurrentTimestamp ();
439 if (i == fData_.end ()) {
440 fStats_.IncrementMisses ();
441 return nullopt;
442 }
443 else {
444 if (i->second.fLastRefreshedAt + maxAge <= now) {
445 /*
446 * Cannot update fData_ to indicate item expired const constant overload
447 */
448 fStats_.IncrementMisses ();
449 return nullopt;
450 }
451 fStats_.IncrementHits ();
452 return make_tuple (i->second.fResult, i->second.fLastRefreshedAt);
453 }
454 }
455 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
456 template <typename K>
457 auto TimedCache<KEY, VALUE, TRAITS>::LookupDetails (TimeStampDifferenceType maxAge) const -> optional<tuple<VALUE, TimeStampType>>
458 requires (not IKeyedCache<K>)
459 {
460 AssertNotImplemented (); // but easy
461 return nullopt;
462 }
463 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
464 template <typename K, typename V>
465 requires (IKeyedCache<K> and IValuelessCache<V>)
466 auto TimedCache<KEY, VALUE, TRAITS>::LookupDetails (typename Common::ArgByValueType<K> key) const -> optional<tuple<KEY, TimeStampType>>
467 {
468 static_assert (not IKeyedCache<KEY>); // cuz cannot be both unkeyed and valueless
469 shared_lock critSec{fMaybeMutex_};
470 typename MyMapType_::const_iterator i = fData_.find (key);
471 if (i == fData_.end ()) {
472 fStats_.IncrementMisses ();
473 return nullopt;
474 }
475 else {
476 if (Expired_ (*i)) {
477 /*
478 * Cannot update fData_ to indicate item expired const constant overload
479 */
480 fStats_.IncrementMisses ();
481 return nullopt;
482 }
483 fStats_.IncrementHits ();
484 // return the STORED key object, which could contain more fields/data than the argument KEY
485 return i->first;
486 }
487 }
488 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
489 template <typename K>
490 auto TimedCache<KEY, VALUE, TRAITS>::GetExpiration () const -> optional<TimeStampType>
491 requires (not IKeyedCache<K>)
492 {
493 if (optional<MyResult_> ov = fData_) {
494 if (Expired_ (*ov)) {
495 /*
496 * Cannot update fData_ to indicate item expired const constant overload
497 */
498 fStats_.IncrementMisses ();
499 return nullopt;
500 }
501 fStats_.IncrementHits ();
502 return ov->fResult;
503 }
504 return nullopt;
505 }
506 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
507 template <typename K>
508 auto TimedCache<KEY, VALUE, TRAITS>::GetExpiration (typename Common::ArgByValueType<K> key) const -> optional<TimeStampType>
509 requires (IKeyedCache<K>)
510 {
511 typename MyMapType_::const_iterator i = fData_.find (key);
512 if (i == fData_.end ()) {
513 return nullopt;
514 }
515 if (Expired_ (i->second)) {
516 return nullopt;
517 }
518 if constexpr (kTrackExpiration) {
519 return i->second.fExpiresAt;
520 }
521 else if constexpr (kTrackFreshness) {
522 return i->second.fLastRefreshedAt + GetMaxAge_ ();
523 }
524 }
525 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
526 template <typename K, Common::invocable_r<VALUE, KEY> CACHE_FILLTER_T>
527 requires (IKeyedCache<K>)
528 VALUE TimedCache<KEY, VALUE, TRAITS>::LookupValue (typename Common::ArgByValueType<K> key, CACHE_FILLTER_T&& cacheFiller)
529 {
530 if (optional<VALUE> o = Lookup (key)) {
531 return *o;
532 }
533 else {
534 return LockingLookupValueAdder_ (key, forward<CACHE_FILLTER_T> (cacheFiller));
535 }
536 }
537 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
538 template <typename K, Common::invocable_r<VALUE, KEY> CACHE_FILLTER_T>
539 requires (IKeyedCache<K> and TRAITS::kTrackFreshness)
540 VALUE TimedCache<KEY, VALUE, TRAITS>::LookupValue (typename Common::ArgByValueType<K> key, TimeStampDifferenceType maxAge, CACHE_FILLTER_T&& cacheFiller)
541 {
542 if (optional<VALUE> o = Lookup (key, maxAge)) {
543 return *o;
544 }
545 else {
546 return LockingLookupValueAdder_ (key, forward<CACHE_FILLTER_T> (cacheFiller));
547 }
548 }
549 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
550 template <typename K, Common::invocable_r<VALUE> CACHE_FILLTER_T>
551 requires (not IKeyedCache<K>)
552 VALUE TimedCache<KEY, VALUE, TRAITS>::LookupValue (CACHE_FILLTER_T&& cacheFiller)
553 {
554 if (optional<VALUE> ov = Lookup ()) {
555 return *ov;
556 }
557 else {
558 return LockingLookupValueAdder_ (forward<CACHE_FILLTER_T> (cacheFiller));
559 }
560 }
561 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
562 template <typename K, Common::invocable_r<VALUE> CACHE_FILLTER_T>
563 requires (not IKeyedCache<K> and TRAITS::kTrackFreshness)
564 VALUE TimedCache<KEY, VALUE, TRAITS>::LookupValue (TimeStampDifferenceType maxAge, CACHE_FILLTER_T&& cacheFiller)
565 {
566 if (optional<VALUE> ov = Lookup (maxAge)) {
567 return *ov;
568 }
569 else {
570 return LockingLookupValueAdder_ (forward<CACHE_FILLTER_T> (cacheFiller));
571 }
572 }
573 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
574 template <typename K, typename V, Common::invocable_r<K, K> CACHE_FILLTER_T>
575 requires (IKeyedCache<K> and IValuelessCache<V>)
576 KEY TimedCache<KEY, VALUE, TRAITS>::LookupValue (typename Common::ArgByValueType<K> key, CACHE_FILLTER_T&& cacheFiller)
577 {
578 if (optional<KEY> ok = Lookup (key)) {
579 return *ok;
580 }
581 else {
582 KEY kr = forward<CACHE_FILLTER_T> (cacheFiller) (key);
583 Add (kr);
584 return kr;
585 }
586 }
587 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
588 template <typename K, Common::invocable_r<VALUE> CACHE_FILLTER_T>
589 requires (not IKeyedCache<K>)
590 inline VALUE TimedCache<KEY, VALUE, TRAITS>::LockingLookupValueAdder_ (CACHE_FILLTER_T&& cacheFiller)
591 {
592 VALUE r = forward<CACHE_FILLTER_T> (cacheFiller) ();
593 Add (r);
594 return r;
595 }
596 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
597 template <typename K, Common::invocable_r<VALUE, KEY> CACHE_FILLTER_T>
598 requires (IKeyedCache<K>)
599 inline VALUE TimedCache<KEY, VALUE, TRAITS>::LockingLookupValueAdder_ (typename Common::ArgByValueType<K> key, CACHE_FILLTER_T&& cacheFiller)
600 {
601 if constexpr (TRAITS::kHoldWriteLockDuringCacheFill) {
602 scoped_lock critSec{fMaybeMutex_};
603 VALUE v = forward<CACHE_FILLTER_T> (cacheFiller) (key);
604 AutomaticallyClearExpiredDataSometimes_ ();
605 Add_ (key, v);
606 return v;
607 }
608 else {
609 VALUE v = forward<CACHE_FILLTER_T> (cacheFiller) (key);
610 Add (key, v); // public API so will acquire lock
611 return v;
612 }
613 }
614 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
615 template <typename K>
616 requires (IKeyedCache<K> and IValuelessCache<VALUE>)
618 {
619 scoped_lock critSec{fMaybeMutex_};
620 // nb skip AutomaticallyClearExpiredDataSometimes_ cuz Valueless has no map, and add will just overwrite
621 TimeStampType now = TRAITS::GetCurrentTimestamp ();
622 if constexpr (kTrackExpiration) {
623 Add_ (key, now + GetMaxAge_ ());
624 }
625 else if constexpr (kTrackFreshness) {
626 Add_ (key, now);
627 }
628 }
629 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
630 template <typename K, typename V>
631 requires (IKeyedCache<K> and not IValuelessCache<V>)
632 void TimedCache<KEY, VALUE, TRAITS>::Add (typename Common::ArgByValueType<K> key, typename Common::ArgByValueType<V> result)
633 {
634 scoped_lock critSec{fMaybeMutex_};
635 AutomaticallyClearExpiredDataSometimes_ ();
636 TimeStampType now = TRAITS::GetCurrentTimestamp ();
637 if constexpr (kTrackExpiration) {
638 Add_ (key, result, now + GetMaxAge_ ());
639 }
640 else if constexpr (kTrackFreshness) {
641 Add_ (key, result, now);
642 }
643 }
644 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
645 template <typename K, typename V>
646 requires (IKeyedCache<K> and not IValuelessCache<V> and TRAITS::kTrackFreshness)
647 void TimedCache<KEY, VALUE, TRAITS>::Add (typename Common::ArgByValueType<K> key, typename Common::ArgByValueType<V> result, TimeStampType freshAsOf)
648 {
649 scoped_lock critSec{fMaybeMutex_};
650 AutomaticallyClearExpiredDataSometimes_ ();
651 Add_ (key, result, freshAsOf);
652 }
653 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
654 template <typename K, typename V>
655 requires (IKeyedCache<K> and not IValuelessCache<V> and TRAITS::kTrackExpiration)
656 void TimedCache<KEY, VALUE, TRAITS>::Add (typename Common::ArgByValueType<K> key, typename Common::ArgByValueType<V> result, TimeStampType expiresAt)
657 {
658 scoped_lock critSec{fMaybeMutex_};
659 AutomaticallyClearExpiredDataSometimes_ ();
660 Add_ (key, result, expiresAt);
661 }
662 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
663 template <typename K, typename V>
664 requires (not IKeyedCache<K> and not IValuelessCache<V>)
667 TimeStampType now = TRAITS::GetCurrentTimestamp ();
668 if constexpr (kTrackExpiration) {
669 Add (result, now + GetMaxAge_ ());
670 }
671 else if constexpr (kTrackFreshness) {
672 Add (result, now);
673 }
674 }
675 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
676 template <typename K, typename V>
677 requires (not IKeyedCache<K> and not IValuelessCache<V> and TRAITS::kTrackFreshness)
678 inline void TimedCache<KEY, VALUE, TRAITS>::Add (typename Common::ArgByValueType<V> result, TimeStampType freshAsOf)
679 {
680 scoped_lock critSec{fMaybeMutex_};
681 // no AutomaticallyClearExpiredDataSometimes_ for not keyed cache
682 Add_ (result, freshAsOf);
683 }
684 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
685 template <typename K, typename V>
686 requires (not IKeyedCache<K> and not IValuelessCache<V> and TRAITS::kTrackExpiration)
687 inline void TimedCache<KEY, VALUE, TRAITS>::Add (typename Common::ArgByValueType<V> result, TimeStampType expiresAt)
688 {
689 scoped_lock critSec{fMaybeMutex_};
690 AutomaticallyClearExpiredDataSometimes_ ();
691 Add_ (result, expiresAt);
692 }
693 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
694 template <typename K>
695 requires (IKeyedCache<K> and IValuelessCache<VALUE> and TRAITS::kTrackExpiration)
696 inline void TimedCache<KEY, VALUE, TRAITS>::Add_ (typename Common::ArgByValueType<K> key, TimeStampType expiresAt)
697 {
698 fData_.insert_or_assign (key, MyResult_{.fExpiresAt = expiresAt});
699 }
700 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
701 template <typename K>
702 requires (IKeyedCache<K> and IValuelessCache<VALUE> and TRAITS::kTrackFreshness)
703 inline void TimedCache<KEY, VALUE, TRAITS>::Add_ (typename Common::ArgByValueType<K> key, TimeStampType freshAsOf)
704 {
705 fData_.insert_or_assign (key, MyResult_{.fLastRefreshedAt = freshAsOf});
706 }
707 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
708 template <typename K, typename V>
709 requires (IKeyedCache<K> and not IValuelessCache<V> and TRAITS::kTrackFreshness)
710 inline void TimedCache<KEY, VALUE, TRAITS>::Add_ (typename Common::ArgByValueType<K> key, typename Common::ArgByValueType<V> result, TimeStampType freshAsOf)
711 {
712 fData_.insert_or_assign (key, MyResult_{.fResult = result, .fLastRefreshedAt = freshAsOf});
713 }
714 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
715 template <typename K, typename V>
716 requires (IKeyedCache<K> and not IValuelessCache<V> and TRAITS::kTrackExpiration)
717 inline void TimedCache<KEY, VALUE, TRAITS>::Add_ (typename Common::ArgByValueType<K> key, typename Common::ArgByValueType<V> result, TimeStampType expiresAt)
718 {
719 fData_.insert_or_assign (key, MyResult_{.fResult = result, .fExpiresAt = expiresAt});
720 }
721 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
722 template <typename K, typename V>
723 requires (not IKeyedCache<K> and not IValuelessCache<V> and TRAITS::kTrackFreshness)
724 inline void TimedCache<KEY, VALUE, TRAITS>::Add_ (typename Common::ArgByValueType<V> result, TimeStampType freshAsOf)
725 {
726 fData_ = MyResult_{.fResult = result, .fLastRefreshedAt = freshAsOf};
727 }
728 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
729 template <typename K, typename V>
730 requires (not IKeyedCache<K> and not IValuelessCache<V> and TRAITS::kTrackExpiration)
731 inline void TimedCache<KEY, VALUE, TRAITS>::Add_ (typename Common::ArgByValueType<V> result, TimeStampType expiresAt)
732 {
733 fData_ = MyResult_{.fResult = result, .fExpiresAt = expiresAt};
734 }
735 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
736 template <typename K>
737 requires (IKeyedCache<K>)
738 inline void TimedCache<KEY, VALUE, TRAITS>::Remove (typename Common::ArgByValueType<K> key)
739 {
740 scoped_lock critSec{fMaybeMutex_};
741 fData_.erase (key);
742 AutomaticallyClearExpiredDataSometimes_ ();
743 }
744 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
745 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (predicate<typename TimedCache<KEY, VALUE, TRAITS>::CacheElement>) PREDICATE>
746 void TimedCache<KEY, VALUE, TRAITS>::RemoveAll (PREDICATE&& p)
747 {
748 scoped_lock critSec{fMaybeMutex_};
749 for (auto i = fData_.begin (); i != fData_.end ();) {
750 if (p (i->second.MakeCacheElement (i->first))) {
751 i = fData_.erase (i);
752 }
753 else {
754 ++i;
755 }
756 }
757 }
758 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
759 template <Traversal::IIterableOfTo<KEY> ITERABLE_OF_KEY_TYPE>
760 void TimedCache<KEY, VALUE, TRAITS>::RetainAll (const ITERABLE_OF_KEY_TYPE& items)
761 {
762 scoped_lock critSec{fMaybeMutex_};
763 // quickie inefficient implementation
764 Containers::Mapping<KEY, MyResult_> tmp{this->fData_};
765 tmp.RetainAll (items);
766 fData_ = tmp.template As<MyMapType_> ();
767 }
768 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
770 {
771 scoped_lock critSec{fMaybeMutex_};
772 fData_.clear ();
773 }
774 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
776 {
777 scoped_lock critSec{fMaybeMutex_};
778 ClearExpired_ ();
779 }
780 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
781 inline void TimedCache<KEY, VALUE, TRAITS>::ClearExpiredData (TimeStampDifferenceType maxAge)
782 requires (TRAITS::kTrackFreshness)
783 {
784 scoped_lock critSec{fMaybeMutex_};
785 ClearExpired_ (maxAge);
786 }
787 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
788 inline typename TRAITS::StatsType TimedCache<KEY, VALUE, TRAITS>::GetStats () const
789 {
790 return fStats_;
791 }
792 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
793 inline void TimedCache<KEY, VALUE, TRAITS>::AutomaticallyClearExpiredDataSometimes_ ()
794 requires (IKeyedCache<KEY>)
795 {
796 if constexpr (TRAITS::kAutomaticPurgeFrequency != TimeStampDifferenceType{TimedCacheSupport::kNoAutomaticPurgeSentinal}) {
797 if constexpr (TRAITS::kAutomaticPurgeFrequency != TimeStampDifferenceType{TimedCacheSupport::kNoAutomaticPurgeSentinal}) {
798 if (fNextAutoClearAt_ < TRAITS::GetCurrentTimestamp ()) [[unlikely]] {
799 ClearExpired_ ();
800 WeakAssert (fNextAutoClearAt_ > TRAITS::GetCurrentTimestamp ()); // note internally resets fNextAutoClearAt_
801 }
802 }
803 }
804 }
805 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
806 void TimedCache<KEY, VALUE, TRAITS>::ClearExpired_ ()
807 requires (IKeyedCache<KEY>)
808 {
809 TimeStampType now = TRAITS::GetCurrentTimestamp ();
810 for (typename MyMapType_::iterator i = fData_.begin (); i != fData_.end ();) {
811 if (Expired_ (i->second, now)) {
812 i = fData_.erase (i);
813 }
814 else {
815 ++i;
816 }
817 }
818 if constexpr (TRAITS::kAutomaticPurgeFrequency != TimeStampDifferenceType{TimedCacheSupport::kNoAutomaticPurgeSentinal}) {
819 fNextAutoClearAt_ = now + TRAITS::kAutomaticPurgeFrequency;
820 }
821 }
822 template <IKey KEY, IValue VALUE, TimedCacheSupport::ITraits<KEY, VALUE> TRAITS>
823 void TimedCache<KEY, VALUE, TRAITS>::ClearExpired_ (TimeStampDifferenceType maxAge)
824 requires (IKeyedCache<KEY> and TRAITS::kTrackFreshness)
825 {
826 TimeStampType now = TRAITS::GetCurrentTimestamp ();
827 for (typename MyMapType_::iterator i = fData_.begin (); i != fData_.end ();) {
828 if (i->second.fLastRefreshedAt + maxAge <= now) {
829 i = fData_.erase (i);
830 }
831 else {
832 ++i;
833 }
834 }
835 if constexpr (TRAITS::kAutomaticPurgeFrequency != TimeStampDifferenceType{TimedCacheSupport::kNoAutomaticPurgeSentinal}) {
836 fNextAutoClearAt_ = now + TRAITS::kAutomaticPurgeFrequency;
837 }
838 }
839
840 ///////////////////
841 // DEPRECATED APIS
842 ///////////////////
843 template <typename KEY, typename VALUE, typename TIME_TRAITS = TimedCacheSupport::DefaultTraits<KEY, VALUE>>
844 class [[deprecated ("Since Stroika v3.0d23 - TimedCache has the same functionality and mostly the same names")]] CallerStalenessCache
845 : public TimedCache<KEY, VALUE, TimedCacheSupport::DefaultTraits<KEY, VALUE>> {
846 private:
847 using inherited = TimedCache<KEY, VALUE, TimedCacheSupport::DefaultTraits<KEY, VALUE>>;
848
849 public:
850 using TraitsType = typename inherited::TraitsType;
851 using TimeStampType = typename inherited::TimeStampType;
852 using TimeStampDifferenceType = typename inherited::TimeStampDifferenceType;
853
854 public:
855 [[deprecated ("Since Stroika v3.0d23, usually just get rid of call and argument can be used directly in situ")]]
856 static TimeStampType Ago (TimeStampDifferenceType backThisTime)
857 {
858 Require (backThisTime >= 0s);
859 return TraitsType::GetCurrentTimestamp () - backThisTime;
860 }
861 [[deprecated ("Since Stroika v3.0d23, use TraitsType::GetCurrentStamp")]]
862 static TimeStampType GetCurrentTimestamp ()
863 {
864 return TraitsType::GetCurrentTimestamp ();
865 }
866 };
867 template <typename KEY, typename VALUE, typename TIME_TRAITS = TimedCacheSupport::DefaultTraits<KEY, VALUE>>
868 class [[deprecated ("Since Stroika v3.0d23 - TimedCache (or SyncrhonizedTimedCache) has the same functionality and mostly the same "
869 "names")]] SynchronizedCallerStalenessCache
870 : public TimedCache<KEY, VALUE, TimedCacheSupport::InternallySynchronizedTraits<TimedCacheSupport::DefaultTraits<KEY, VALUE>>> {
871 private:
872 using inherited = TimedCache<KEY, VALUE, TimedCacheSupport::InternallySynchronizedTraits<TimedCacheSupport::DefaultTraits<KEY, VALUE>>>;
873
874 public:
875 using TraitsType = typename inherited::TraitsType;
876 using TimeStampType = typename inherited::TimeStampType;
877 using TimeStampDifferenceType = typename inherited::TimeStampDifferenceType;
878
879 public:
880 [[deprecated ("Since Stroika v3.0d23, usually just get rid of call and argument can be used directly in situ")]]
881 static TimeStampType Ago (TimeStampDifferenceType backThisTime)
882 {
883 Require (backThisTime >= 0s);
884 return TraitsType::GetCurrentTimestamp () - backThisTime;
885 }
886 [[deprecated ("Since Stroika v3.0d23, use TraitsType::GetCurrentStamp")]]
887 static TimeStampType GetCurrentTimestamp ()
888 {
889 return TraitsType::GetCurrentTimestamp ();
891 };
892
893}
#define AssertNotImplemented()
Definition Assertions.h:402
#define WeakAssert(c)
A WeakAssert() is for things that aren't guaranteed to be true, but are overwhelmingly likely to be t...
Definition Assertions.h:439
#define qStroika_ATTRIBUTE_INDETERMINATE
qStroika_ATTRIBUTE_INDETERMINATE is used where you would use a C++ attribute for a variable that is i...
Definition StdCompat.h:395
Keep track of a bunch of objects, each with an associated time used to allow data to 'expire'.
Definition TimedCache.h:572
nonvirtual VALUE LookupValue(typename Common::ArgByValueType< K > key, CACHE_FILLTER_T &&cacheFiller)
Lookup value, and if missing, fetch it with argument cacheFiller (and add/return its value).
nonvirtual optional< tuple< VALUE, TimeStampType > > LookupDetails(typename Common::ArgByValueType< K > key) const
Returns the value associated with argument 'key' (if IKeyedCache), or nullopt, if its missing (missin...
nonvirtual TimeStampDifferenceType GetMaxAge() const
nonvirtual Traversal::Iterable< K > Keys() const
nonvirtual void SetMaxAge(TimeStampDifferenceType maxAge)
nonvirtual Traversal::Iterable< CacheElement > Elements() const
nonvirtual optional< TimeStampType > GetExpiration() const
Get the Expiration of object or nullopt of item expired/not in cache.
nonvirtual void RetainAll(const ITERABLE_OF_KEY_TYPE &items2Keep)
like Mapping<>::RetainAll () - throw away all elements not in items2Keep
nonvirtual void Add(typename Common::ArgByValueType< K > key)
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
constexpr float kNoAutomaticPurgeSentinal
see TimedCache<>::TraitsType::kAutomaticPurgeFrequency - disable automatic purging
Definition TimedCache.h:62
static constexpr bool IKeyedCache
does this cache have a KEY type (overwhelming YES, but sometimes handy to have 'singleton' cache,...
conditional_t<(sizeof(CHECK_T)<=2 *sizeof(void *)) and is_trivially_copyable_v< CHECK_T >, CHECK_T, const CHECK_T & > ArgByValueType
This is an alias for 'T' - but how we want to pass it on stack as formal parameter.
Definition TypeHints.h:36
everything here is optional ;-) But typically, its fKey, fValue, fLastRefreshedAt
Definition TimedCache.h:653