| // Copyright 2019 The Chromium Authors |
| // 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_RECEIVER_H_ |
| #define MOJO_PUBLIC_CPP_BINDINGS_RECEIVER_H_ |
| |
| #include <memory> |
| #include <string_view> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/compiler_specific.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "mojo/public/cpp/bindings/async_flusher.h" |
| #include "mojo/public/cpp/bindings/connection_error_callback.h" |
| #include "mojo/public/cpp/bindings/connection_group.h" |
| #include "mojo/public/cpp/bindings/lib/binding_state.h" |
| #include "mojo/public/cpp/bindings/pending_flush.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h" |
| #include "mojo/public/cpp/bindings/runtime_features.h" |
| #include "mojo/public/cpp/system/message_pipe.h" |
| |
| namespace mojo { |
| |
| // A Receiver is used to receive and dispatch Interface method calls to a local |
| // implementation of Interface. Every Receiver object is permanently linked to |
| // an implementation of Interface at construction time. The Receiver begins |
| // receiving and scheduling method calls to the implementation once it becomes |
| // bound either by consuming a PendingReceiver (at construction time or via |
| // |Bind()|) or by calling |BindNewPipeAndPassRemote()|. |
| // |
| // Receiver is NOT thread- or sequence- safe and must be used from a single |
| // (but otherwise arbitrary) sequence. All bound Receiver objects are associated |
| // with a base::SequencedTaskRunner which the Receiver uses exclusively to |
| // schedule incoming method calls and disconnection notifications. |
| // |
| // IMPORTANT: In the name of memory safety, Interface method calls and |
| // disconnection notifications scheduled by a Receiver object will NEVER run |
| // beyond the lifetime of the Receiver object. |
| template <typename Interface, |
| typename ImplRefTraits = RawPtrImplRefTraits<Interface>> |
| class Receiver { |
| public: |
| // Typically (and by default) a Receiver uses a raw pointer to reference its |
| // linked Interface implementation object, because typically that |
| // implementation object owns the Receiver. An alternative |ImplRefTraits| may |
| // be provided as a second Receiver template argument in order to use a |
| // different reference type. |
| using ImplPointerType = typename ImplRefTraits::PointerType; |
| |
| // Constructs an unbound Receiver linked to |impl| for the duration of the |
| // Receiver's lifetime. The Receiver can be bound later by calling |Bind()| or |
| // |BindNewPipeAndPassRemote()|. An unbound Receiver does not schedule any |
| // asynchronous tasks. |
| explicit Receiver(ImplPointerType impl) : internal_state_(std::move(impl)) {} |
| |
| // Constructs a bound Receiver by consuming |pending_receiver|. The Receiver |
| // is permanently linked to |impl| and will schedule incoming |impl| method |
| // and disconnection notifications on the default SequencedTaskRunner (i.e. |
| // base::SequencedTaskRunner::GetCurrentDefault() at construction time). |
| Receiver(ImplPointerType impl, PendingReceiver<Interface> pending_receiver) |
| : Receiver(std::move(impl), std::move(pending_receiver), nullptr) {} |
| |
| // Similar to above but the constructed Receiver schedules all tasks via |
| // |task_runner| instead of the default SequencedTaskRunner. |task_runner| |
| // must run tasks on the same sequence that owns this Receiver. |
| Receiver(ImplPointerType impl, |
| PendingReceiver<Interface> pending_receiver, |
| scoped_refptr<base::SequencedTaskRunner> task_runner) |
| : internal_state_(std::move(impl)) { |
| Bind(std::move(pending_receiver), std::move(task_runner)); |
| } |
| |
| Receiver(const Receiver&) = delete; |
| Receiver& operator=(const Receiver&) = delete; |
| |
| ~Receiver() = default; |
| |
| // Indicates whether this Receiver is bound, meaning it may continue to |
| // receive Interface method calls from a remote caller. |
| // |
| // NOTE: A Receiver is NEVER passively unbound. The only way for it to become |
| // unbound is to explicitly call |reset()| or |Unbind()|. |
| bool is_bound() const { return internal_state_.is_bound(); } |
| |
| // Sets a OnceClosure to be invoked if this Receiver is cut off from its |
| // Remote (or PendingRemote). This can happen if the corresponding Remote (or |
| // unconsumed PendingRemote) has been destroyed, or if the Remote sends a |
| // malformed message. Must only be called on a bound Receiver object, and only |
| // remains set as long as the Receiver is both bound and connected. |
| // |
| // If ever invoked, |handler| will be scheduled asynchronously on the |
| // Receiver's bound SequencedTaskRunner. |
| void set_disconnect_handler(base::OnceClosure handler) { |
| internal_state_.set_connection_error_handler(std::move(handler)); |
| } |
| |
| // Like above but if this callback is set instead of the above, it can receive |
| // additional details about why the remote endpoint was closed, if provided. |
| void set_disconnect_with_reason_handler( |
| ConnectionErrorWithReasonCallback error_handler) { |
| DCHECK(is_bound()); |
| internal_state_.set_connection_error_with_reason_handler( |
| std::move(error_handler)); |
| } |
| |
| // Resets this Receiver to an unbound state. An unbound Receiver will NEVER |
| // schedule method calls or disconnection notifications, and any pending tasks |
| // which were scheduled prior to unbinding are effectively cancelled. |
| void reset() { internal_state_.Close(); } |
| |
| // Similar to the method above, but also specifies a disconnect reason. |
| void ResetWithReason(uint32_t custom_reason_code, |
| std::string_view description) { |
| internal_state_.CloseWithReason(custom_reason_code, description); |
| } |
| |
| // Binds this Receiver, connecting it to a new PendingRemote which is |
| // returned for transmission elsewhere (typically to a Remote who will consume |
| // it to start making calls). |
| // |
| // The Receiver will schedule incoming |impl| method calls and disconnection |
| // notifications on the default SequencedTaskRunner (i.e. |
| // base::SequencedTaskRunner::GetCurrentDefault() at the time of this call). |
| // Must only be called on an unbound Receiver. |
| [[nodiscard]] PendingRemote<Interface> BindNewPipeAndPassRemote() { |
| return BindNewPipeAndPassRemote(nullptr); |
| } |
| |
| // Like above, but the Receiver will schedule incoming |impl| method calls and |
| // disconnection notifications on |task_runner| rather than on the default |
| // SequencedTaskRunner. Must only be called on an unbound Receiver. |
| // |task_runner| must run tasks on the same sequence that owns this Receiver. |
| [[nodiscard]] PendingRemote<Interface> BindNewPipeAndPassRemote( |
| scoped_refptr<base::SequencedTaskRunner> task_runner) { |
| DCHECK(!is_bound()) << "Receiver for " << Interface::Name_ |
| << " is already bound"; |
| PendingRemote<Interface> remote; |
| if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) { |
| reset(); |
| return remote; |
| } |
| Bind(remote.InitWithNewPipeAndPassReceiver(), std::move(task_runner)); |
| return remote; |
| } |
| |
| // Like above, but the returned PendingRemote has the version annotated. |
| [[nodiscard]] PendingRemote<Interface> BindNewPipeAndPassRemoteWithVersion( |
| scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) { |
| auto remote = BindNewPipeAndPassRemote(task_runner); |
| remote.internal_state()->version = Interface::Version_; |
| return remote; |
| } |
| |
| // Binds this Receiver by consuming |pending_receiver|, which must be valid. |
| // Must only be called on an unbound Receiver. |
| // |
| // The newly bound Receiver will schedule incoming |impl| method calls and |
| // disconnection notifications on the default SequencedTaskRunner (i.e. |
| // base::SequencedTaskRunner::GetCurrentDefault() at the time of this call). |
| void Bind(PendingReceiver<Interface> pending_receiver) { |
| DCHECK(!is_bound()) << "Receiver for " << Interface::Name_ |
| << " is already bound"; |
| Bind(std::move(pending_receiver), nullptr); |
| } |
| |
| // Like above, but the newly bound Receiver will schedule incoming |impl| |
| // method calls and disconnection notifications on |task_runner| instead of |
| // the default SequencedTaskRunner. Must only be called on an unbound |
| // Receiver. |task_runner| must run tasks on the same sequence that owns this |
| // Receiver. |
| void Bind(PendingReceiver<Interface> pending_receiver, |
| scoped_refptr<base::SequencedTaskRunner> task_runner) { |
| DCHECK(!is_bound()) << "Receiver for " << Interface::Name_ |
| << " is already bound"; |
| if (!pending_receiver) { |
| reset(); |
| return; |
| } |
| if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) { |
| reset(); |
| return; |
| } |
| internal_state_.Bind(pending_receiver.internal_state(), |
| std::move(task_runner)); |
| } |
| |
| // Unbinds this Receiver, preventing any further |impl| method calls or |
| // disconnection notifications from being scheduled by it. Any such tasks that |
| // were scheduled prior to unbinding are effectively cancelled. |
| // |
| // Returns a PendingReceiver which remains connected to this receiver's |
| // Remote and which may be transferred elsewhere and consumed by another |
| // Receiver. Any messages received but not actually dispatched by this |
| // Receiver remain intact within the returned PendingReceiver and can be |
| // dispatched by whomever binds with it later. |
| // |
| // Note that a Receiver should not be unbound while there are still living |
| // response callbacks that haven't been invoked, as once the Receiver is |
| // unbound those response callbacks are no longer valid and the Remote will |
| // never be able to receive its expected responses. |
| [[nodiscard]] PendingReceiver<Interface> Unbind() { |
| DCHECK(is_bound()); |
| CHECK(!internal_state_.HasAssociatedInterfaces()); |
| return internal_state_.Unbind(); |
| } |
| |
| // Sets the message filter to be notified of each incoming message before |
| // dispatch. If a filter returns |false| from WillDispatch(), the message is |
| // not dispatched and the pipe is closed. Filters cannot be removed once |
| // added and only one can be set. |
| void SetFilter(std::unique_ptr<MessageFilter> filter) { |
| DCHECK(is_bound()); |
| internal_state_.SetFilter(std::move(filter)); |
| } |
| |
| // Pause and resume message dispatch. |
| void Pause() { |
| CHECK(!internal_state_.HasAssociatedInterfaces()); |
| internal_state_.PauseIncomingMethodCallProcessing(); |
| } |
| |
| void Resume() { internal_state_.ResumeIncomingMethodCallProcessing(); } |
| |
| // Blocks the calling thread until a new message arrives and is dispatched |
| // to the bound implementation. |
| bool WaitForIncomingCall() { |
| return internal_state_.WaitForIncomingMethodCall(); |
| } |
| |
| // Pauses the Remote endpoint, stopping dispatch of callbacks on that end. Any |
| // callbacks called prior to this will dispatch before the Remote endpoint is |
| // paused; any callbacks called after this will only be called once the flush |
| // operation corresponding to |flush| is 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 PauseRemoteCallbacksUntilFlushCompletes(PendingFlush flush) { |
| internal_state_.PauseRemoteCallbacksUntilFlushCompletes(std::move(flush)); |
| } |
| |
| // Flushes the Remote endpoint asynchronously using |flusher|. The |
| // corresponding PendingFlush will be notified only once all response |
| // callbacks issued prior to this operation have been dispatched at the Remote |
| // endpoint. |
| // |
| // 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; |
| } |
| |
| // Flushes any replies previously sent by the Receiver, only unblocking once |
| // acknowledgement from the Remote is received. |
| void FlushForTesting() { internal_state_.FlushForTesting(); } |
| |
| // Exposed for testing, should not generally be used. |
| void EnableTestingMode() { internal_state_.EnableTestingMode(); } |
| |
| // Allows test code to swap the interface implementation. |
| // |
| // Returns the existing interface implementation to the caller. |
| // |
| // The caller needs to guarantee that `new_impl` will live longer than |
| // `this` Receiver. One way to achieve this is to store the returned |
| // `old_impl` and swap it back in when `new_impl` is getting destroyed. |
| // Test code should prefer using `mojo::test::ScopedSwapImplForTesting` if |
| // possible. |
| [[nodiscard]] ImplPointerType SwapImplForTesting(ImplPointerType new_impl) { |
| return internal_state_.SwapImplForTesting(std::move(new_impl)); |
| } |
| |
| // Reports the currently dispatching message as bad and resets this receiver. |
| // Note that this is only legal to call from within the stack frame of a |
| // message dispatch. If you need to do asynchronous work before determining |
| // the legitimacy of a message, use GetBadMessageCallback() and retain its |
| // result until ready to invoke or discard it. |
| NOT_TAIL_CALLED void ReportBadMessage(std::string_view error) { |
| GetBadMessageCallback().Run(error); |
| } |
| |
| // Acquires a callback which may be run to report the currently dispatching |
| // message as bad and reset this receiver. Note that this is only legal to |
| // call from directly within stack frame of a message dispatch, but the |
| // returned callback may be called exactly once any time thereafter to report |
| // the message as bad. |GetBadMessageCallback()| may only be called once per |
| // message, and the returned callback must be run on the same sequence to |
| // which this Receiver is bound. |
| ReportBadMessageCallback GetBadMessageCallback() { |
| return internal_state_.GetBadMessageCallback(); |
| } |
| |
| // DO NOT USE. Exposed only for internal use and for testing. |
| internal::BindingState<Interface, ImplRefTraits>* internal_state() { |
| return &internal_state_; |
| } |
| |
| private: |
| internal::BindingState<Interface, ImplRefTraits> internal_state_; |
| }; |
| |
| } // namespace mojo |
| |
| #endif // MOJO_PUBLIC_CPP_BINDINGS_RECEIVER_H_ |