File indexing completed on 2024-05-12 15:58:26
0001 /* 0002 * SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #ifndef __KIS_CACHE_STATE_VALUE_H 0008 #define __KIS_CACHE_STATE_VALUE_H 0009 0010 #include <QAtomicInt> 0011 0012 /** 0013 * This class implements a state variable for a lockfree cache object. 0014 * The implementation is actually a variation of a 'seqlock', but with 0015 * a simple modification: the values of "cache validity", "number of 0016 * writers" and "sequence number" are multiplexed in a single int 0017 * value. 0018 */ 0019 class KisCacheStateValue 0020 { 0021 static const int WritersCountMask = 0x00FF; 0022 static const int WritersCountIncrement = 0x0001; 0023 static const int IsValidMask = 0x0100; 0024 static const int SeqNoMask = ~(WritersCountMask | IsValidMask); 0025 static const int SeqNoIncrement = 0x0200; 0026 public: 0027 typedef int SeqValue; 0028 public: 0029 inline void invalidate() { 0030 int oldValue; 0031 int newValue = -1; 0032 do { 0033 oldValue = m_value; 0034 newValue = incrementSeqNo(oldValue) & ~IsValidMask; 0035 } while(!m_value.testAndSetOrdered(oldValue, newValue)); 0036 } 0037 0038 inline bool startRead(int *seq) const { 0039 *seq = m_value; 0040 0041 return (*seq & IsValidMask) && 0042 !(*seq & WritersCountMask); 0043 } 0044 0045 inline bool endRead(int seq) const { 0046 bool result = seq == m_value; 0047 return result; 0048 } 0049 0050 0051 inline bool startWrite(int *seq) { 0052 int oldValue; 0053 int newValue; 0054 do { 0055 oldValue = m_value; 0056 if ((oldValue & IsValidMask) || 0057 (oldValue & WritersCountMask)) { 0058 0059 return false; 0060 } 0061 newValue = incrementSeqNo(oldValue) + WritersCountIncrement; 0062 } while(!m_value.testAndSetOrdered(oldValue, newValue)); 0063 0064 *seq = newValue; 0065 return true; 0066 } 0067 0068 inline void endWrite(int seq) { 0069 int oldValue; 0070 int newValue; 0071 do { 0072 oldValue = m_value; 0073 0074 if (oldValue == seq) { 0075 newValue = (incrementSeqNo(oldValue) - WritersCountIncrement) | IsValidMask; 0076 } else { 0077 newValue = (incrementSeqNo(oldValue) - WritersCountIncrement) & ~IsValidMask; 0078 } 0079 } while(!m_value.testAndSetOrdered(oldValue, newValue)); 0080 } 0081 0082 private: 0083 int incrementSeqNo(int value) { 0084 // handle overflow properly 0085 if ((value & SeqNoMask) == SeqNoMask) { 0086 value = value & ~SeqNoMask; 0087 } else { 0088 value += SeqNoIncrement; 0089 } 0090 return value; 0091 } 0092 0093 private: 0094 QAtomicInt m_value; 0095 }; 0096 0097 template <typename T> 0098 class KisLockFreeCache 0099 { 0100 public: 0101 virtual ~KisLockFreeCache() {} 0102 0103 /** 0104 * Notify the cache that the value has changed 0105 */ 0106 void invalidate() { 0107 m_state.invalidate(); 0108 } 0109 0110 /** 0111 * Calculate the value or fetch it from the cache 0112 */ 0113 T getValue() const { 0114 KisCacheStateValue::SeqValue seqValue; 0115 bool isValid = false; 0116 T savedValue; 0117 0118 if (m_state.startRead(&seqValue)) { 0119 savedValue = m_value; 0120 isValid = m_state.endRead(seqValue); 0121 } 0122 0123 if (isValid) { 0124 return savedValue; 0125 } else if (m_state.startWrite(&seqValue)) { 0126 savedValue = calculateNewValue(); 0127 m_value = savedValue; 0128 m_state.endWrite(seqValue); 0129 return savedValue; 0130 } else { 0131 return calculateNewValue(); 0132 } 0133 } 0134 0135 bool tryGetValue(T &result) const { 0136 KisCacheStateValue::SeqValue seqValue; 0137 bool isValid = false; 0138 T savedValue; 0139 0140 if (m_state.startRead(&seqValue)) { 0141 savedValue = m_value; 0142 isValid = m_state.endRead(seqValue); 0143 } 0144 0145 if (isValid) { 0146 result = savedValue; 0147 } 0148 0149 return isValid; 0150 } 0151 0152 protected: 0153 /** 0154 * Calculate the value. Used by the cache 0155 * internally. Reimplemented by the user. 0156 */ 0157 virtual T calculateNewValue() const = 0; 0158 0159 private: 0160 mutable KisCacheStateValue m_state; 0161 mutable T m_value; 0162 }; 0163 0164 template <typename T, typename Mode> 0165 class KisLockFreeCacheWithModeConsistency 0166 { 0167 public: 0168 virtual ~KisLockFreeCacheWithModeConsistency() 0169 { 0170 } 0171 0172 /** 0173 * Notify the cache that the value has changed 0174 */ 0175 void invalidate() { 0176 m_state.invalidate(); 0177 } 0178 0179 /** 0180 * Calculate the value or fetch it from the cache 0181 */ 0182 T getValue(Mode mode) const { 0183 KisCacheStateValue::SeqValue seqValue; 0184 bool isValid = false; 0185 T savedValue; 0186 Mode savedMode; 0187 0188 if (m_state.startRead(&seqValue)) { 0189 savedValue = m_value; 0190 savedMode = m_mode; 0191 isValid = m_state.endRead(seqValue); 0192 isValid &= savedMode == mode; 0193 } 0194 0195 if (isValid) { 0196 return savedValue; 0197 } else if (m_state.startWrite(&seqValue)) { 0198 savedValue = calculateNewValue(); 0199 m_value = savedValue; 0200 m_mode = mode; 0201 m_state.endWrite(seqValue); 0202 return savedValue; 0203 } else { 0204 return calculateNewValue(); 0205 } 0206 } 0207 0208 bool tryGetValue(T &result, Mode mode) const { 0209 KisCacheStateValue::SeqValue seqValue; 0210 bool isValid = false; 0211 T newValue; 0212 Mode savedMode; 0213 0214 if (m_state.startRead(&seqValue)) { 0215 newValue = m_value; 0216 savedMode = m_mode; 0217 isValid = m_state.endRead(seqValue); 0218 isValid &= savedMode == mode; 0219 } 0220 0221 if (isValid) { 0222 result = newValue; 0223 } 0224 0225 return isValid; 0226 } 0227 0228 protected: 0229 /** 0230 * Calculate the value. Used by the cache 0231 * internally. Reimplemented by the user. 0232 */ 0233 virtual T calculateNewValue() const = 0; 0234 0235 private: 0236 mutable KisCacheStateValue m_state; 0237 mutable T m_value; 0238 mutable Mode m_mode; 0239 }; 0240 0241 0242 0243 #endif /* __KIS_CACHE_STATE_VALUE_H */