blob: 8d51fbad3832ee831e19ef7cc575da989ddaa43f [file] [log] [blame]
// 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_