• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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