1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // signal_utils:
7 //   Helper classes for tracking dependent state changes between objects.
8 //   These changes are signaled to the dependent class via channels.
9 //   See design document:
10 //   https://docs.google.com/document/d/15Edfotqg6_l1skTEL8ADQudF_oIdNa7i8Po43k6jMd4/
11 
12 #ifndef LIBANGLE_SIGNAL_UTILS_H_
13 #define LIBANGLE_SIGNAL_UTILS_H_
14 
15 #include <set>
16 
17 #include "common/angleutils.h"
18 #include "common/debug.h"
19 
20 namespace angle
21 {
22 
23 // Interface that the depending class inherits from.
24 template <typename ChannelID = uint32_t, typename... MessageT>
25 class SignalReceiver
26 {
27   public:
28     virtual ~SignalReceiver() = default;
29     virtual void signal(ChannelID channelID, MessageT... message) = 0;
30 };
31 
32 template <typename ChannelID, typename... MessageT>
33 class ChannelBinding;
34 
35 // The host class owns the channel. It uses the channel to fire signals to the receiver.
36 template <typename ChannelID = uint32_t, typename... MessageT>
37 class BroadcastChannel final : NonCopyable
38 {
39   public:
40     BroadcastChannel();
41     ~BroadcastChannel();
42 
43     void signal(MessageT... message) const;
44 
45     void reset();
46 
47     bool empty() const;
48 
49   private:
50     // Only the ChannelBinding class should add or remove receivers.
51     friend class ChannelBinding<ChannelID, MessageT...>;
52     void addReceiver(ChannelBinding<ChannelID, MessageT...> *receiver);
53     void removeReceiver(ChannelBinding<ChannelID, MessageT...> *receiver);
54 
55     std::vector<ChannelBinding<ChannelID, MessageT...> *> mReceivers;
56 };
57 
58 template <typename ChannelID, typename... MessageT>
BroadcastChannel()59 BroadcastChannel<ChannelID, MessageT...>::BroadcastChannel()
60 {
61 }
62 
63 template <typename ChannelID, typename... MessageT>
~BroadcastChannel()64 BroadcastChannel<ChannelID, MessageT...>::~BroadcastChannel()
65 {
66     reset();
67 }
68 
69 template <typename ChannelID, typename... MessageT>
addReceiver(ChannelBinding<ChannelID,MessageT...> * receiver)70 void BroadcastChannel<ChannelID, MessageT...>::addReceiver(
71     ChannelBinding<ChannelID, MessageT...> *receiver)
72 {
73     ASSERT(std::find(mReceivers.begin(), mReceivers.end(), receiver) == mReceivers.end());
74     mReceivers.push_back(receiver);
75 }
76 
77 template <typename ChannelID, typename... MessageT>
removeReceiver(ChannelBinding<ChannelID,MessageT...> * receiver)78 void BroadcastChannel<ChannelID, MessageT...>::removeReceiver(
79     ChannelBinding<ChannelID, MessageT...> *receiver)
80 {
81     auto iter = std::find(mReceivers.begin(), mReceivers.end(), receiver);
82     ASSERT(iter != mReceivers.end());
83     mReceivers.erase(iter);
84 }
85 
86 template <typename ChannelID, typename... MessageT>
signal(MessageT...message)87 void BroadcastChannel<ChannelID, MessageT...>::signal(MessageT... message) const
88 {
89     if (mReceivers.empty())
90         return;
91 
92     for (const auto *receiver : mReceivers)
93     {
94         receiver->signal(message...);
95     }
96 }
97 
98 template <typename ChannelID, typename... MessageT>
reset()99 void BroadcastChannel<ChannelID, MessageT...>::reset()
100 {
101     for (auto receiver : mReceivers)
102     {
103         receiver->onChannelClosed();
104     }
105     mReceivers.clear();
106 }
107 
108 template <typename ChannelID, typename... MessageT>
empty()109 bool BroadcastChannel<ChannelID, MessageT...>::empty() const
110 {
111     return mReceivers.empty();
112 }
113 
114 // The dependent class keeps bindings to the host's BroadcastChannel.
115 template <typename ChannelID = uint32_t, typename... MessageT>
116 class ChannelBinding final
117 {
118   public:
119     ChannelBinding(SignalReceiver<ChannelID, MessageT...> *receiver, ChannelID channelID);
120     ~ChannelBinding();
121     ChannelBinding(const ChannelBinding &other) = default;
122     ChannelBinding &operator=(const ChannelBinding &other) = default;
123 
124     void bind(BroadcastChannel<ChannelID, MessageT...> *channel);
125     void reset();
126     void signal(MessageT... message) const;
127     void onChannelClosed();
128 
129   private:
130     BroadcastChannel<ChannelID, MessageT...> *mChannel;
131     SignalReceiver<ChannelID, MessageT...> *mReceiver;
132     ChannelID mChannelID;
133 };
134 
135 template <typename ChannelID, typename... MessageT>
ChannelBinding(SignalReceiver<ChannelID,MessageT...> * receiver,ChannelID channelID)136 ChannelBinding<ChannelID, MessageT...>::ChannelBinding(
137     SignalReceiver<ChannelID, MessageT...> *receiver,
138     ChannelID channelID)
139     : mChannel(nullptr), mReceiver(receiver), mChannelID(channelID)
140 {
141     ASSERT(receiver);
142 }
143 
144 template <typename ChannelID, typename... MessageT>
~ChannelBinding()145 ChannelBinding<ChannelID, MessageT...>::~ChannelBinding()
146 {
147     reset();
148 }
149 
150 template <typename ChannelID, typename... MessageT>
bind(BroadcastChannel<ChannelID,MessageT...> * channel)151 void ChannelBinding<ChannelID, MessageT...>::bind(BroadcastChannel<ChannelID, MessageT...> *channel)
152 {
153     ASSERT(mReceiver);
154     if (mChannel)
155     {
156         mChannel->removeReceiver(this);
157     }
158 
159     mChannel = channel;
160 
161     if (mChannel)
162     {
163         mChannel->addReceiver(this);
164     }
165 }
166 
167 template <typename ChannelID, typename... MessageT>
reset()168 void ChannelBinding<ChannelID, MessageT...>::reset()
169 {
170     bind(nullptr);
171 }
172 
173 template <typename ChannelID, typename... MessageT>
signal(MessageT...message)174 void ChannelBinding<ChannelID, MessageT...>::signal(MessageT... message) const
175 {
176     mReceiver->signal(mChannelID, message...);
177 }
178 
179 template <typename ChannelID, typename... MessageT>
onChannelClosed()180 void ChannelBinding<ChannelID, MessageT...>::onChannelClosed()
181 {
182     mChannel = nullptr;
183 }
184 
185 }  // namespace angle
186 
187 #endif  // LIBANGLE_SIGNAL_UTILS_H_
188