ATLAS Offline Software
Loading...
Searching...
No Matches
CachedValue.icc
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4/**
5 * @file CxxUtils/CachedValue.icc
6 * @author scott snyder <snyder@bnl.gov>
7 * @date Sep, 2017
8 * @brief Cached value with atomic update.
9 */
10
11
12namespace CxxUtils {
13
14
15/**
16 * @brief Default constructor. Sets the value to invalid.
17 */
18template <class T>
19inline
20CachedValue<T>::CachedValue()
21 : m_val (),
22 m_cacheValid (INVALID)
23{
24}
25
26
27/**
28 * @brief Constructor from a value.
29 */
30template <class T>
31inline
32CachedValue<T>::CachedValue (const T& val)
33 : m_val (val),
34 m_cacheValid (VALID)
35{
36}
37
38
39/**
40 * @brief Move constructor from a value.
41 */
42template <class T>
43inline
44CachedValue<T>::CachedValue (T&& val) noexcept
45 : m_val (std::move (val)),
46 m_cacheValid (VALID)
47{
48}
49
50
51/**
52 * @brief Copy constructor.
53 */
54template <class T>
55inline
56CachedValue<T>::CachedValue (const CachedValue& other)
57 : m_val(),
58 m_cacheValid (INVALID)
59{
60 // If the source is in the middle of an update, set this value
61 // to invalid. Don't copy the value itself unless the source is valid.
62 if (other.m_cacheValid == VALID) {
63 m_cacheValid = VALID;
64 m_val = other.m_val;
65 }
66}
67
68
69/**
70 * @brief Move constructor.
71 * No concurrent operations on OTHER are possible.
72 */
73template <class T>
74inline
75CachedValue<T>::CachedValue (CachedValue&& other) noexcept
76 : m_val(),
77 m_cacheValid (INVALID)
78{
79 if (other.m_cacheValid == VALID) {
80 // If we get here, m_val should be properly initialized.
81 // But sometimes gcc12 has trouble proving that.
82 // Suppress a maybe-uninitialized warning here.
83#if __GNUC__ >= 12
84#pragma GCC diagnostic push
85#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
86#endif
87 m_val = std::move (other.m_val);
88#if __GNUC__ >= 12
89#pragma GCC diagnostic pop
90#endif
91 other.m_cacheValid = INVALID;
92 m_cacheValid = VALID;
93 }
94}
95
96
97/**
98 * @brief Copy constructor from other type.
99 */
100template <class T>
101template <class U>
102inline
103CachedValue<T>::CachedValue (const CachedValue<U>& other)
104 : m_cacheValid (INVALID)
105{
106 // If the source is in the middle of an update, set this value
107 // to invalid. Don't copy the value itself unless the source is valid.
108 if (other.m_cacheValid == VALID) {
109 m_cacheValid = VALID;
110 m_val = other.m_val;
111 }
112 else {
113 m_val = T();
114 }
115}
116
117
118/**
119 * @brief Assignment.
120 */
121template <class T>
122inline
123CachedValue<T>& CachedValue<T>::operator= (const CachedValue& other)
124{
125 if (this != &other) {
126 if (other.m_cacheValid == VALID) {
127 m_cacheValid = VALID;
128 m_val = other.m_val;
129 }
130 else {
131 m_cacheValid = INVALID;
132 }
133 }
134 return *this;
135}
136
137
138/**
139 * @brief Move.
140 * No concurrent operations on OTHER are possible.
141 */
142template <class T>
143inline
144CachedValue<T>& CachedValue<T>::operator= (CachedValue&& other) noexcept
145{
146 if (this != &other) {
147 if (other.m_cacheValid == VALID) {
148 m_val = std::move (other.m_val);
149 other.m_cacheValid = INVALID;
150 m_cacheValid = VALID;
151 }
152 else {
153 m_cacheValid = INVALID;
154 }
155 }
156 return *this;
157}
158
159
160/// Helper for setting the value in an exception-safe manner.
161struct CacheLock
162{
163 /// If the cache is currently INVALID, then switch to UPDATING.
164 /// In that case, then this object will evaluate as true, and the
165 /// state will be changed to VALID when this object is destroyed.
166 /// Otherwise, this object will evaluate as false, and the cache state
167 /// will not be changed.
168 CacheLock (std::atomic<CacheState>& flag)
169 : m_flag (flag),
170 m_vflag (INVALID)
171 {
172 flag.compare_exchange_strong (m_vflag, UPDATING);
173 }
174 operator bool() const { return m_vflag == INVALID; }
175 ~CacheLock() { if (m_vflag == INVALID) m_flag = VALID; }
176
177 std::atomic<CacheState>& m_flag;
178 CacheState m_vflag;
179};
180
181
182/**
183 * @brief Set the value, assuming it is currently invalid.
184 * Otherwise, this method will do nothing.
185 * The value will be valid once this returns.
186 */
187template <class T>
188inline
189void CachedValue<T>::set (const T& val) const
190{
191 // Interlocked assignment of the cached value,
192 // only if the current state is INVALID.
193 {
194 CacheLock lock (m_cacheValid);
195 if (lock) {
196 m_val = val;
197 }
198 else {
199 // Spin if another thread is currently updating.
200 while (m_cacheValid == UPDATING) {
201 CxxUtils::stall();
202 }
203 }
204 }
205}
206
207
208/**
209 * @brief Set the value by move, assuming it is currently invalid.
210 * Otherwise, this method will do nothing.
211 * The value will be valid once this returns.
212 */
213template <class T>
214inline
215void CachedValue<T>::set (T&& val) const noexcept
216{
217 // Interlocked assignment of the cached value,
218 // only if the current state is INVALID.
219 {
220 CacheLock lock (m_cacheValid);
221 if (lock) {
222 m_val = std::move (val);
223 }
224 else {
225 // Spin if another thread is currently updating.
226 while (m_cacheValid == UPDATING) {
227 CxxUtils::stall();
228 }
229 }
230 }
231}
232
233
234/**
235 * @brief Test to see if the value is valid.
236 * May spin if another thread is currently updating the value.
237 */
238template <class T>
239inline
240bool CachedValue<T>::isValid() const
241{
242 CacheState stat = m_cacheValid;
243 if (stat == VALID) [[likely]] return true;
244 // Get the state flag.
245 // Spin if another thread is currently updating.
246 while ((stat = m_cacheValid) == UPDATING) {
247 CxxUtils::stall();
248 }
249 return stat == VALID;
250}
251
252
253/**
254 * @brief Return a pointer to the cached value.
255 * Should not be called unless isValid() has returned true or set()
256 * has returned.
257 */
258template <class T>
259inline
260const T* CachedValue<T>::ptr() const
261{
262 return &m_val;
263}
264
265
266/**
267 * @brief Store a new value to the value.
268 */
269template <class T>
270inline
271void CachedValue<T>::store (const T& val)
272{
273 m_cacheValid = VALID;
274 m_val = val;
275}
276
277/**
278 * @brief Store a new value to the value, by move.
279 */
280template <class T>
281inline
282void CachedValue<T>::store (T&& val) noexcept
283{
284 m_cacheValid = VALID;
285 m_val = std::move (val);
286}
287
288/**
289 * @brief Reset the value to invalid.
290 */
291template <class T>
292inline
293void CachedValue<T>::reset()
294{
295 m_cacheValid = INVALID;
296}
297
298
299} // namespace CxxUtils