// 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 #include #include "base/callback_forward.h" #include "base/check.h" #include "base/compiler_specific.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/async_flusher.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_flush.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 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 class Remote { public: using InterfaceType = Interface; using PendingType = PendingRemote; // 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 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 pending_remote, scoped_refptr 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 any time 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. // - There are no interfaces which were bound directly or transitively // through this Remote and are still connected. // - 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 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 BindNewPipeAndPassReceiver( scoped_refptr task_runner) WARN_UNUSED_RESULT { MessagePipe pipe; Bind(PendingRemote(std::move(pipe.handle0), 0), std::move(task_runner)); return PendingReceiver(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 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 pending_remote, scoped_refptr 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 Unbind() WARN_UNUSED_RESULT { DCHECK(is_bound()); CHECK(!internal_state_.has_pending_callbacks()); State state; internal_state_.Swap(&state); InterfacePtrInfo info = state.PassInterface(); return PendingRemote(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 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); } // Pauses the receiving endpoint until the flush corresponding to |flush| has // completed. Any calls made on this Remote prior to this call will be // dispatched at the receiving endpoint before pausing. The endpoint will not // dispatch any subsequent calls until the flush operation corresponding to // |flush| has been completed or canceled. // // See documentation for |FlushAsync()| on Remote and Receiver for how to // acquire a PendingFlush object, and documentation on PendingFlush for // example usage. void PauseReceiverUntilFlushCompletes(PendingFlush flush) { internal_state_.PauseReceiverUntilFlushCompletes(std::move(flush)); } // Flushes the receiving endpoint asynchronously using |flusher|. Once all // calls made on this Remote prior to this |FlushAsyncWithFlusher()| call have // dispatched at the receiving endpoint, |flusher| will signal its // corresponding PendingFlush, unblocking any endpoint waiting on the flush // operation. // // NOTE: It is more common to use |FlushAsync()| defined below. If you really // want to provide your own AsyncFlusher using this method, see the // single-arugment constructor on PendingFlush. This would typically be used // when code executing on the current sequence wishes to immediately pause // one of its remote endpoints to wait on a flush operation that needs to be // initiated on a separate sequence. Rather than bouncing to the second // sequence to initiate a flush and then passing a PendingFlush back to the // original sequence, the AsyncFlusher/PendingFlush can be created on the // original sequence and a single task can be posted to pass the AsyncFlusher // to the second sequence for use with this method. void FlushAsyncWithFlusher(AsyncFlusher flusher) { internal_state_.FlushAsync(std::move(flusher)); } // Same as above but an AsyncFlusher/PendingFlush pair is created on the // caller's behalf. The AsyncFlusher is immediately passed to a // |FlushAsyncWithFlusher()| call on this object, while the PendingFlush is // returned for use by the caller. See documentation on PendingFlush for // example usage. PendingFlush FlushAsync() { AsyncFlusher flusher; PendingFlush flush(&flusher); FlushAsyncWithFlusher(std::move(flusher)); return flush; } // 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* internal_state() { return &internal_state_; } private: using State = internal::InterfacePtrState; mutable State internal_state_; DISALLOW_COPY_AND_ASSIGN(Remote); }; } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_REMOTE_H_