| // Copyright 2019 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_REMOTE_H_ |
| #define MOJO_PUBLIC_CPP_BINDINGS_REMOTE_H_ |
| |
| #include <cstdint> |
| #include <utility> |
| |
| #include "base/callback_forward.h" |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/time/time.h" |
| #include "mojo/public/cpp/bindings/interface_ptr_info.h" |
| #include "mojo/public/cpp/bindings/lib/interface_ptr_state.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/system/message_pipe.h" |
| |
| namespace mojo { |
| |
| // A Remote is used to issue Interface method calls to a single connected |
| // Receiver or PendingReceiver. The Remote must be bound in order to issue those |
| // method calls, and it becomes bound by consuming a PendingRemote either at |
| // construction time or by calling |Bind()|. |
| // |
| // Remote is NOT thread- or sequence-safe and must be used on a single |
| // (but otherwise arbitrary) sequence. All bound Remote objects are associated |
| // with a base::SequenceTaskRunner which the Remote uses exclusively to schedule |
| // response callbacks and disconnection notifications. |
| // |
| // The most common ways to bind a Remote are to consume a PendingRemote received |
| // via some IPC, or to call |BindNewPipeAndPassReceiver()| and send the returned |
| // PendingReceiver somewhere useful (i.e., to a remote Receiver who will consume |
| // it). For example: |
| // |
| // mojo::Remote<mojom::Widget> widget; |
| // widget_factory->CreateWidget(widget.BindNewPipeAndPassReceiver()); |
| // widget->Click(); |
| // |
| // IMPORTANT: There are some things to be aware of regarding Interface method |
| // calls as they relate to Remote object lifetime: |
| // |
| // - Interface method calls issued immediately before the destruction of a |
| // Remote ARE guaranteed to be transmitted to the remote's receiver as long |
| // as the receiver itself remains alive, either as a Receiver or a |
| // PendingReceiver. |
| // |
| // - In the name of memory safety, Interface method response callbacks (and in |
| // general ANY tasks which can be scheduled by a Remote) will NEVER |
| // be dispatched beyond the lifetime of the Remote object. As such, if |
| // you make a call and you need its reply, you must keep the Remote alive |
| // until the reply is received. |
| template <typename Interface> |
| class Remote { |
| public: |
| using InterfaceType = Interface; |
| using PendingType = PendingRemote<Interface>; |
| |
| // Constructs an unbound Remote. This object cannot issue Interface method |
| // calls and does not schedule any tasks. |
| Remote() = default; |
| Remote(Remote&& other) noexcept { *this = std::move(other); } |
| |
| // Constructs a new Remote which is bound from |pending_remote| and which |
| // schedules response callbacks and disconnection notifications on the default |
| // SequencedTaskRunner (i.e., base::SequencedTaskRunnerHandle::Get() at |
| // construction time). |
| explicit Remote(PendingRemote<Interface> pending_remote) |
| : Remote(std::move(pending_remote), nullptr) {} |
| |
| // Constructs a new Remote which is bound from |pending_remote| and which |
| // schedules response callbacks and disconnection notifications on |
| // |task_runner|. |task_runner| must run tasks on the same sequence that owns |
| // this Remote. |
| Remote(PendingRemote<Interface> pending_remote, |
| scoped_refptr<base::SequencedTaskRunner> task_runner) { |
| Bind(std::move(pending_remote), std::move(task_runner)); |
| } |
| |
| ~Remote() = default; |
| |
| Remote& operator=(Remote&& other) noexcept { |
| internal_state_.Swap(&other.internal_state_); |
| return *this; |
| } |
| |
| // Exposes access to callable Interface methods directed at this Remote's |
| // receiver. Must only be called on a bound Remote. |
| typename Interface::Proxy_* get() const { |
| DCHECK(is_bound()) |
| << "Cannot issue Interface method calls on an unbound Remote"; |
| return internal_state_.instance(); |
| } |
| |
| // Shorthand form of |get()|. See above. |
| typename Interface::Proxy_* operator->() const { return get(); } |
| typename Interface::Proxy_& operator*() const { return *get(); } |
| |
| // Indicates whether this Remote is bound and thus can issue Interface method |
| // calls via the above accessors. |
| // |
| // NOTE: The state of being "bound" should not be confused with the state of |
| // being "connected" (see |is_connected()| below). A Remote is NEVER passively |
| // unbound and the only way for it to become unbound is to explicitly call |
| // |reset()| or |Unbind()|. As such, unless you make explicit calls to those |
| // methods, it is always safe to assume that a Remote you've bound will remain |
| // bound and callable. |
| bool is_bound() const { return internal_state_.is_bound(); } |
| explicit operator bool() const { return is_bound(); } |
| |
| // Indicates whether this Remote is connected to a receiver. Must only be |
| // called on a bound Remote. If this returns |true|, method calls made by this |
| // Remote may eventually end up at the connected receiver (though it's of |
| // course possible for this call to race with disconnection). If this returns |
| // |false| however, all future Interface method calls on this Remote will be |
| // silently dropped. |
| // |
| // A bound Remote becomes disconnected automatically either when its receiver |
| // is destroyed, or when it receives a malformed or otherwise unexpected |
| // response message from the receiver. |
| // |
| // NOTE: The state of being "bound" should not be confused with the state of |
| // being "connected". See |is_bound()| above. |
| bool is_connected() const { |
| DCHECK(is_bound()); |
| return !internal_state_.encountered_error(); |
| } |
| |
| // Sets a Closure to be invoked if this Remote is cut off from its receiver. |
| // This can happen if the corresponding Receiver (or unconsumed |
| // PendingReceiver) is destroyed, or if the Receiver sends a malformed or |
| // otherwise unexpected response message to this Remote. Must only be called |
| // on a bound Remote object, and only remains set as long as the Remote is |
| // both bound and connected. |
| // |
| // If invoked at all, |handler| will be scheduled asynchronously using the |
| // Remote's bound SequencedTaskRunner. |
| void set_disconnect_handler(base::OnceClosure handler) { |
| if (is_connected()) |
| internal_state_.set_connection_error_handler(std::move(handler)); |
| } |
| |
| // Like above but also receives extra user-defined metadata about why the |
| // receiving endpoint was closed. |
| void set_disconnect_with_reason_handler( |
| ConnectionErrorWithReasonCallback handler) { |
| internal_state_.set_connection_error_with_reason_handler( |
| std::move(handler)); |
| } |
| |
| // A convenient helper that resets this Remote on disconnect. Note that this |
| // replaces any previously set disconnection handler. |
| void reset_on_disconnect() { |
| if (!is_connected()) { |
| reset(); |
| return; |
| } |
| set_disconnect_handler( |
| base::BindOnce(&Remote::reset, base::Unretained(this))); |
| } |
| |
| // Sets a Closure to be invoked if the receiving endpoint reports itself as |
| // idle and there are no in-flight messages it has yet to acknowledge, and |
| // this state occurs continuously for a duration of at least |timeout|. The |
| // first time this is called, it must be called BEFORE sending any interface |
| // messages to the receiver. It may be called any number of times after that |
| // to reconfigure the idle timeout period or assign a new idle handler. |
| // |
| // Once called, the interface connection incurs some permanent additional |
| // per-message overhead to help track idle state across the interface |
| // boundary. |
| // |
| // Whenever this callback is invoked, the following conditions are guaranteed |
| // to hold: |
| // |
| // - There are no messages sent on this Remote that have not already been |
| // dispatched by the receiver. |
| // - The receiver has explicitly notified us that it considers itself to be |
| // "idle." |
| // - The receiver has not dispatched any additional messages since sending |
| // this idle notification |
| // - The Remote does not have any outstanding reply callbacks that haven't |
| // been called yet |
| // - All of the above has been true continuously for a duration of at least |
| // |timeout|. |
| // |
| void set_idle_handler(base::TimeDelta timeout, |
| base::RepeatingClosure handler) { |
| internal_state_.set_idle_handler(timeout, std::move(handler)); |
| } |
| |
| // A convenient helper for common idle timeout behavior. This is equivalent to |
| // calling |set_idle_handler| with a handler that only resets this Remote. |
| void reset_on_idle_timeout(base::TimeDelta timeout) { |
| set_idle_handler( |
| timeout, base::BindRepeating(&Remote::reset, base::Unretained(this))); |
| } |
| |
| // Resets this Remote to an unbound state. To reset the Remote and recover an |
| // PendingRemote that can be bound again later, use |Unbind()| instead. |
| void reset() { |
| State doomed_state; |
| internal_state_.Swap(&doomed_state); |
| } |
| |
| // 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(); |
| } |
| |
| // Returns the version of Interface used by this Remote. Defaults to 0 but can |
| // be adjusted either at binding time, or by invoking either |QueryVersion()| |
| // or |RequireVersion()|. |
| uint32_t version() const { return internal_state_.version(); } |
| |
| // Binds this Remote, connecting it to a new PendingReceiver which is |
| // returned for transmission to some Receiver which can bind it. The Remote |
| // will schedule any response callbacks or disconnection notifications on the |
| // default SequencedTaskRunner (i.e. base::SequencedTaskRunnerHandle::Get() at |
| // the time of this call). Must only be called on an unbound Remote. |
| PendingReceiver<Interface> BindNewPipeAndPassReceiver() WARN_UNUSED_RESULT { |
| return BindNewPipeAndPassReceiver(nullptr); |
| } |
| |
| // Like above, but the Remote will schedule response callbacks and |
| // disconnection notifications on |task_runner| instead of the default |
| // SequencedTaskRunner. |task_runner| must run tasks on the same sequence that |
| // owns this Remote. |
| PendingReceiver<Interface> BindNewPipeAndPassReceiver( |
| scoped_refptr<base::SequencedTaskRunner> task_runner) WARN_UNUSED_RESULT { |
| MessagePipe pipe; |
| Bind(PendingRemote<Interface>(std::move(pipe.handle0), 0), |
| std::move(task_runner)); |
| return PendingReceiver<Interface>(std::move(pipe.handle1)); |
| } |
| |
| // Binds this Remote by consuming |pending_remote|, which must be valid. The |
| // Remote will schedule any response callbacks or disconnection notifications |
| // on the default SequencedTaskRunner (i.e. |
| // base::SequencedTaskRunnerHandle::Get() at the time of this call). Must only |
| // be called on an unbound Remote. |
| void Bind(PendingRemote<Interface> pending_remote) { |
| DCHECK(pending_remote.is_valid()); |
| Bind(std::move(pending_remote), nullptr); |
| } |
| |
| // Like above, but the Remote will schedule response callbacks and |
| // disconnection notifications on |task_runner| instead of the default |
| // SequencedTaskRunner. Must only be called on an unbound Remote. |
| // |task_runner| must run tasks on the same sequence that owns this Remote. |
| void Bind(PendingRemote<Interface> pending_remote, |
| scoped_refptr<base::SequencedTaskRunner> task_runner) { |
| DCHECK(!is_bound()) << "Remote is already bound"; |
| if (!pending_remote) { |
| reset(); |
| return; |
| } |
| |
| internal_state_.Bind(pending_remote.internal_state(), |
| std::move(task_runner)); |
| |
| // Force the internal state to configure its proxy. Unlike InterfacePtr we |
| // do not use Remote in transit, so binding to a pipe handle can also imply |
| // binding to a SequencedTaskRunner and observing pipe handle state. This |
| // allows for e.g. |is_connected()| to be a more reliable API than |
| // |InterfacePtr::encountered_error()|. |
| ignore_result(internal_state_.instance()); |
| } |
| |
| // Unbinds this Remote, rendering it unable to issue further Interface method |
| // calls. Returns a PendingRemote which may be passed across threads or |
| // processes and consumed by another Remote elsewhere. |
| // |
| // Note that it is an error (the bad, crashy kind of error) to attempt to |
| // |Unbind()| a Remote which is awaiting one or more responses to previously |
| // issued Interface method calls. Calling this method should only be |
| // considered in cases where satisfaction of that constraint can be proven. |
| // |
| // Must only be called on a bound Remote. |
| PendingRemote<Interface> Unbind() WARN_UNUSED_RESULT { |
| DCHECK(is_bound()); |
| CHECK(!internal_state_.has_pending_callbacks()); |
| State state; |
| internal_state_.Swap(&state); |
| InterfacePtrInfo<Interface> info = state.PassInterface(); |
| return PendingRemote<Interface>(info.PassHandle(), info.version()); |
| } |
| |
| // Queries the max version that the receiving endpoint supports. Once a |
| // response is received, |callback| will be invoked with the version number |
| // and the version number of this Remote object will also be updated. |
| void QueryVersion(base::OnceCallback<void(uint32_t)> callback) { |
| internal_state_.QueryVersion(std::move(callback)); |
| } |
| |
| // Requires the receiving endpoint to support at least the specified |
| // |version|. If it does not, it will close its end of the connection |
| // immediately. |
| 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(); } |
| |
| // Same as |FlushForTesting()| but will call |callback| when the flush is |
| // complete. |
| void FlushAsyncForTesting(base::OnceClosure callback) { |
| internal_state_.FlushAsyncForTesting(std::move(callback)); |
| } |
| |
| // Returns the number of unacknowledged messages sent by this Remote. Only |
| // non-zero when |set_idle_handler()| has been called. |
| unsigned int GetNumUnackedMessagesForTesting() const { |
| return internal_state_.GetNumUnackedMessagesForTesting(); |
| } |
| |
| // DO NOT USE. Exposed only for internal use and for testing. |
| internal::InterfacePtrState<Interface>* internal_state() { |
| return &internal_state_; |
| } |
| |
| private: |
| using State = internal::InterfacePtrState<Interface>; |
| mutable State internal_state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Remote); |
| }; |
| |
| } // namespace mojo |
| |
| #endif // MOJO_PUBLIC_CPP_BINDINGS_REMOTE_H_ |