2 Copyright (C) 2002-2026 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.
16 * @brief Default constructor. Sets the value to invalid.
20CachedValue<T>::CachedValue()
22 m_cacheValid (INVALID)
28 * @brief Constructor from a value.
32CachedValue<T>::CachedValue (const T& val)
40 * @brief Move constructor from a value.
44CachedValue<T>::CachedValue (T&& val) noexcept
45 : m_val (std::move (val)),
52 * @brief Copy constructor.
56CachedValue<T>::CachedValue (const CachedValue& other)
58 m_cacheValid (INVALID)
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) {
70 * @brief Move constructor.
71 * No concurrent operations on OTHER are possible.
75CachedValue<T>::CachedValue (CachedValue&& other) noexcept
77 m_cacheValid (INVALID)
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.
84#pragma GCC diagnostic push
85#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
87 m_val = std::move (other.m_val);
89#pragma GCC diagnostic pop
91 other.m_cacheValid = INVALID;
98 * @brief Copy constructor from other type.
103CachedValue<T>::CachedValue (const CachedValue<U>& other)
104 : m_cacheValid (INVALID)
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;
123CachedValue<T>& CachedValue<T>::operator= (const CachedValue& other)
125 if (this != &other) {
126 if (other.m_cacheValid == VALID) {
127 m_cacheValid = VALID;
131 m_cacheValid = INVALID;
140 * No concurrent operations on OTHER are possible.
144CachedValue<T>& CachedValue<T>::operator= (CachedValue&& other) noexcept
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;
153 m_cacheValid = INVALID;
160/// Helper for setting the value in an exception-safe manner.
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)
172 flag.compare_exchange_strong (m_vflag, UPDATING);
174 operator bool() const { return m_vflag == INVALID; }
175 ~CacheLock() { if (m_vflag == INVALID) m_flag = VALID; }
177 std::atomic<CacheState>& m_flag;
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.
189void CachedValue<T>::set (const T& val) const
191 // Interlocked assignment of the cached value,
192 // only if the current state is INVALID.
194 CacheLock lock (m_cacheValid);
199 // Spin if another thread is currently updating.
200 while (m_cacheValid == UPDATING) {
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.
215void CachedValue<T>::set (T&& val) const noexcept
217 // Interlocked assignment of the cached value,
218 // only if the current state is INVALID.
220 CacheLock lock (m_cacheValid);
222 m_val = std::move (val);
225 // Spin if another thread is currently updating.
226 while (m_cacheValid == UPDATING) {
235 * @brief Test to see if the value is valid.
236 * May spin if another thread is currently updating the value.
240bool CachedValue<T>::isValid() const
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) {
249 return stat == VALID;
254 * @brief Return a pointer to the cached value.
255 * Should not be called unless isValid() has returned true or set()
260const T* CachedValue<T>::ptr() const
267 * @brief Store a new value to the value.
271void CachedValue<T>::store (const T& val)
273 m_cacheValid = VALID;
278 * @brief Store a new value to the value, by move.
282void CachedValue<T>::store (T&& val) noexcept
284 m_cacheValid = VALID;
285 m_val = std::move (val);
289 * @brief Reset the value to invalid.
293void CachedValue<T>::reset()
295 m_cacheValid = INVALID;
299} // namespace CxxUtils