Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
TimedCache.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
6#include "Stroika/Foundation/Execution/Common.h"
7
9
10 /*
11 ********************************************************************************
12 ************************* TimedCache<KEY,VALUE,TRAITS> *************************
13 ********************************************************************************
14 */
15 template <typename KEY, typename VALUE, typename TRAITS>
16 TimedCache<KEY, VALUE, TRAITS>::TimedCache (const Time::Duration& minimumAllowedFreshness)
17 : fMinimumAllowedFreshness_{minimumAllowedFreshness}
18 , fNextAutoClearAt_{Time::GetTickCount () + minimumAllowedFreshness}
19 {
20 Require (fMinimumAllowedFreshness_ > 0.0s);
21 }
22 template <typename KEY, typename VALUE, typename TRAITS>
24 {
25 shared_lock critSec{fAssertExternallySyncrhonized_};
26 return Time::Duration{fMinimumAllowedFreshness_};
27 }
28 template <typename KEY, typename VALUE, typename TRAITS>
30 {
31 Require (minimumAllowedFreshness > 0.0s);
32 lock_guard critSec{fAssertExternallySyncrhonized_};
33 if (fMinimumAllowedFreshness_ != minimumAllowedFreshness) {
34 fMinimumAllowedFreshness_ = minimumAllowedFreshness;
35 ClearOld_ (); // ClearOld_ not ClearIfNeeded_ to force auto-update of fNextAutoClearAt_, and cuz moderately likely items interestingly out of date after adjust of min allowed freshness
36 }
37 }
38 template <typename KEY, typename VALUE, typename TRAITS>
39 auto TimedCache<KEY, VALUE, TRAITS>::Elements () const -> Traversal::Iterable<CacheElement>
40 {
42 r.reserve (fMap_.size ());
43 Time::TimePointSeconds lastAccessThreshold = Time::GetTickCount () - fMinimumAllowedFreshness_;
44 for (const auto& i : fMap_) {
45 if (i.second.fLastRefreshedAt >= lastAccessThreshold) {
46 r.push_back (CacheElement{i.first, i.second.fResult, i.second.fLastRefreshedAt});
47 }
48 }
50 }
51 template <typename KEY, typename VALUE, typename TRAITS>
53 {
54 shared_lock critSec{fAssertExternallySyncrhonized_};
55 typename MyMapType_::const_iterator i = fMap_.find (key);
56 Time::TimePointSeconds now = Time::GetTickCount ();
57 if (i == fMap_.end ()) {
58 fStats_.IncrementMisses ();
59 return nullopt;
60 }
61 else {
62 Time::TimePointSeconds lastAccessThreshold = now - fMinimumAllowedFreshness_;
63 if (i->second.fLastRefreshedAt < lastAccessThreshold) {
64 /**
65 * Before Stroika 3.0d1, we used to remove the entry from the list (an optimization). But
66 * that required Lookup to be non-const (with synchronization in mind probably a pessimization).
67 * So instead, count on PurgeUnusedData being called automatically on future adds,
68 * explicit user calls to purge unused data.
69 *
70 * i = fMap_.erase (i);
71 */
72 fStats_.IncrementMisses ();
73 return nullopt;
74 }
75 fStats_.IncrementHits ();
76 if (lastRefreshedAt != nullptr) {
77 *lastRefreshedAt = i->second.fLastRefreshedAt;
78 }
79 return i->second.fResult;
80 }
81 }
82 template <typename KEY, typename VALUE, typename TRAITS>
84 {
85 lock_guard critSec{fAssertExternallySyncrhonized_};
86 typename MyMapType_::iterator i = fMap_.find (key);
87 Time::TimePointSeconds now = Time::GetTickCount ();
88 if (i == fMap_.end ()) {
89 fStats_.IncrementMisses ();
90 return nullopt;
91 }
92 else {
93 Time::TimePointSeconds lastAccessThreshold = now - fMinimumAllowedFreshness_;
94 if (i->second.fLastRefreshedAt < lastAccessThreshold) {
95 /**
96 * Before Stroika 3.0d1, we used to remove the entry from the list (an optimization). But
97 * that required Lookup to be non-const (with synchronization in mind probably a pessimization).
98 * So instead, count on PurgeUnusedData being called automatically on future adds,
99 * explicit user calls to purge unused data.
100 *
101 * i = fMap_.erase (i);
102 */
103 fStats_.IncrementMisses ();
104 return nullopt;
105 }
106 if (successfulLookupRefreshesAcceesFlag == LookupMarksDataAsRefreshed::eTreatFoundThroughLookupAsRefreshed) {
107 i->second.fLastRefreshedAt = Time::GetTickCount ();
108 }
109 fStats_.IncrementHits ();
110 return i->second.fResult;
111 }
112 }
113 template <typename KEY, typename VALUE, typename TRAITS>
115 const function<VALUE (typename Common::ArgByValueType<KEY>)>& cacheFiller,
116 LookupMarksDataAsRefreshed successfulLookupRefreshesAcceesFlag,
117 PurgeSpoiledDataFlagType purgeSpoiledData)
118 {
120 return *o;
121 }
122 else {
123 VALUE v = cacheFiller (key);
124 Add (key, v, purgeSpoiledData);
125 return v;
126 }
127 }
128 template <typename KEY, typename VALUE, typename TRAITS>
130 PurgeSpoiledDataFlagType prgeSpoiledData)
131 {
132 lock_guard critSec{fAssertExternallySyncrhonized_};
133 if (prgeSpoiledData == PurgeSpoiledDataFlagType::eAutomaticallyPurgeSpoiledData) {
134 ClearIfNeeded_ ();
135 }
136 typename MyMapType_::iterator i = fMap_.find (key);
137 if (i == fMap_.end ()) {
138 fMap_.insert ({key, MyResult_{result}});
139 }
140 else {
141 i->second = MyResult_{result}; // overwrite if its already there
142 }
143 }
144 template <typename KEY, typename VALUE, typename TRAITS>
147 {
148 lock_guard critSec{fAssertExternallySyncrhonized_};
149 fMap_.insert ({key, MyResult_{result, freshAsOf}});
150 }
151 template <typename KEY, typename VALUE, typename TRAITS>
152 inline void TimedCache<KEY, VALUE, TRAITS>::Remove (typename Common::ArgByValueType<KEY> key)
153 {
154 lock_guard critSec{fAssertExternallySyncrhonized_};
155 fMap_.erase (key);
156 }
157 template <typename KEY, typename VALUE, typename TRAITS>
159 {
160 lock_guard critSec{fAssertExternallySyncrhonized_};
161 fMap_.clear ();
162 }
163 template <typename KEY, typename VALUE, typename TRAITS>
165 {
166 lock_guard critSec{fAssertExternallySyncrhonized_};
167 ClearOld_ ();
168 }
169 template <typename KEY, typename VALUE, typename TRAITS>
171 {
172 if (fNextAutoClearAt_ < Time::GetTickCount ()) {
173 ClearOld_ ();
174 }
175 }
176 template <typename KEY, typename VALUE, typename TRAITS>
177 void TimedCache<KEY, VALUE, TRAITS>::ClearOld_ ()
178 {
179 Time::TimePointSeconds now = Time::GetTickCount ();
180 fNextAutoClearAt_ = now + fMinimumAllowedFreshness_ * 0.75; // somewhat arbitrary how far into the future we do this...
181 Time::TimePointSeconds lastAccessThreshold = now - fMinimumAllowedFreshness_;
182 for (typename MyMapType_::iterator i = fMap_.begin (); i != fMap_.end ();) {
183 if (i->second.fLastRefreshedAt < lastAccessThreshold) {
184 i = fMap_.erase (i);
185 }
186 else {
187 ++i;
188 }
189 }
190 }
191
192}
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
Definition Realtime.h:82
TimePointSeconds GetTickCount() noexcept
get the current (monotonically increasing) time - from RealtimeClock
Definition Realtime.inl:16
LRUCache implements a simple least-recently-used caching strategy, with optional hashing (of keys) to...
Definition LRUCache.h:94
Keep track of a bunch of objects, each with an associated 'freshness' which meet a TimedCache-associa...
Definition TimedCache.h:257
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
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:32