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