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 */