CacheInvalidator.h (4491B)
1 /* -*- Mode: C++; tab-width: 13; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=13 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef MOZILLA_CACHE_INVALIDATOR_H_ 8 #define MOZILLA_CACHE_INVALIDATOR_H_ 9 10 #include <cstddef> // nullptr_t 11 #include <memory> 12 #include <optional> 13 #include <vector> 14 15 #include "DmdStdContainers.h" 16 #include "mozilla/Assertions.h" 17 18 // - 19 20 namespace mozilla { 21 22 class AbstractCache; 23 24 // - 25 26 class CacheInvalidator { 27 friend class AbstractCache; 28 29 private: 30 mutable webgl::dmd_unordered_set<AbstractCache*> mCaches; 31 32 public: 33 virtual ~CacheInvalidator() { 34 // It's actually generally unsafe to wait until now to invalidate caches, 35 // because when used as a mixin, this dtor is called after the dtor for the 36 // derived class. This means that if the derived class holds a cache (or is 37 // a cache!), OnInvalidate() will be called on a destroyed object. 38 // MOZ_ASSERT(!mCaches); 39 InvalidateCaches(); 40 } 41 42 void InvalidateCaches() const; 43 44 // - 45 46 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mso) const { 47 return mCaches.SizeOfExcludingThis(mso); 48 } 49 }; 50 51 // - 52 53 class AbstractCache { 54 using InvalidatorListT = std::vector<const CacheInvalidator*>; 55 56 private: 57 InvalidatorListT mInvalidators; 58 59 public: 60 AbstractCache() = default; 61 62 explicit AbstractCache(InvalidatorListT&& invalidators) { 63 ResetInvalidators(std::move(invalidators)); 64 } 65 66 virtual ~AbstractCache() { ResetInvalidators({}); } 67 68 public: 69 virtual void OnInvalidate() = 0; 70 71 void ResetInvalidators(InvalidatorListT&&); 72 void AddInvalidator(const CacheInvalidator&); 73 }; 74 75 // - 76 77 template <typename T> 78 class CacheMaybe : public AbstractCache { 79 std::optional<T> mVal; 80 81 public: 82 CacheMaybe& operator=(std::optional<T>&& rhs) { 83 mVal.swap(rhs); 84 rhs.reset(); 85 return *this; 86 } 87 88 template <typename U> 89 CacheMaybe& operator=(U&& rhs) { 90 mVal.emplace(std::move(rhs)); 91 return *this; 92 } 93 94 CacheMaybe& operator=(std::nullptr_t) { 95 mVal.reset(); 96 return *this; 97 } 98 99 void OnInvalidate() override { 100 *this = {}; 101 ResetInvalidators({}); 102 } 103 104 explicit operator bool() const { return bool(mVal); } 105 T* get() const { return mVal ? &*mVal : nullptr; } 106 T* operator->() const { return get(); } 107 }; 108 109 // - 110 111 template <typename KeyT, typename ValueT> 112 class CacheWeakMap final { 113 class Entry final : public AbstractCache { 114 public: 115 CacheWeakMap& mParent; 116 const KeyT mKey; 117 const ValueT mValue; 118 119 Entry(CacheWeakMap& parent, const KeyT& key, ValueT&& value) 120 : mParent(parent), mKey(key), mValue(std::move(value)) {} 121 122 void OnInvalidate() override { 123 const auto erased = mParent.mMap.erase(&mKey); 124 MOZ_ALWAYS_TRUE(erased == 1); 125 } 126 }; 127 128 struct DerefHash final { 129 size_t operator()(const KeyT* const a) const { 130 return std::hash<KeyT>()(*a); 131 } 132 }; 133 struct DerefEqual final { 134 bool operator()(const KeyT* const a, const KeyT* const b) const { 135 return *a == *b; 136 } 137 }; 138 139 using MapT = webgl::dmd_unordered_map<const KeyT*, std::unique_ptr<Entry>, 140 DerefHash, DerefEqual>; 141 MapT mMap; 142 143 public: 144 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mso) const { 145 return mMap.SizeOfExcludingThis(mso); 146 } 147 148 std::unique_ptr<Entry> MakeEntry(const KeyT& key, ValueT&& value) { 149 return std::unique_ptr<Entry>(new Entry(*this, key, std::move(value))); 150 } 151 std::unique_ptr<Entry> MakeEntry(const KeyT& key, const ValueT& value) { 152 return MakeEntry(key, ValueT(value)); 153 } 154 155 const ValueT* Insert(std::unique_ptr<Entry>&& entry) { 156 auto insertable = typename MapT::value_type{&entry->mKey, std::move(entry)}; 157 158 const auto res = mMap.insert(std::move(insertable)); 159 const auto& didInsert = res.second; 160 MOZ_ALWAYS_TRUE(didInsert); 161 162 const auto& itr = res.first; 163 return &itr->second->mValue; 164 } 165 166 const ValueT* Find(const KeyT& key) const { 167 const auto itr = mMap.find(&key); 168 if (itr == mMap.end()) return nullptr; 169 170 return &itr->second->mValue; 171 } 172 173 void Clear() const { 174 while (true) { 175 const auto itr = mMap.begin(); 176 if (itr == mMap.end()) return; 177 itr->second->OnInvalidate(); 178 } 179 } 180 181 ~CacheWeakMap() { Clear(); } 182 }; 183 184 } // namespace mozilla 185 186 #endif // MOZILLA_CACHE_INVALIDATOR_H_