1 // Copyright 2015 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_INTERFACE_PTR_H_ 6 #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_ 7 8 #include <stdint.h> 9 10 #include <string> 11 #include <utility> 12 13 #include "base/callback.h" 14 #include "base/logging.h" 15 #include "base/macros.h" 16 #include "base/memory/ref_counted.h" 17 #include "base/sequenced_task_runner.h" 18 #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" 19 #include "mojo/public/cpp/bindings/associated_interface_request.h" 20 #include "mojo/public/cpp/bindings/bindings_export.h" 21 #include "mojo/public/cpp/bindings/connection_error_callback.h" 22 #include "mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h" 23 #include "mojo/public/cpp/bindings/lib/multiplex_router.h" 24 #include "mojo/public/cpp/system/message_pipe.h" 25 26 namespace mojo { 27 28 // Represents the client side of an associated interface. It is similar to 29 // InterfacePtr, except that it doesn't own a message pipe handle. 30 template <typename Interface> 31 class AssociatedInterfacePtr { 32 public: 33 using InterfaceType = Interface; 34 using PtrInfoType = AssociatedInterfacePtrInfo<Interface>; 35 using Proxy = typename Interface::Proxy_; 36 37 // Constructs an unbound AssociatedInterfacePtr. 38 AssociatedInterfacePtr() {} 39 AssociatedInterfacePtr(decltype(nullptr)) {} 40 41 AssociatedInterfacePtr(AssociatedInterfacePtr&& other) { 42 internal_state_.Swap(&other.internal_state_); 43 } 44 45 explicit AssociatedInterfacePtr(PtrInfoType&& info) { Bind(std::move(info)); } 46 47 AssociatedInterfacePtr& operator=(AssociatedInterfacePtr&& other) { 48 reset(); 49 internal_state_.Swap(&other.internal_state_); 50 return *this; 51 } 52 53 // Assigning nullptr to this class causes it to closes the associated 54 // interface (if any) and returns the pointer to the unbound state. 55 AssociatedInterfacePtr& operator=(decltype(nullptr)) { 56 reset(); 57 return *this; 58 } 59 60 ~AssociatedInterfacePtr() {} 61 62 // Sets up this object as the client side of an associated interface. 63 // Calling with an invalid |info| has the same effect as reset(). In this 64 // case, the AssociatedInterfacePtr is not considered as bound. 65 // 66 // Optionally, |runner| is a SequencedTaskRunner bound to the current sequence 67 // on which all callbacks and connection error notifications will be 68 // dispatched. It is only useful to specify this to use a different 69 // SequencedTaskRunner than SequencedTaskRunnerHandle::Get(). 70 // 71 // NOTE: The corresponding AssociatedInterfaceRequest must be sent over 72 // another interface before using this object to make calls. Please see the 73 // comments of MakeRequest(AssociatedInterfacePtr<Interface>*) for more 74 // details. 75 void Bind(AssociatedInterfacePtrInfo<Interface> info, 76 scoped_refptr<base::SequencedTaskRunner> runner = nullptr) { 77 reset(); 78 79 if (info.is_valid()) 80 internal_state_.Bind(std::move(info), std::move(runner)); 81 } 82 83 bool is_bound() const { return internal_state_.is_bound(); } 84 85 Proxy* get() const { return internal_state_.instance(); } 86 87 // Functions like a pointer to Interface. Must already be bound. 88 Proxy* operator->() const { return get(); } 89 Proxy& operator*() const { return *get(); } 90 91 // Returns the version number of the interface that the remote side supports. 92 uint32_t version() const { return internal_state_.version(); } 93 94 // Queries the max version that the remote side supports. On completion, the 95 // result will be returned as the input of |callback|. The version number of 96 // this object will also be updated. 97 void QueryVersion(const base::Callback<void(uint32_t)>& callback) { 98 internal_state_.QueryVersion(callback); 99 } 100 101 // If the remote side doesn't support the specified version, it will close the 102 // associated interface asynchronously. This does nothing if it's already 103 // known that the remote side supports the specified version, i.e., if 104 // |version <= this->version()|. 105 // 106 // After calling RequireVersion() with a version not supported by the remote 107 // side, all subsequent calls to interface methods will be ignored. 108 void RequireVersion(uint32_t version) { 109 internal_state_.RequireVersion(version); 110 } 111 112 // Sends a message on the underlying message pipe and runs the current 113 // message loop until its response is received. This can be used in tests to 114 // verify that no message was sent on a message pipe in response to some 115 // stimulus. 116 void FlushForTesting() { internal_state_.FlushForTesting(); } 117 118 // Closes the associated interface (if any) and returns the pointer to the 119 // unbound state. 120 void reset() { 121 State doomed; 122 internal_state_.Swap(&doomed); 123 } 124 125 // Similar to the method above, but also specifies a disconnect reason. 126 void ResetWithReason(uint32_t custom_reason, const std::string& description) { 127 if (internal_state_.is_bound()) 128 internal_state_.CloseWithReason(custom_reason, description); 129 reset(); 130 } 131 132 // Indicates whether an error has been encountered. If true, method calls made 133 // on this interface will be dropped (and may already have been dropped). 134 bool encountered_error() const { return internal_state_.encountered_error(); } 135 136 // Registers a handler to receive error notifications. 137 // 138 // This method may only be called after the AssociatedInterfacePtr has been 139 // bound. 140 void set_connection_error_handler(base::OnceClosure error_handler) { 141 internal_state_.set_connection_error_handler(std::move(error_handler)); 142 } 143 144 void set_connection_error_with_reason_handler( 145 ConnectionErrorWithReasonCallback error_handler) { 146 internal_state_.set_connection_error_with_reason_handler( 147 std::move(error_handler)); 148 } 149 150 // Unbinds and returns the associated interface pointer information which 151 // could be used to setup an AssociatedInterfacePtr again. This method may be 152 // used to move the proxy to a different sequence. 153 // 154 // It is an error to call PassInterface() while there are pending responses. 155 // TODO: fix this restriction, it's not always obvious when there is a 156 // pending response. 157 AssociatedInterfacePtrInfo<Interface> PassInterface() { 158 DCHECK(!internal_state_.has_pending_callbacks()); 159 State state; 160 internal_state_.Swap(&state); 161 162 return state.PassInterface(); 163 } 164 165 // DO NOT USE. Exposed only for internal use and for testing. 166 internal::AssociatedInterfacePtrState<Interface>* internal_state() { 167 return &internal_state_; 168 } 169 170 // Allow AssociatedInterfacePtr<> to be used in boolean expressions. 171 explicit operator bool() const { return internal_state_.is_bound(); } 172 173 private: 174 typedef internal::AssociatedInterfacePtrState<Interface> State; 175 mutable State internal_state_; 176 177 DISALLOW_COPY_AND_ASSIGN(AssociatedInterfacePtr); 178 }; 179 180 // Creates an associated interface. The returned request is supposed to be sent 181 // over another interface (either associated or non-associated). 182 // 183 // NOTE: |ptr| must NOT be used to make calls before the request is sent. 184 // Violating that will lead to crash. On the other hand, as soon as the request 185 // is sent, |ptr| is usable. There is no need to wait until the request is bound 186 // to an implementation at the remote side. 187 template <typename Interface> 188 AssociatedInterfaceRequest<Interface> MakeRequest( 189 AssociatedInterfacePtr<Interface>* ptr, 190 scoped_refptr<base::SequencedTaskRunner> runner = nullptr) { 191 AssociatedInterfacePtrInfo<Interface> ptr_info; 192 auto request = MakeRequest(&ptr_info); 193 ptr->Bind(std::move(ptr_info), std::move(runner)); 194 return request; 195 } 196 197 // Creates an associated interface. One of the two endpoints is supposed to be 198 // sent over another interface (either associated or non-associated); while the 199 // other is used locally. 200 // 201 // NOTE: If |ptr_info| is used locally and bound to an AssociatedInterfacePtr, 202 // the interface pointer must NOT be used to make calls before the request is 203 // sent. Please see NOTE of the previous function for more details. 204 template <typename Interface> 205 AssociatedInterfaceRequest<Interface> MakeRequest( 206 AssociatedInterfacePtrInfo<Interface>* ptr_info) { 207 ScopedInterfaceEndpointHandle handle0; 208 ScopedInterfaceEndpointHandle handle1; 209 ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&handle0, 210 &handle1); 211 212 ptr_info->set_handle(std::move(handle0)); 213 ptr_info->set_version(0); 214 215 return AssociatedInterfaceRequest<Interface>(std::move(handle1)); 216 } 217 218 // Like MakeRequest() above, but it creates a dedicated message pipe. The 219 // returned request can be bound directly to an implementation, without being 220 // first passed through a message pipe endpoint. 221 // 222 // This function has two main uses: 223 // 224 // * In testing, where the returned request is bound to e.g. a mock and there 225 // are no other interfaces involved. 226 // 227 // * When discarding messages sent on an interface, which can be done by 228 // discarding the returned request. 229 template <typename Interface> 230 AssociatedInterfaceRequest<Interface> MakeRequestAssociatedWithDedicatedPipe( 231 AssociatedInterfacePtr<Interface>* ptr) { 232 MessagePipe pipe; 233 scoped_refptr<internal::MultiplexRouter> router0 = 234 new internal::MultiplexRouter( 235 std::move(pipe.handle0), internal::MultiplexRouter::MULTI_INTERFACE, 236 false, base::SequencedTaskRunnerHandle::Get()); 237 scoped_refptr<internal::MultiplexRouter> router1 = 238 new internal::MultiplexRouter( 239 std::move(pipe.handle1), internal::MultiplexRouter::MULTI_INTERFACE, 240 true, base::SequencedTaskRunnerHandle::Get()); 241 242 ScopedInterfaceEndpointHandle endpoint0, endpoint1; 243 ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&endpoint0, 244 &endpoint1); 245 InterfaceId id = router1->AssociateInterface(std::move(endpoint0)); 246 endpoint0 = router0->CreateLocalEndpointHandle(id); 247 248 ptr->Bind(AssociatedInterfacePtrInfo<Interface>(std::move(endpoint0), 249 Interface::Version_)); 250 return AssociatedInterfaceRequest<Interface>(std::move(endpoint1)); 251 } 252 253 // |handle| is supposed to be the request of an associated interface. This 254 // method associates the interface with a dedicated, disconnected message pipe. 255 // That way, the corresponding associated interface pointer of |handle| can 256 // safely make calls (although those calls are silently dropped). 257 MOJO_CPP_BINDINGS_EXPORT void AssociateWithDisconnectedPipe( 258 ScopedInterfaceEndpointHandle handle); 259 260 } // namespace mojo 261 262 #endif // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_ 263