| // Copyright 2018 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef SRC_LIB_CALLBACK_AUTO_CLEANABLE_H_ | |
| #define SRC_LIB_CALLBACK_AUTO_CLEANABLE_H_ | |
| #include <lib/async/default.h> | |
| #include <lib/fit/function.h> | |
| #include <lib/syslog/cpp/macros.h> | |
| #include <functional> | |
| #include <map> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include "src/lib/callback/scoped_task_runner.h" | |
| namespace callback { | |
| // List that will delete its elements when they call their on_discardable. | |
| // The elements must have a setter method: | |
| // |void SetOnDiscardable(fit::closure on_discardable)| | |
| // and another to check whether they can be discarded: | |
| // |bool IsDiscardable()|. | |
| template <typename V> | |
| class AutoCleanableSet { | |
| public: | |
| class ReferenceEquality { | |
| public: | |
| bool operator()(const V& v1, const V& v2) const { return &v1 == &v2; } | |
| }; | |
| class ReferenceHash { | |
| public: | |
| uintptr_t operator()(const V& v) const { return reinterpret_cast<uintptr_t>(&v); } | |
| }; | |
| using Set_ = typename std::unordered_set<V, ReferenceHash, ReferenceEquality>; | |
| class iterator : public std::iterator<std::forward_iterator_tag, V> { | |
| public: | |
| explicit iterator(typename Set_::iterator base) : base_(base) {} | |
| iterator& operator++() { | |
| ++base_; | |
| return *this; | |
| } | |
| iterator operator++(int) { | |
| iterator result(base_); | |
| operator++(); | |
| return result; | |
| } | |
| bool operator==(const iterator& rhs) const { return base_ == rhs.base_; } | |
| bool operator!=(const iterator& rhs) const { return base_ != rhs.base_; } | |
| // This set uses the reference of the object (and not its contents) to | |
| // define object equality. Thus, calling non-const methods on the stored | |
| // objects does not change their hash, and they are safe to use. | |
| V& operator*() const { return const_cast<V&>(*(base_)); } | |
| V* operator->() const { return const_cast<V*>(base_.operator->()); } | |
| private: | |
| typename Set_::iterator base_; | |
| }; | |
| AutoCleanableSet(async_dispatcher_t* dispatcher) : task_runner_(dispatcher) {} | |
| AutoCleanableSet(const AutoCleanableSet<V>& other) = delete; | |
| ~AutoCleanableSet() {} | |
| AutoCleanableSet<V>& operator=(const AutoCleanableSet<V>& other) = delete; | |
| // Capacity methods. | |
| size_t size() const { return set_.size(); } | |
| bool empty() const { return set_.empty(); } | |
| void clear() { | |
| task_runner_.Reset(); | |
| set_.clear(); | |
| } | |
| template <class... Args> | |
| V& emplace(Args&&... args) { | |
| auto pair = set_.emplace(std::forward<Args>(args)...); | |
| FX_DCHECK(pair.second); | |
| // Set iterators are const because modifying the element would change the | |
| // hash. In this particular case, this is safe because this set uses | |
| // reference equality. | |
| V& item = const_cast<V&>(*(pair.first)); | |
| item.SetOnDiscardable([this, &item] { | |
| task_runner_.PostTask([this, &item] { | |
| auto it = set_.find(item); | |
| if (it == set_.end() || !it->IsDiscardable()) { | |
| return; | |
| } | |
| set_.erase(it); | |
| CheckDiscardable(); | |
| }); | |
| }); | |
| return item; | |
| } | |
| iterator begin() { return iterator(set_.begin()); } | |
| iterator end() { return iterator(set_.end()); } | |
| void SetOnDiscardable(fit::closure on_discardable) { | |
| on_discardable_ = std::move(on_discardable); | |
| } | |
| bool IsDiscardable() const { return empty(); } | |
| private: | |
| static bool Equals(const V& v1, const V& v2) { return v1 == v2; }; | |
| static std::size_t Hash(const V& v1) { return &v1; }; | |
| void CheckDiscardable() { | |
| if (IsDiscardable() && on_discardable_) | |
| on_discardable_(); | |
| } | |
| Set_ set_; | |
| fit::closure on_discardable_; | |
| // Must be the last member of the class. | |
| ScopedTaskRunner task_runner_; | |
| }; | |
| // Map that will delete its elements when they call their on_discardable. | |
| // The elements must have a setter method: | |
| // |void SetOnDiscardable(fit::closure on_discardable)| | |
| // and another to check whether they can be discarded: | |
| // |bool IsDiscardable()|. | |
| template <typename K, typename V, typename Compare = std::less<K>> | |
| class AutoCleanableMap { | |
| public: | |
| using Map_ = typename std::map<K, V, Compare>; | |
| using iterator = typename Map_::iterator; | |
| using const_iterator = typename Map_::const_iterator; | |
| AutoCleanableMap(async_dispatcher_t* dispatcher) : task_runner_(dispatcher) {} | |
| AutoCleanableMap(const AutoCleanableMap<K, V, Compare>& other) = delete; | |
| ~AutoCleanableMap() {} | |
| AutoCleanableMap<K, V, Compare>& operator=(const AutoCleanableMap<K, V, Compare>& other) = delete; | |
| template <typename Key, typename... Args> | |
| std::pair<iterator, bool> try_emplace(Key&& key, Args&&... args) { | |
| auto result = map_.try_emplace(std::forward<Key>(key), std::forward<Args>(args)...); | |
| if (result.second) { | |
| auto& key = result.first->first; | |
| auto& value = result.first->second; | |
| value.SetOnDiscardable([this, key]() { | |
| task_runner_.PostTask([this, key] { | |
| auto it = map_.find(key); | |
| if (it != map_.end() && it->second.IsDiscardable()) { | |
| map_.erase(it); | |
| } | |
| CheckDiscardable(); | |
| }); | |
| }); | |
| } | |
| return result; | |
| } | |
| template <typename... Args> | |
| std::pair<iterator, bool> emplace(Args&&... args) { | |
| auto result = map_.emplace(std::forward<Args>(args)...); | |
| if (result.second) { | |
| auto& key = result.first->first; | |
| auto& value = result.first->second; | |
| value.SetOnDiscardable([this, key] { | |
| task_runner_.PostTask([this, key] { | |
| auto it = map_.find(key); | |
| if (it == map_.end() || !it->second.IsDiscardable()) { | |
| return; | |
| } | |
| map_.erase(it); | |
| CheckDiscardable(); | |
| }); | |
| }); | |
| } | |
| return result; | |
| } | |
| void erase(iterator pos) { | |
| map_.erase(pos); | |
| CheckDiscardable(); | |
| } | |
| template <class KR> | |
| iterator find(const KR& x) { | |
| return map_.find(x); | |
| } | |
| template <class KR> | |
| const_iterator find(const KR& x) const { | |
| return map_.find(x); | |
| } | |
| iterator begin() { return map_.begin(); } | |
| const_iterator begin() const { return map_.begin(); } | |
| iterator end() { return map_.end(); } | |
| const_iterator end() const { return map_.end(); } | |
| void SetOnDiscardable(fit::closure on_discardable) { | |
| on_discardable_ = std::move(on_discardable); | |
| } | |
| bool IsDiscardable() const { return empty(); } | |
| size_t size() const { return map_.size(); } | |
| bool empty() const { return map_.empty(); } | |
| void clear() { | |
| task_runner_.Reset(); | |
| map_.clear(); | |
| } | |
| private: | |
| void CheckDiscardable() { | |
| if (IsDiscardable() && on_discardable_) { | |
| on_discardable_(); | |
| } | |
| } | |
| Map_ map_; | |
| fit::closure on_discardable_; | |
| // Must be the last member of the class. | |
| ScopedTaskRunner task_runner_; | |
| }; | |
| } // namespace callback | |
| #endif // SRC_LIB_CALLBACK_AUTO_CLEANABLE_H_ |