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