1 // Copyright 2016 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #pragma once 16 17 #include "aemu/base/Compiler.h" 18 19 #include <functional> 20 #include <list> 21 #include <memory> 22 23 #include <assert.h> 24 25 namespace android { 26 namespace base { 27 28 // Any object that intends to notify certain subscribers on the event loop can 29 // use the SubscriberList to keep track of all active clients. The clients in 30 // turn, must keep the returned SubscriptionToken alive for as long as they want 31 // to be notified of updates. 32 // 33 // Once a SubscriptionToken is destroyed, the SubscriptionList removes that 34 // client from future notifications. 35 // If the SubscriptionList is destroyed while some tokens are still active, 36 // destryong them no longer has any effect. 37 // 38 // See SubscriberList_unittest.cpp -- TestSubscription and TestClient -- for an 39 // example of how to use this. 40 // 41 // These objects are not thread safe. These operations MUST be done on the same 42 // thread: 43 // - |insert| / |emplace| new subscriptions. 44 // - Destroy a returned SubscriptionToken. 45 // - Destroy the SubscriberList. 46 namespace internal { 47 // Helper class that stores a callable object and invokes it upon destruction 48 // (unless invalidate() has been called). 49 class SubscriptionTokenImpl { 50 public: 51 using Callback = std::function<void()>; 52 SubscriptionTokenImpl(const Callback & cb)53 explicit SubscriptionTokenImpl(const Callback& cb) : mCb(cb) {} 54 ~SubscriptionTokenImpl()55 ~SubscriptionTokenImpl() { 56 if (mCb) 57 mCb(); 58 } 59 60 // If this method is called, the stored callable object 61 // will not be invoked upon destruction. invalidate()62 void invalidate() { mCb = Callback(); } 63 64 private: 65 Callback mCb; 66 DISALLOW_COPY_ASSIGN_AND_MOVE(SubscriptionTokenImpl); 67 }; 68 } // namespace internal 69 70 using SubscriptionToken = std::unique_ptr<internal::SubscriptionTokenImpl>; 71 72 template <class SubscriberInfo> 73 class SubscriberList { 74 private: 75 // DO NOT CHANGE TO std::vector (or any other type of container 76 // that invalidates iterators on removal)! 77 // A list of objects that our clients care about. 78 using SubscriberInfoList = std::list<SubscriberInfo>; 79 using SubscriberInfoIterator = typename SubscriberInfoList::iterator; 80 // A list for internal bookkeeping. 81 typedef struct { 82 internal::SubscriptionTokenImpl* token; 83 SubscriberInfoIterator infoIterator; 84 } MetaData; 85 using MetaDataList = std::list<MetaData>; 86 using MetaDataListIterator = typename MetaDataList::iterator; 87 88 public: 89 using const_iterator = typename SubscriberInfoList::const_iterator; 90 91 SubscriberList() = default; 92 93 template <class... Args> emplace(Args &&...args)94 SubscriptionToken emplace(Args&&... args) { 95 mSubscriberInfos.emplace_back(std::forward<Args>(args)...); 96 return insertInternal(std::prev(mSubscriberInfos.end())); 97 } 98 insert(SubscriberInfo && info)99 SubscriptionToken insert(SubscriberInfo&& info) { 100 mSubscriberInfos.push_back(std::move(info)); 101 return insertInternal(std::prev(mSubscriberInfos.end())); 102 } 103 insert(const SubscriberInfo & info)104 SubscriptionToken insert(const SubscriberInfo& info) { 105 mSubscriberInfos.push_back(info); 106 return insertInternal(std::prev(mSubscriberInfos.end())); 107 } 108 109 // Your gateway to the subscribers. 110 // These iterators can be invalidated as subscribers are destroyed. Do not 111 // capture the iterators for asynchronous use. begin()112 const_iterator begin() const { return mSubscriberInfos.begin(); } end()113 const_iterator end() const { return mSubscriberInfos.end(); } size()114 size_t size() const { return mSubscriberInfos.size(); } 115 ~SubscriberList()116 ~SubscriberList() { 117 // Invalidate all the tokens dispensed by this instance. Failing to do 118 // that would cause the tokens to try and call into this instance upon 119 // destruction, resulting in undefined behavior. 120 for (const auto& subscriber : mMetaData) { 121 // No way the token can be NULL. 122 assert(subscriber.token); 123 subscriber.token->invalidate(); 124 } 125 } 126 127 private: unsubscribe(MetaDataListIterator iterator)128 void unsubscribe(MetaDataListIterator iterator) { 129 mSubscriberInfos.erase(iterator->infoIterator); 130 mMetaData.erase(iterator); 131 } 132 insertInternal(SubscriberInfoIterator infoIterator)133 SubscriptionToken insertInternal(SubscriberInfoIterator infoIterator) { 134 // Add an entry with information needed for subscriber book-keeping. 135 mMetaData.emplace_back(); 136 auto datum = std::prev(mMetaData.end()); 137 datum->infoIterator = infoIterator; 138 139 // Create a new token that calls |unsubscribe| upon destruction. 140 SubscriptionToken token(new internal::SubscriptionTokenImpl( 141 [this, datum] { this->unsubscribe(datum); })); 142 // Ensure that this instance can invalidate the token. 143 datum->token = token.get(); 144 145 // Give the token to the subscriber. 146 // The token will remain alive for as long as the subscriber that is 147 // holding on to it is alive. 148 // If the token is destroyed before the SubscriptionList is destroyed, 149 // it will notify us, and the corresponding subscriber entry will be 150 // removed. 151 // When the SubscriptionList is destroyed, it will go through all the 152 // remaining subscriber entries and invalidate their tokens, so that 153 // they don't try notifying the "dead" SubscriptionList. 154 return token; 155 } 156 157 SubscriberInfoList mSubscriberInfos; 158 MetaDataList mMetaData; 159 160 DISALLOW_COPY_ASSIGN_AND_MOVE(SubscriberList); 161 }; 162 163 } // namespace base 164 } // namespace android 165