/* -*- Mode: C++; tab-width: 13; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=13 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef MOZILLA_CACHE_INVALIDATOR_H_ #define MOZILLA_CACHE_INVALIDATOR_H_ #include "mozilla/Maybe.h" #include "mozilla/UniquePtr.h" #include #include #include namespace std { // You know it's going to be good with this at the top of the // file. // The STL is lazy and doesn't provide these: template struct hash { auto operator()(const T* const x) const { return hash()(const_cast(x)); } }; template struct hash { auto operator()(const T x) const { return hash()(const_cast(x)); } }; } // namespace std // - namespace mozilla { class AbstractCache; // - class CacheInvalidator { friend class AbstractCache; private: mutable std::unordered_set mCaches; public: virtual ~CacheInvalidator() { // It's actually generally unsafe to wait until now to invalidate caches, // because when used as a mixin, this dtor is called after the dtor for the // derived class. This means that if the derived class holds a cache (or is // a cache!), OnInvalidate() will be called on a destroyed object. // MOZ_ASSERT(!mCaches); InvalidateCaches(); } void InvalidateCaches() const; }; // - class AbstractCache { typedef std::vector InvalidatorListT; private: InvalidatorListT mInvalidators; public: AbstractCache() = default; explicit AbstractCache(InvalidatorListT&& invalidators) { ResetInvalidators(std::move(invalidators)); } virtual ~AbstractCache() { ResetInvalidators({}); } public: virtual void OnInvalidate() = 0; InvalidatorListT ResetInvalidators( InvalidatorListT&&); // Returns the old list. void AddInvalidator(const CacheInvalidator&); }; // - template class CacheMaybe : public AbstractCache { Maybe mVal; public: template CacheMaybe& operator=(Maybe&& rhs) { mVal.reset(); if (rhs) { mVal.emplace(std::move(rhs.ref())); } return *this; } CacheMaybe& operator=(Nothing) { return *this = Maybe(); } void OnInvalidate() override { *this = Nothing(); ResetInvalidators({}); } explicit operator bool() const { return bool(mVal); } T* get() const { return mVal.ptrOr(nullptr); } T* operator->() const { return get(); } }; // - template class CacheWeakMap final { class Entry final : public AbstractCache { public: CacheWeakMap& mParent; const KeyT mKey; const ValueT mValue; Entry(CacheWeakMap& parent, const KeyT& key, ValueT&& value) : mParent(parent), mKey(key), mValue(value) {} void OnInvalidate() override { const auto erased = mParent.mMap.erase(&mKey); MOZ_ALWAYS_TRUE(erased == 1); } }; struct DerefHash final { size_t operator()(const KeyT* const a) const { return std::hash()(*a); } }; struct DerefEqual final { bool operator()(const KeyT* const a, const KeyT* const b) const { return *a == *b; } }; typedef std::unordered_map, DerefHash, DerefEqual> MapT; MapT mMap; public: UniquePtr MakeEntry(const KeyT& key, ValueT&& value) { return UniquePtr(new Entry(*this, key, std::move(value))); } UniquePtr MakeEntry(const KeyT& key, const ValueT& value) { return MakeEntry(key, ValueT(value)); } const ValueT* Insert(UniquePtr&& entry) { auto insertable = typename MapT::value_type{&entry->mKey, std::move(entry)}; const auto res = mMap.insert(std::move(insertable)); const auto& didInsert = res.second; MOZ_ALWAYS_TRUE(didInsert); const auto& itr = res.first; return &itr->second->mValue; } const ValueT* Find(const KeyT& key) const { const auto itr = mMap.find(&key); if (itr == mMap.end()) return nullptr; return &itr->second->mValue; } void Clear() const { while (true) { const auto itr = mMap.begin(); if (itr == mMap.end()) return; itr->second->OnInvalidate(); } } ~CacheWeakMap() { Clear(); } }; } // namespace mozilla #endif // MOZILLA_CACHE_INVALIDATOR_H_