• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
6 #define MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
7 
8 #include <string>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/macros.h"
14 #include "base/memory/ptr_util.h"
15 #include "mojo/public/cpp/bindings/binding.h"
16 #include "mojo/public/cpp/bindings/connection_error_callback.h"
17 #include "mojo/public/cpp/bindings/interface_ptr.h"
18 #include "mojo/public/cpp/bindings/interface_request.h"
19 #include "mojo/public/cpp/bindings/message.h"
20 
21 namespace mojo {
22 
23 template <typename BindingType>
24 struct BindingSetTraits;
25 
26 template <typename Interface, typename ImplRefTraits>
27 struct BindingSetTraits<Binding<Interface, ImplRefTraits>> {
28   using ProxyType = InterfacePtr<Interface>;
29   using RequestType = InterfaceRequest<Interface>;
30   using BindingType = Binding<Interface, ImplRefTraits>;
31   using ImplPointerType = typename BindingType::ImplPointerType;
32 
33   static RequestType MakeRequest(ProxyType* proxy) {
34     return mojo::MakeRequest(proxy);
35   }
36 };
37 
38 using BindingId = size_t;
39 
40 template <typename ContextType>
41 struct BindingSetContextTraits {
42   using Type = ContextType;
43 
44   static constexpr bool SupportsContext() { return true; }
45 };
46 
47 template <>
48 struct BindingSetContextTraits<void> {
49   // NOTE: This choice of Type only matters insofar as it affects the size of
50   // the |context_| field of a BindingSetBase::Entry with void context. The
51   // context value is never used in this case.
52   using Type = bool;
53 
54   static constexpr bool SupportsContext() { return false; }
55 };
56 
57 // Generic definition used for BindingSet and AssociatedBindingSet to own a
58 // collection of bindings which point to the same implementation.
59 //
60 // If |ContextType| is non-void, then every added binding must include a context
61 // value of that type, and |dispatch_context()| will return that value during
62 // the extent of any message dispatch targeting that specific binding.
63 template <typename Interface, typename BindingType, typename ContextType>
64 class BindingSetBase {
65  public:
66   using ContextTraits = BindingSetContextTraits<ContextType>;
67   using Context = typename ContextTraits::Type;
68   using PreDispatchCallback = base::Callback<void(const Context&)>;
69   using Traits = BindingSetTraits<BindingType>;
70   using ProxyType = typename Traits::ProxyType;
71   using RequestType = typename Traits::RequestType;
72   using ImplPointerType = typename Traits::ImplPointerType;
73 
74   BindingSetBase() {}
75 
76   void set_connection_error_handler(const base::Closure& error_handler) {
77     error_handler_ = error_handler;
78     error_with_reason_handler_.Reset();
79   }
80 
81   void set_connection_error_with_reason_handler(
82       const ConnectionErrorWithReasonCallback& error_handler) {
83     error_with_reason_handler_ = error_handler;
84     error_handler_.Reset();
85   }
86 
87   // Sets a callback to be invoked immediately before dispatching any message or
88   // error received by any of the bindings in the set. This may only be used
89   // with a non-void |ContextType|.
90   void set_pre_dispatch_handler(const PreDispatchCallback& handler) {
91     static_assert(ContextTraits::SupportsContext(),
92                   "Pre-dispatch handler usage requires non-void context type.");
93     pre_dispatch_handler_ = handler;
94   }
95 
96   // Adds a new binding to the set which binds |request| to |impl| with no
97   // additional context.
98   BindingId AddBinding(ImplPointerType impl, RequestType request) {
99     static_assert(!ContextTraits::SupportsContext(),
100                   "Context value required for non-void context type.");
101     return AddBindingImpl(std::move(impl), std::move(request), false);
102   }
103 
104   // Adds a new binding associated with |context|.
105   BindingId AddBinding(ImplPointerType impl,
106                        RequestType request,
107                        Context context) {
108     static_assert(ContextTraits::SupportsContext(),
109                   "Context value unsupported for void context type.");
110     return AddBindingImpl(std::move(impl), std::move(request),
111                           std::move(context));
112   }
113 
114   // Removes a binding from the set. Note that this is safe to call even if the
115   // binding corresponding to |id| has already been removed.
116   //
117   // Returns |true| if the binding was removed and |false| if it didn't exist.
118   bool RemoveBinding(BindingId id) {
119     auto it = bindings_.find(id);
120     if (it == bindings_.end())
121       return false;
122     bindings_.erase(it);
123     return true;
124   }
125 
126   // Returns a proxy bound to one end of a pipe whose other end is bound to
127   // |this|. If |id_storage| is not null, |*id_storage| will be set to the ID
128   // of the added binding.
129   ProxyType CreateInterfacePtrAndBind(ImplPointerType impl,
130                                       BindingId* id_storage = nullptr) {
131     ProxyType proxy;
132     BindingId id = AddBinding(std::move(impl), Traits::MakeRequest(&proxy));
133     if (id_storage)
134       *id_storage = id;
135     return proxy;
136   }
137 
138   void CloseAllBindings() { bindings_.clear(); }
139 
140   bool empty() const { return bindings_.empty(); }
141 
142   // Implementations may call this when processing a dispatched message or
143   // error. During the extent of message or error dispatch, this will return the
144   // context associated with the specific binding which received the message or
145   // error. Use AddBinding() to associated a context with a specific binding.
146   const Context& dispatch_context() const {
147     static_assert(ContextTraits::SupportsContext(),
148                   "dispatch_context() requires non-void context type.");
149     DCHECK(dispatch_context_);
150     return *dispatch_context_;
151   }
152 
153   void FlushForTesting() {
154     for (auto& binding : bindings_)
155       binding.second->FlushForTesting();
156   }
157 
158  private:
159   friend class Entry;
160 
161   class Entry {
162    public:
163     Entry(ImplPointerType impl,
164           RequestType request,
165           BindingSetBase* binding_set,
166           BindingId binding_id,
167           Context context)
168         : binding_(std::move(impl), std::move(request)),
169           binding_set_(binding_set),
170           binding_id_(binding_id),
171           context_(std::move(context)) {
172       if (ContextTraits::SupportsContext())
173         binding_.AddFilter(base::MakeUnique<DispatchFilter>(this));
174       binding_.set_connection_error_with_reason_handler(
175           base::Bind(&Entry::OnConnectionError, base::Unretained(this)));
176     }
177 
178     void FlushForTesting() { binding_.FlushForTesting(); }
179 
180    private:
181     class DispatchFilter : public MessageReceiver {
182      public:
183       explicit DispatchFilter(Entry* entry) : entry_(entry) {}
184       ~DispatchFilter() override {}
185 
186      private:
187       // MessageReceiver:
188       bool Accept(Message* message) override {
189         entry_->WillDispatch();
190         return true;
191       }
192 
193       Entry* entry_;
194 
195       DISALLOW_COPY_AND_ASSIGN(DispatchFilter);
196     };
197 
198     void WillDispatch() {
199       DCHECK(ContextTraits::SupportsContext());
200       binding_set_->SetDispatchContext(&context_);
201     }
202 
203     void OnConnectionError(uint32_t custom_reason,
204                            const std::string& description) {
205       if (ContextTraits::SupportsContext())
206         WillDispatch();
207       binding_set_->OnConnectionError(binding_id_, custom_reason, description);
208     }
209 
210     BindingType binding_;
211     BindingSetBase* const binding_set_;
212     const BindingId binding_id_;
213     Context const context_;
214 
215     DISALLOW_COPY_AND_ASSIGN(Entry);
216   };
217 
218   void SetDispatchContext(const Context* context) {
219     DCHECK(ContextTraits::SupportsContext());
220     dispatch_context_ = context;
221     if (!pre_dispatch_handler_.is_null())
222       pre_dispatch_handler_.Run(*context);
223   }
224 
225   BindingId AddBindingImpl(ImplPointerType impl,
226                            RequestType request,
227                            Context context) {
228     BindingId id = next_binding_id_++;
229     DCHECK_GE(next_binding_id_, 0u);
230     auto entry = base::MakeUnique<Entry>(std::move(impl), std::move(request),
231                                          this, id, std::move(context));
232     bindings_.insert(std::make_pair(id, std::move(entry)));
233     return id;
234   }
235 
236   void OnConnectionError(BindingId id,
237                          uint32_t custom_reason,
238                          const std::string& description) {
239     auto it = bindings_.find(id);
240     DCHECK(it != bindings_.end());
241 
242     // We keep the Entry alive throughout error dispatch.
243     std::unique_ptr<Entry> entry = std::move(it->second);
244     bindings_.erase(it);
245 
246     if (!error_handler_.is_null())
247       error_handler_.Run();
248     else if (!error_with_reason_handler_.is_null())
249       error_with_reason_handler_.Run(custom_reason, description);
250   }
251 
252   base::Closure error_handler_;
253   ConnectionErrorWithReasonCallback error_with_reason_handler_;
254   PreDispatchCallback pre_dispatch_handler_;
255   BindingId next_binding_id_ = 0;
256   std::map<BindingId, std::unique_ptr<Entry>> bindings_;
257   const Context* dispatch_context_ = nullptr;
258 
259   DISALLOW_COPY_AND_ASSIGN(BindingSetBase);
260 };
261 
262 template <typename Interface, typename ContextType = void>
263 using BindingSet = BindingSetBase<Interface, Binding<Interface>, ContextType>;
264 
265 }  // namespace mojo
266 
267 #endif  // MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
268