2 Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
5 * @file CxxUtils/CachedValue.icc
6 * @author scott snyder <snyder@bnl.gov>
8 * @brief Cached value with atomic update.
12 #include "CxxUtils/AthUnlikelyMacros.h"
19 * @brief Default constructor. Sets the value to invalid.
23 //cppcheck-suppress uninitMemberVar
24 CachedValue<T>::CachedValue()
26 m_cacheValid (INVALID)
32 * @brief Constructor from a value.
36 //cppcheck-suppress uninitMemberVar
37 CachedValue<T>::CachedValue (const T& val)
45 * @brief Move constructor from a value.
49 //cppcheck-suppress uninitMemberVar
50 CachedValue<T>::CachedValue (T&& val) noexcept
51 : m_val (std::move (val)),
58 * @brief Copy constructor.
62 CachedValue<T>::CachedValue (const CachedValue& other)
64 m_cacheValid (INVALID)
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) {
76 * @brief Move constructor.
77 * No concurrent operations on OTHER are possible.
81 CachedValue<T>::CachedValue (CachedValue&& other) noexcept
83 m_cacheValid (INVALID)
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.
90 #pragma GCC diagnostic push
91 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
93 m_val = std::move (other.m_val);
95 #pragma GCC diagnostic pop
97 other.m_cacheValid = INVALID;
104 * @brief Copy constructor from other type.
109 CachedValue<T>::CachedValue (const CachedValue<U>& other)
110 : m_cacheValid (INVALID)
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;
129 CachedValue<T>& CachedValue<T>::operator= (const CachedValue& other)
131 if (this != &other) {
132 if (other.m_cacheValid == VALID) {
133 m_cacheValid = VALID;
137 m_cacheValid = INVALID;
146 * No concurrent operations on OTHER are possible.
150 CachedValue<T>& CachedValue<T>::operator= (CachedValue&& other) noexcept
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;
159 m_cacheValid = INVALID;
166 /// Helper for setting the value in an exception-safe manner.
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)
178 flag.compare_exchange_strong (m_vflag, UPDATING);
180 operator bool() const { return m_vflag == INVALID; }
181 ~CacheLock() { if (m_vflag == INVALID) m_flag = VALID; }
183 std::atomic<CacheState>& m_flag;
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.
195 void CachedValue<T>::set (const T& val) const
197 // Interlocked assignment of the cached value,
198 // only if the current state is INVALID.
200 CacheLock lock (m_cacheValid);
205 // Spin if another thread is currently updating.
206 while (m_cacheValid == UPDATING) {
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.
221 void CachedValue<T>::set (T&& val) const noexcept
223 // Interlocked assignment of the cached value,
224 // only if the current state is INVALID.
226 CacheLock lock (m_cacheValid);
228 m_val = std::move (val);
231 // Spin if another thread is currently updating.
232 while (m_cacheValid == UPDATING) {
241 * @brief Test to see if the value is valid.
242 * May spin if another thread is currently updating the value.
246 bool CachedValue<T>::isValid() const
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) {
255 return stat == VALID;
260 * @brief Return a pointer to the cached value.
261 * Should not be called unless isValid() has returned true or set()
266 const T* CachedValue<T>::ptr() const
273 * @brief Store a new value to the value.
277 void CachedValue<T>::store (const T& val)
279 m_cacheValid = VALID;
284 * @brief Store a new value to the value, by move.
288 void CachedValue<T>::store (T&& val) noexcept
290 m_cacheValid = VALID;
291 m_val = std::move (val);
295 * @brief Reset the value to invalid.
299 void CachedValue<T>::reset()
301 m_cacheValid = INVALID;
305 } // namespace CxxUtils