| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_ |
| #define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_ |
| |
| #include <stdint.h> |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/callback_forward.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/sequenced_task_runner.h" |
| #include "mojo/public/cpp/bindings/connection_error_callback.h" |
| #include "mojo/public/cpp/bindings/interface_ptr_info.h" |
| #include "mojo/public/cpp/bindings/lib/interface_ptr_state.h" |
| |
| namespace mojo { |
| |
| // A pointer to a local proxy of a remote Interface implementation. Uses a |
| // message pipe to communicate with the remote implementation, and automatically |
| // closes the pipe and deletes the proxy on destruction. The pointer must be |
| // bound to a message pipe before the interface methods can be called. Once a |
| // pointer is destroyed, it is guaranteed that pending callbacks as well as the |
| // connection error handler (if registered) won't be called. |
| // |
| // This class is thread hostile, as is the local proxy it manages, while bound |
| // to a message pipe. All calls to this class or the proxy should be from the |
| // same sequence that bound it. If you need to move the proxy to a different |
| // sequence, extract the InterfacePtrInfo (containing just the message pipe and |
| // any version information) using PassInterface() on the original sequence, pass |
| // it to a different sequence, and create and bind a new InterfacePtr from that |
| // sequence. If an InterfacePtr is not bound to a message pipe, it may be bound |
| // or destroyed on any sequence. |
| template <typename Interface> |
| class InterfacePtr { |
| public: |
| using InterfaceType = Interface; |
| using PtrInfoType = InterfacePtrInfo<Interface>; |
| using Proxy = typename Interface::Proxy_; |
| |
| // Constructs an unbound InterfacePtr. |
| InterfacePtr() {} |
| InterfacePtr(decltype(nullptr)) {} |
| |
| // Takes over the binding of another InterfacePtr. |
| InterfacePtr(InterfacePtr&& other) noexcept { |
| internal_state_.Swap(&other.internal_state_); |
| } |
| |
| explicit InterfacePtr(PtrInfoType&& info) noexcept { Bind(std::move(info)); } |
| |
| // Takes over the binding of another InterfacePtr, and closes any message pipe |
| // already bound to this pointer. |
| InterfacePtr& operator=(InterfacePtr&& other) noexcept { |
| reset(); |
| internal_state_.Swap(&other.internal_state_); |
| return *this; |
| } |
| |
| // Assigning nullptr to this class causes it to close the currently bound |
| // message pipe (if any) and returns the pointer to the unbound state. |
| InterfacePtr& operator=(decltype(nullptr)) { |
| reset(); |
| return *this; |
| } |
| |
| // Closes the bound message pipe (if any) on destruction. |
| ~InterfacePtr() {} |
| |
| // Binds the InterfacePtr to a remote implementation of Interface. |
| // |
| // Calling with an invalid |info| (containing an invalid message pipe handle) |
| // has the same effect as reset(). In this case, the InterfacePtr is not |
| // considered as bound. |
| // |
| // Optionally, |runner| is a SequencedTaskRunner bound to the current sequence |
| // on which all callbacks and connection error notifications will be |
| // dispatched. It is only useful to specify this to use a different |
| // SequencedTaskRunner than SequencedTaskRunnerHandle::Get(). |
| void Bind(InterfacePtrInfo<Interface> info, |
| scoped_refptr<base::SequencedTaskRunner> runner = nullptr) { |
| reset(); |
| if (info.is_valid()) |
| internal_state_.Bind(std::move(info), std::move(runner)); |
| } |
| |
| // Returns whether or not this InterfacePtr is bound to a message pipe. |
| bool is_bound() const { return internal_state_.is_bound(); } |
| |
| // Returns a raw pointer to the local proxy. Caller does not take ownership. |
| // Note that the local proxy is thread hostile, as stated above. |
| Proxy* get() const { return internal_state_.instance(); } |
| |
| // Functions like a pointer to Interface. Must already be bound. |
| Proxy* operator->() const { return get(); } |
| Proxy& operator*() const { return *get(); } |
| |
| // Returns the version number of the interface that the remote side supports. |
| uint32_t version() const { return internal_state_.version(); } |
| |
| // Queries the max version that the remote side supports. On completion, the |
| // result will be returned as the input of |callback|. The version number of |
| // this interface pointer will also be updated. |
| void QueryVersion(const base::Callback<void(uint32_t)>& callback) { |
| internal_state_.QueryVersion(callback); |
| } |
| |
| // If the remote side doesn't support the specified version, it will close its |
| // end of the message pipe asynchronously. This does nothing if it's already |
| // known that the remote side supports the specified version, i.e., if |
| // |version <= this->version()|. |
| // |
| // After calling RequireVersion() with a version not supported by the remote |
| // side, all subsequent calls to interface methods will be ignored. |
| void RequireVersion(uint32_t version) { |
| internal_state_.RequireVersion(version); |
| } |
| |
| // Sends a no-op message on the underlying message pipe and runs the current |
| // message loop until its response is received. This can be used in tests to |
| // verify that no message was sent on a message pipe in response to some |
| // stimulus. |
| void FlushForTesting() { internal_state_.FlushForTesting(); } |
| |
| // Closes the bound message pipe, if any. |
| void reset() { |
| State doomed; |
| internal_state_.Swap(&doomed); |
| } |
| |
| // Similar to the method above, but also specifies a disconnect reason. |
| void ResetWithReason(uint32_t custom_reason, const std::string& description) { |
| if (internal_state_.is_bound()) |
| internal_state_.CloseWithReason(custom_reason, description); |
| reset(); |
| } |
| |
| // Whether there are any associated interfaces running on the pipe currently. |
| bool HasAssociatedInterfaces() const { |
| return internal_state_.HasAssociatedInterfaces(); |
| } |
| |
| // Returns true if bound and awaiting a response to a message. |
| bool IsExpectingResponse() { return internal_state_.has_pending_callbacks(); } |
| |
| // Indicates whether the message pipe has encountered an error. If true, |
| // method calls made on this interface will be dropped (and may already have |
| // been dropped). |
| bool encountered_error() const { return internal_state_.encountered_error(); } |
| |
| // Registers a handler to receive error notifications. The handler will be |
| // called from the sequence that owns this InterfacePtr. |
| // |
| // This method may only be called after the InterfacePtr has been bound to a |
| // message pipe. |
| void set_connection_error_handler(base::OnceClosure error_handler) { |
| internal_state_.set_connection_error_handler(std::move(error_handler)); |
| } |
| |
| void set_connection_error_with_reason_handler( |
| ConnectionErrorWithReasonCallback error_handler) { |
| internal_state_.set_connection_error_with_reason_handler( |
| std::move(error_handler)); |
| } |
| |
| // Unbinds the InterfacePtr and returns the information which could be used |
| // to setup an InterfacePtr again. This method may be used to move the proxy |
| // to a different sequence (see class comments for details). |
| // |
| // It is an error to call PassInterface() while: |
| // - there are pending responses; or |
| // TODO: fix this restriction, it's not always obvious when there is a |
| // pending response. |
| // - there are associated interfaces running. |
| // TODO(yzshen): For now, users need to make sure there is no one holding |
| // on to associated interface endpoint handles at both sides of the |
| // message pipe in order to call this method. We need a way to forcefully |
| // invalidate associated interface endpoint handles. |
| InterfacePtrInfo<Interface> PassInterface() { |
| CHECK(!HasAssociatedInterfaces()); |
| CHECK(!internal_state_.has_pending_callbacks()); |
| State state; |
| internal_state_.Swap(&state); |
| |
| return state.PassInterface(); |
| } |
| |
| bool Equals(const InterfacePtr& other) const { |
| if (this == &other) |
| return true; |
| |
| // Now that the two refer to different objects, they are equivalent if |
| // and only if they are both null. |
| return !(*this) && !other; |
| } |
| |
| // DO NOT USE. Exposed only for internal use and for testing. |
| internal::InterfacePtrState<Interface>* internal_state() { |
| return &internal_state_; |
| } |
| |
| // Allow InterfacePtr<> to be used in boolean expressions. |
| explicit operator bool() const { return internal_state_.is_bound(); } |
| |
| private: |
| typedef internal::InterfacePtrState<Interface> State; |
| mutable State internal_state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InterfacePtr); |
| }; |
| |
| // If |info| is valid (containing a valid message pipe handle), returns an |
| // InterfacePtr bound to it. Otherwise, returns an unbound InterfacePtr. |
| template <typename Interface> |
| InterfacePtr<Interface> MakeProxy( |
| InterfacePtrInfo<Interface> info, |
| scoped_refptr<base::SequencedTaskRunner> runner = nullptr) { |
| InterfacePtr<Interface> ptr; |
| if (info.is_valid()) |
| ptr.Bind(std::move(info), std::move(runner)); |
| return std::move(ptr); |
| } |
| |
| } // namespace mojo |
| |
| #endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_ |