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 CachedValue<T>::CachedValue()
25 m_cacheValid (INVALID)
31 * @brief Constructor from a value.
35 CachedValue<T>::CachedValue (const T& val)
43 * @brief Move constructor from a value.
47 CachedValue<T>::CachedValue (T&& val) noexcept
48 : m_val (std::move (val)),
55 * @brief Copy constructor.
59 CachedValue<T>::CachedValue (const CachedValue& other)
61 m_cacheValid (INVALID)
63 // If the source is in the middle of an update, set this value
64 // to invalid. Don't copy the value itself unless the source is valid.
65 if (other.m_cacheValid == VALID) {
73 * @brief Move constructor.
74 * No concurrent operations on OTHER are possible.
78 CachedValue<T>::CachedValue (CachedValue&& other) noexcept
80 m_cacheValid (INVALID)
82 if (other.m_cacheValid == VALID) {
83 // If we get here, m_val should be properly initialized.
84 // But sometimes gcc12 has trouble proving that.
85 // Suppress a maybe-uninitialized warning here.
87 #pragma GCC diagnostic push
88 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
90 m_val = std::move (other.m_val);
92 #pragma GCC diagnostic pop
94 other.m_cacheValid = INVALID;
101 * @brief Copy constructor from other type.
106 CachedValue<T>::CachedValue (const CachedValue<U>& other)
107 : m_cacheValid (INVALID)
109 // If the source is in the middle of an update, set this value
110 // to invalid. Don't copy the value itself unless the source is valid.
111 if (other.m_cacheValid == VALID) {
112 m_cacheValid = VALID;
126 CachedValue<T>& CachedValue<T>::operator= (const CachedValue& other)
128 if (this != &other) {
129 if (other.m_cacheValid == VALID) {
130 m_cacheValid = VALID;
134 m_cacheValid = INVALID;
143 * No concurrent operations on OTHER are possible.
147 CachedValue<T>& CachedValue<T>::operator= (CachedValue&& other) noexcept
149 if (this != &other) {
150 if (other.m_cacheValid == VALID) {
151 m_val = std::move (other.m_val);
152 other.m_cacheValid = INVALID;
153 m_cacheValid = VALID;
156 m_cacheValid = INVALID;
163 /// Helper for setting the value in an exception-safe manner.
166 /// If the cache is currently INVALID, then switch to UPDATING.
167 /// In that case, then this object will evaluate as true, and the
168 /// state will be changed to VALID when this object is destroyed.
169 /// Otherwise, this object will evaluate as false, and the cache state
170 /// will not be changed.
171 CacheLock (std::atomic<CacheState>& flag)
175 flag.compare_exchange_strong (m_vflag, UPDATING);
177 operator bool() const { return m_vflag == INVALID; }
178 ~CacheLock() { if (m_vflag == INVALID) m_flag = VALID; }
180 std::atomic<CacheState>& m_flag;
186 * @brief Set the value, assuming it is currently invalid.
187 * Otherwise, this method will do nothing.
188 * The value will be valid once this returns.
192 void CachedValue<T>::set (const T& val) const
194 // Interlocked assignment of the cached value,
195 // only if the current state is INVALID.
197 CacheLock lock (m_cacheValid);
202 // Spin if another thread is currently updating.
203 while (m_cacheValid == UPDATING) {
212 * @brief Set the value by move, assuming it is currently invalid.
213 * Otherwise, this method will do nothing.
214 * The value will be valid once this returns.
218 void CachedValue<T>::set (T&& val) const noexcept
220 // Interlocked assignment of the cached value,
221 // only if the current state is INVALID.
223 CacheLock lock (m_cacheValid);
225 m_val = std::move (val);
228 // Spin if another thread is currently updating.
229 while (m_cacheValid == UPDATING) {
238 * @brief Test to see if the value is valid.
239 * May spin if another thread is currently updating the value.
243 bool CachedValue<T>::isValid() const
245 CacheState stat = m_cacheValid;
246 if (ATH_LIKELY(stat == VALID)) return true;
247 // Get the state flag.
248 // Spin if another thread is currently updating.
249 while ((stat = m_cacheValid) == UPDATING) {
252 return stat == VALID;
257 * @brief Return a pointer to the cached value.
258 * Should not be called unless isValid() has returned true or set()
263 const T* CachedValue<T>::ptr() const
270 * @brief Store a new value to the value.
274 void CachedValue<T>::store (const T& val)
276 m_cacheValid = VALID;
281 * @brief Store a new value to the value, by move.
285 void CachedValue<T>::store (T&& val) noexcept
287 m_cacheValid = VALID;
288 m_val = std::move (val);
292 * @brief Reset the value to invalid.
296 void CachedValue<T>::reset()
298 m_cacheValid = INVALID;
302 } // namespace CxxUtils