1 // Copyright 2019 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_ASSOCIATED_REMOTE_H_
6 #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_REMOTE_H_
7 
8 #include <cstdint>
9 #include <utility>
10 
11 #include "base/callback_forward.h"
12 #include "base/check.h"
13 #include "base/compiler_specific.h"
14 #include "base/macros.h"
15 #include "base/memory/scoped_refptr.h"
16 #include "base/sequenced_task_runner.h"
17 #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
18 #include "mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h"
19 #include "mojo/public/cpp/bindings/lib/multiplex_router.h"
20 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
21 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
22 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
23 #include "mojo/public/cpp/system/message_pipe.h"
24 
25 namespace mojo {
26 
27 // An AssociatedRemote is similar to a Remote (see remote.h) in that it is used
28 // to transmit mojom interface method calls to a remote (Associated) Receiver.
29 //
30 // Unlike Remote, an entangled AssociatedRemote/AssociatedReceiver pair cannot
31 // operate on its own and requires a concrete Remote/Receiver pair upon which
32 // to piggyback.
33 template <typename Interface>
34 class AssociatedRemote {
35  public:
36   using InterfaceType = Interface;
37   using PendingType = PendingAssociatedRemote<Interface>;
38   using Proxy = typename Interface::Proxy_;
39 
40   // Constructs an unbound AssociatedRemote. This object cannot issue Interface
41   // method calls and does not schedule any tasks.
42   AssociatedRemote() = default;
AssociatedRemote(AssociatedRemote && other)43   AssociatedRemote(AssociatedRemote&& other) noexcept {
44     *this = std::move(other);
45   }
46 
47   // Constructs a new AssociatedRemote which is bound from |pending_remote| and
48   // which schedules response callbacks and disconnection notifications on the
49   // default SequencedTaskRunner (i.e., base::SequencedTaskRunnerHandle::Get()
50   // at construction time).
AssociatedRemote(PendingAssociatedRemote<Interface> pending_remote)51   explicit AssociatedRemote(PendingAssociatedRemote<Interface> pending_remote)
52       : AssociatedRemote(std::move(pending_remote), nullptr) {}
53 
54   // Constructs a new AssociatedRemote which is bound from |pending_remote| and
55   // which schedules response callbacks and disconnection notifications on
56   // |task_runner|. |task_runner| must run tasks on the same sequence that owns
57   // this AssociatedRemote.
AssociatedRemote(PendingAssociatedRemote<Interface> pending_remote,scoped_refptr<base::SequencedTaskRunner> task_runner)58   AssociatedRemote(PendingAssociatedRemote<Interface> pending_remote,
59                    scoped_refptr<base::SequencedTaskRunner> task_runner) {
60     Bind(std::move(pending_remote), std::move(task_runner));
61   }
62 
63   ~AssociatedRemote() = default;
64 
65   AssociatedRemote& operator=(AssociatedRemote&& other) noexcept {
66     internal_state_.Swap(&other.internal_state_);
67     return *this;
68   }
69 
70   // Exposes access to callable Interface methods directed at this
71   // AssociatedRemote's receiver. Must only be called on a bound
72   // AssociatedRemote.
get()73   typename Interface::Proxy_* get() const {
74     DCHECK(is_bound())
75         << "Cannot issue Interface method calls on an unbound AssociatedRemote";
76     return internal_state_.instance();
77   }
78 
79   // Shorthand form of |get()|. See above.
80   typename Interface::Proxy_* operator->() const { return get(); }
81   typename Interface::Proxy_& operator*() const { return *get(); }
82 
83   // Indicates whether this AssociatedRemote is bound and thus can issue
84   // Interface method calls via the above accessors.
85   //
86   // NOTE: The state of being "bound" should not be confused with the state of
87   // being "connected" (see |is_connected()| below). An AssociatedRemote is
88   // NEVER passively unbound and the only way for it to become unbound is to
89   // explicitly call |reset()| or |Unbind()|. As such, unless you make explicit
90   // calls to those methods, it is always safe to assume that a AssociatedRemote
91   // you've bound will remain bound and callable.
is_bound()92   bool is_bound() const { return internal_state_.is_bound(); }
93   explicit operator bool() const { return is_bound(); }
94 
95   // Indicates whether this AssociatedRemote is connected to a receiver. Must
96   // only be called on a bound AssociatedRemote. If this returns |true|, method
97   // calls made by this AssociatedRemote may eventually end up at the connected
98   // receiver (though it's of course possible for this call to race with
99   // disconnection). If this returns |false| however, all future Interface
100   // method calls on this AssociatedRemote will be silently dropped.
101   //
102   // A bound AssociatedRemote becomes disconnected automatically either when its
103   // receiver is destroyed, or when it receives a malformed or otherwise
104   // unexpected response message from the receiver.
105   //
106   // NOTE: The state of being "bound" should not be confused with the state of
107   // being "connected". See |is_bound()| above.
is_connected()108   bool is_connected() const {
109     DCHECK(is_bound());
110     return !internal_state_.encountered_error();
111   }
112 
113   // Sets a Closure to be invoked if this AssociatedRemote is cut off from its
114   // receiver. This can happen if the corresponding AssociatedReceiver (or
115   // unconsumed PendingAssociatedReceiver) is destroyed, or if the
116   // AssociatedReceiver sends a malformed or otherwise unexpected response
117   // message to this AssociatedRemote. Must only be called
118   // on a bound AssociatedRemote object, and only remains set as long as the
119   // AssociatedRemote is both bound and connected.
120   //
121   // If invoked at all, |handler| will be scheduled asynchronously using the
122   // AssociatedRemote's bound SequencedTaskRunner.
set_disconnect_handler(base::OnceClosure handler)123   void set_disconnect_handler(base::OnceClosure handler) {
124     if (is_connected())
125       internal_state_.set_connection_error_handler(std::move(handler));
126   }
127 
128   // Similar to above but the handler receives additional metadata if provided
129   // by the receiving endpoint when closing itself.
set_disconnect_with_reason_handler(ConnectionErrorWithReasonCallback handler)130   void set_disconnect_with_reason_handler(
131       ConnectionErrorWithReasonCallback handler) {
132     internal_state_.set_connection_error_with_reason_handler(
133         std::move(handler));
134   }
135 
136   // Resets this AssociatedRemote to an unbound state. To reset the
137   // AssociatedRemote and recover an PendingAssociatedRemote that can be bound
138   // again later, use |Unbind()| instead.
reset()139   void reset() {
140     State doomed_state;
141     internal_state_.Swap(&doomed_state);
142   }
143 
144   // Similar to the method above, but also specifies a disconnect reason.
ResetWithReason(uint32_t custom_reason,const std::string & description)145   void ResetWithReason(uint32_t custom_reason, const std::string& description) {
146     if (internal_state_.is_bound())
147       internal_state_.CloseWithReason(custom_reason, description);
148     reset();
149   }
150 
151   // Binds this AssociatedRemote, connecting it to a new
152   // PendingAssociatedReceiver which is returned for transmission to some
153   // AssociatedReceiver which can bind it. The AssociatedRemote will schedule
154   // any response callbacks or disconnection notifications on the default
155   // SequencedTaskRunner (i.e. base::SequencedTaskRunnerHandle::Get() at the
156   // time of this call). Must only be called on an unbound AssociatedRemote.
BindNewEndpointAndPassReceiver()157   PendingAssociatedReceiver<Interface> BindNewEndpointAndPassReceiver()
158       WARN_UNUSED_RESULT {
159     return BindNewEndpointAndPassReceiver(nullptr);
160   }
161 
162   // Like above, but the AssociatedRemote will schedule response callbacks and
163   // disconnection notifications on |task_runner| instead of the default
164   // SequencedTaskRunner. |task_runner| must run tasks on the same sequence that
165   // owns this AssociatedRemote.
BindNewEndpointAndPassReceiver(scoped_refptr<base::SequencedTaskRunner> task_runner)166   PendingAssociatedReceiver<Interface> BindNewEndpointAndPassReceiver(
167       scoped_refptr<base::SequencedTaskRunner> task_runner) WARN_UNUSED_RESULT {
168     ScopedInterfaceEndpointHandle remote_handle;
169     ScopedInterfaceEndpointHandle receiver_handle;
170     ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(
171         &remote_handle, &receiver_handle);
172     Bind(PendingAssociatedRemote<Interface>(std::move(remote_handle), 0),
173          std::move(task_runner));
174     return PendingAssociatedReceiver<Interface>(std::move(receiver_handle));
175   }
176 
177   // Like BindNewEndpointAndPassReceiver() above, but it creates a dedicated
178   // message pipe. The returned receiver can be bound directly to an
179   // implementation, without being first passed through a message pipe endpoint.
180   //
181   // For testing, where the returned request is bound to e.g. a mock and there
182   // are no other interfaces involved.
BindNewEndpointAndPassDedicatedReceiver()183   PendingAssociatedReceiver<Interface> BindNewEndpointAndPassDedicatedReceiver()
184       WARN_UNUSED_RESULT {
185     MessagePipe pipe;
186     scoped_refptr<internal::MultiplexRouter> router0 =
187         new internal::MultiplexRouter(
188             std::move(pipe.handle0), internal::MultiplexRouter::MULTI_INTERFACE,
189             false, base::SequencedTaskRunnerHandle::Get());
190     scoped_refptr<internal::MultiplexRouter> router1 =
191         new internal::MultiplexRouter(
192             std::move(pipe.handle1), internal::MultiplexRouter::MULTI_INTERFACE,
193             true, base::SequencedTaskRunnerHandle::Get());
194 
195     ScopedInterfaceEndpointHandle remote_handle;
196     ScopedInterfaceEndpointHandle receiver_handle;
197     ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(
198         &remote_handle, &receiver_handle);
199     InterfaceId id = router1->AssociateInterface(std::move(remote_handle));
200     remote_handle = router0->CreateLocalEndpointHandle(id);
201 
202     Bind(PendingAssociatedRemote<Interface>(std::move(remote_handle), 0),
203          nullptr);
204     return PendingAssociatedReceiver<Interface>(std::move(receiver_handle));
205   }
206 
207   // Binds this AssociatedRemote by consuming |pending_remote|, which must be
208   // valid. The AssociatedRemote will schedule any response callbacks or
209   // disconnection notifications on the default SequencedTaskRunner (i.e.
210   // base::SequencedTaskRunnerHandle::Get() at the time of this call). Must only
211   // be called on an unbound AssociatedRemote.
Bind(PendingAssociatedRemote<Interface> pending_remote)212   void Bind(PendingAssociatedRemote<Interface> pending_remote) {
213     DCHECK(pending_remote.is_valid());
214     Bind(std::move(pending_remote), nullptr);
215   }
216 
217   // Like above, but the AssociatedRemote will schedule response callbacks and
218   // disconnection notifications on |task_runner| instead of the default
219   // SequencedTaskRunner. Must only be called on an unbound AssociatedRemote.
220   // |task_runner| must run tasks on the same sequence that owns this
221   // AssociatedRemote.
Bind(PendingAssociatedRemote<Interface> pending_remote,scoped_refptr<base::SequencedTaskRunner> task_runner)222   void Bind(PendingAssociatedRemote<Interface> pending_remote,
223             scoped_refptr<base::SequencedTaskRunner> task_runner) {
224     DCHECK(!is_bound()) << "AssociatedRemote is already bound";
225     if (!pending_remote) {
226       reset();
227       return;
228     }
229 
230     internal_state_.Bind(
231         AssociatedInterfacePtrInfo<Interface>(pending_remote.PassHandle(),
232                                               pending_remote.version()),
233         std::move(task_runner));
234 
235     // Force the internal state to configure its proxy. Unlike InterfacePtr we
236     // do not use AssociatedRemote in transit, so binding to a pipe handle can
237     // also imply binding to a SequencedTaskRunner and observing pipe handle
238     // state.
239     ignore_result(internal_state_.instance());
240   }
241 
242   // Unbinds this AssociatedRemote, rendering it unable to issue further
243   // Interface method calls. Returns a PendingAssociatedRemote which may be
244   // passed across threads or processes and consumed by another AssociatedRemote
245   // elsewhere.
246   //
247   // Note that it is an error (the bad, crashy kind of error) to attempt to
248   // |Unbind()| a AssociatedRemote which is awaiting one or more responses to
249   // previously issued Interface method calls. Calling this method should only
250   // be considered in cases where satisfaction of that constraint can be proven.
251   //
252   // Must only be called on a bound AssociatedRemote.
Unbind()253   PendingAssociatedRemote<Interface> Unbind() WARN_UNUSED_RESULT {
254     DCHECK(is_bound());
255     CHECK(!internal_state_.has_pending_callbacks());
256     State state;
257     internal_state_.Swap(&state);
258     AssociatedInterfacePtrInfo<Interface> info = state.PassInterface();
259     return PendingAssociatedRemote<Interface>(info.PassHandle(),
260                                               info.version());
261   }
262 
263   // Sends a message on the underlying message pipe and runs the current
264   // message loop until its response is received. This can be used in tests to
265   // verify that no message was sent on a message pipe in response to some
266   // stimulus.
FlushForTesting()267   void FlushForTesting() { internal_state_.FlushForTesting(); }
268 
internal_state()269   internal::AssociatedInterfacePtrState<Interface>* internal_state() {
270     return &internal_state_;
271   }
272 
273  private:
274   using State = internal::AssociatedInterfacePtrState<Interface>;
275   mutable State internal_state_;
276 
277   DISALLOW_COPY_AND_ASSIGN(AssociatedRemote);
278 };
279 
280 }  // namespace mojo
281 
282 #endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_REMOTE_H_
283