File indexing completed on 2024-05-12 15:57:02
0001 /* 0002 * SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #ifndef KISLAZYSHAREDCACHESTORAGE_H 0008 #define KISLAZYSHAREDCACHESTORAGE_H 0009 0010 #include <type_traits> 0011 #include <functional> 0012 #include <utility> 0013 0014 #include <QMutex> 0015 #include <QAtomicPointer> 0016 #include <QSharedPointer> 0017 #include <QSharedDataPointer> 0018 #include <QExplicitlySharedDataPointer> 0019 0020 namespace KisLazySharedCacheStorageDetail 0021 { 0022 0023 /** 0024 * A policy-like class for sharing the **data** between two 0025 * objects 0026 */ 0027 template <typename StorageType, typename T, typename... Args> 0028 struct DataStorage 0029 { 0030 using ConstType = std::add_const_t<T>; 0031 using FactoryType = T*(Args...); 0032 0033 DataStorage() {} 0034 DataStorage(T *value) : m_value(value) {} 0035 DataStorage(const DataStorage &rhs) = default; 0036 DataStorage& operator=(const DataStorage &rhs) = default; 0037 0038 ConstType* lazyInitialize(const std::function<FactoryType> &factory, Args... args) { 0039 if (!m_value) { 0040 m_value.reset(factory(std::forward<Args...>(args...))); 0041 } 0042 return m_value.data(); 0043 } 0044 0045 bool hasValue() const { 0046 return !m_value.isNull(); 0047 } 0048 0049 void reset() { 0050 m_value.reset(); 0051 } 0052 0053 private: 0054 StorageType m_value; 0055 }; 0056 0057 template <typename T, typename... Args> 0058 using DataWrapperLocal = DataStorage<QSharedPointer<T>, T, Args...>; 0059 0060 0061 /** 0062 * A policy-like class for sharing DataStorage object 0063 * between the two different objects. It just ads one 0064 * more level of locking to ensure thread-safety. 0065 */ 0066 template <typename T, typename... Args> 0067 struct DataWrapperShared 0068 { 0069 using ConstType = std::add_const_t<T>; 0070 using FactoryType = T*(Args...); 0071 0072 private: 0073 struct SharedStorage 0074 { 0075 using ConstType = std::add_const_t<T>; 0076 0077 SharedStorage() {} 0078 SharedStorage(T *value) : m_value(value) {} 0079 SharedStorage(const SharedStorage &rhs) = delete; 0080 0081 QMutex sharedMutex; 0082 DataStorage<QScopedPointer<ConstType>, T, Args...> m_value; 0083 }; 0084 public: 0085 0086 0087 DataWrapperShared() : m_sharedStorage(new SharedStorage()) {} 0088 DataWrapperShared(T *value) : m_sharedStorage(new SharedStorage(value)) {} 0089 DataWrapperShared(const DataWrapperShared &rhs) = default; 0090 DataWrapperShared& operator=(const DataWrapperShared &rhs) = default; 0091 0092 ConstType* lazyInitialize(const std::function<FactoryType> &factory, Args... args) { 0093 QMutexLocker l(&m_sharedStorage->sharedMutex); 0094 return m_sharedStorage->m_value.lazyInitialize(factory, std::forward<Args...>(args...)); 0095 } 0096 0097 bool hasValue() const { 0098 QMutexLocker l(&m_sharedStorage->sharedMutex); 0099 return m_sharedStorage->m_value.hasValue(); 0100 } 0101 0102 void reset() { 0103 m_sharedStorage.reset(new SharedStorage()); 0104 } 0105 0106 private: 0107 QSharedPointer<SharedStorage> m_sharedStorage; 0108 }; 0109 0110 } 0111 0112 template <typename DataWrapper, typename T, typename... Args> 0113 class KisLazySharedCacheStorageBase 0114 { 0115 public: 0116 using ConstType = std::add_const_t<T>; 0117 using FactoryType = T*(Args...); 0118 0119 public: 0120 KisLazySharedCacheStorageBase() 0121 { 0122 } 0123 0124 KisLazySharedCacheStorageBase(std::function<FactoryType> factory) 0125 : m_factory(factory) 0126 { 0127 } 0128 0129 KisLazySharedCacheStorageBase(const KisLazySharedCacheStorageBase &rhs) { 0130 QMutexLocker l(&rhs.m_mutex); 0131 m_factory = rhs.m_factory; 0132 m_dataWrapper = rhs.m_dataWrapper; 0133 m_cachedValue = rhs.m_cachedValue; 0134 } 0135 0136 void setFactory(std::function<FactoryType> factory) { 0137 m_factory = factory; 0138 } 0139 0140 ConstType* value(Args... args) { 0141 ConstType *result = 0; 0142 0143 if (m_cachedValue) { 0144 result = m_cachedValue; 0145 } else { 0146 QMutexLocker l1(&m_mutex); 0147 m_cachedValue = m_dataWrapper.lazyInitialize(m_factory, args...); 0148 result = m_cachedValue; 0149 } 0150 return result; 0151 } 0152 0153 bool isNull() const { 0154 return !bool(m_cachedValue) && !m_dataWrapper.hasValue(); 0155 } 0156 0157 void initialize(Args... args) { 0158 (void) value(args...); 0159 } 0160 0161 void reset() { 0162 QMutexLocker l(&m_mutex); 0163 m_cachedValue.store(nullptr); 0164 m_dataWrapper.reset(); 0165 } 0166 0167 private: 0168 std::function<FactoryType> m_factory; 0169 DataWrapper m_dataWrapper; 0170 QAtomicPointer<ConstType> m_cachedValue; 0171 mutable QMutex m_mutex; 0172 }; 0173 0174 /** 0175 * KisLazySharedCacheStorage is a special class that allows two classes 0176 * lazily share an existing cache data. That is, after the class has been 0177 * cloned via a copy constructor it keeps a pointer to the cached data of 0178 * the source object. This link is kept until one of the objects calls 0179 * `cache.reset()`, then the cached data is detached. 0180 * 0181 * Take it into account that KisLazySharedCacheStorage will keep a link 0182 * **only** in case the cache existed at the moment of cloning. That is 0183 * the main difference to KisLazySharedCacheStorageLinked. 0184 * 0185 * The class guarantees full tread-safety of the access and detach of 0186 * the cache object. 0187 */ 0188 template <typename T, typename... Args> 0189 using KisLazySharedCacheStorage = 0190 KisLazySharedCacheStorageBase< 0191 KisLazySharedCacheStorageDetail::DataWrapperLocal<T, Args...>, T, Args...>; 0192 0193 /** 0194 * KisLazySharedCacheStorageLinked is a special class that allows two classes 0195 * lazily share a cache object. The two classes will not only share cache data, 0196 * but also the cache object itself. That is, when one of the objects updates 0197 * the cache data, this data will also be uploaded into the cache of the other 0198 * object. The link between the two objects is kept until one of the objects 0199 * calls `cache.reset()`. 0200 * 0201 * The class guarantees full tread-safety of the access and detach of 0202 * the cache object. 0203 */ 0204 template <typename T, typename... Args> 0205 using KisLazySharedCacheStorageLinked = 0206 KisLazySharedCacheStorageBase< 0207 KisLazySharedCacheStorageDetail::DataWrapperShared<T, Args...>, T, Args...>; 0208 0209 0210 #endif // KISLAZYSHAREDCACHESTORAGE_H