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