blob: 34d3fbe104cb88284ec583a70237a3d9fe913ca8 [file]
// 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_SHARED_REMOTE_H_
#define MOJO_PUBLIC_CPP_BINDINGS_SHARED_REMOTE_H_
#include <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/stl_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "mojo/public/cpp/bindings/associated_group.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "mojo/public/cpp/bindings/sync_event_watcher.h"
#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
namespace mojo {
template <typename Interface>
class SharedRemote;
template <typename RemoteType>
class SharedRemoteBase
: public base::RefCountedThreadSafe<SharedRemoteBase<RemoteType>> {
public:
using InterfaceType = typename RemoteType::InterfaceType;
using PendingType = typename RemoteType::PendingType;
InterfaceType* get() { return &forwarder_->proxy(); }
InterfaceType* operator->() { return get(); }
InterfaceType& operator*() { return *get(); }
void set_disconnect_handler(
base::OnceClosure handler,
scoped_refptr<base::SequencedTaskRunner> handler_task_runner) {
wrapper_->set_disconnect_handler(std::move(handler),
std::move(handler_task_runner));
}
private:
friend class base::RefCountedThreadSafe<SharedRemoteBase<RemoteType>>;
template <typename Interface>
friend class SharedRemote;
template <typename Interface>
friend class SharedAssociatedRemote;
struct RemoteWrapperDeleter;
// Helper class which owns a |RemoteType| instance on an appropriate sequence.
// This is kept alive as long as it's bound within some ThreadSafeForwarder's
// callbacks.
class RemoteWrapper
: public base::RefCountedThreadSafe<RemoteWrapper, RemoteWrapperDeleter> {
public:
explicit RemoteWrapper(RemoteType remote)
: RemoteWrapper(base::SequencedTaskRunnerHandle::Get()) {
remote_ = std::move(remote);
associated_group_ = *remote_.internal_state()->associated_group();
}
explicit RemoteWrapper(scoped_refptr<base::SequencedTaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {}
void BindOnTaskRunner(PendingType remote) {
// TODO(https://crbug.com/682334): At the moment we don't have a group
// controller available. That means the user won't be able to pass
// associated endpoints on this interface (at least not immediately). In
// order to fix this, we need to create a MultiplexRouter immediately and
// bind it to the interface pointer on the |task_runner_|. Therefore,
// MultiplexRouter should be able to be created on a sequence different
// than the one that it is supposed to listen on.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&RemoteWrapper::Bind, this, std::move(remote)));
}
std::unique_ptr<ThreadSafeForwarder<InterfaceType>> CreateForwarder() {
return std::make_unique<ThreadSafeForwarder<InterfaceType>>(
task_runner_, base::BindRepeating(&RemoteWrapper::Accept, this),
base::BindRepeating(&RemoteWrapper::AcceptWithResponder, this),
associated_group_);
}
void set_disconnect_handler(
base::OnceClosure handler,
scoped_refptr<base::SequencedTaskRunner> handler_task_runner) {
if (!task_runner_->RunsTasksInCurrentSequence()) {
// Make sure we modify the remote's disconnect handler on the
// correct sequence.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&RemoteWrapper::set_disconnect_handler, this,
std::move(handler), std::move(handler_task_runner)));
return;
}
// The actual handler will post a task to run |handler| on
// |handler_task_runner|.
auto wrapped_handler =
base::BindOnce(base::IgnoreResult(&base::TaskRunner::PostTask),
handler_task_runner, FROM_HERE, std::move(handler));
// Because we may have had to post a task to set this handler,
// this call may land after the remote has just been disconnected.
// Manually invoke the handler in that case.
if (!remote_.is_connected()) {
std::move(wrapped_handler).Run();
return;
}
remote_.set_disconnect_handler(std::move(wrapped_handler));
}
private:
friend struct RemoteWrapperDeleter;
~RemoteWrapper() {}
void Bind(PendingType remote) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
remote_.Bind(std::move(remote));
// The ThreadSafeForwarder will always block the calling thread on a
// reply, so there's no need for the endpoint to employ its own sync
// waiting logic.
remote_.internal_state()->force_outgoing_messages_async(true);
}
void Accept(Message message) {
remote_.internal_state()->ForwardMessage(std::move(message));
}
void AcceptWithResponder(Message message,
std::unique_ptr<MessageReceiver> responder) {
remote_.internal_state()->ForwardMessageWithResponder(
std::move(message), std::move(responder));
}
void DeleteOnCorrectThread() const {
if (!task_runner_->RunsTasksInCurrentSequence()) {
// NOTE: This is only called when there are no more references to
// |this|, so binding it unretained is both safe and necessary.
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&RemoteWrapper::DeleteOnCorrectThread,
base::Unretained(this)));
} else {
delete this;
}
}
RemoteType remote_;
const scoped_refptr<base::SequencedTaskRunner> task_runner_;
AssociatedGroup associated_group_;
DISALLOW_COPY_AND_ASSIGN(RemoteWrapper);
};
struct RemoteWrapperDeleter {
static void Destruct(const RemoteWrapper* wrapper) {
wrapper->DeleteOnCorrectThread();
}
};
explicit SharedRemoteBase(scoped_refptr<RemoteWrapper> wrapper)
: wrapper_(std::move(wrapper)), forwarder_(wrapper_->CreateForwarder()) {}
// Creates a SharedRemoteBase wrapping an underlying non-thread-safe
// PendingType which is bound to the calling sequence. All messages sent
// via this thread-safe proxy will internally be sent by first posting to this
// (the calling) sequence's TaskRunner.
static scoped_refptr<SharedRemoteBase> Create(PendingType pending_remote) {
scoped_refptr<RemoteWrapper> wrapper =
new RemoteWrapper(RemoteType(std::move(pending_remote)));
return new SharedRemoteBase(wrapper);
}
// Creates a SharedRemoteBase which binds the underlying
// non-thread-safe InterfacePtrType on the specified TaskRunner. All messages
// sent via this thread-safe proxy will internally be sent by first posting to
// that TaskRunner.
static scoped_refptr<SharedRemoteBase> Create(
PendingType pending_remote,
scoped_refptr<base::SequencedTaskRunner> bind_task_runner) {
scoped_refptr<RemoteWrapper> wrapper =
new RemoteWrapper(std::move(bind_task_runner));
wrapper->BindOnTaskRunner(std::move(pending_remote));
return new SharedRemoteBase(wrapper);
}
~SharedRemoteBase() {}
const scoped_refptr<RemoteWrapper> wrapper_;
const std::unique_ptr<ThreadSafeForwarder<InterfaceType>> forwarder_;
DISALLOW_COPY_AND_ASSIGN(SharedRemoteBase);
};
// SharedRemote wraps a non-thread-safe Remote and proxies messages to it.
// Unlike normal Remote objects, SharedRemote is copyable and usable from any
// thread, but has some additional overhead and latency in message transmission
// as a trade-off.
//
// Async calls are posted to the bound sequence (the sequence that the
// underlying Remote is bound to, i.e. |bind_task_runner| below), and responses
// are posted back to the calling sequence. Sync calls are dispatched directly
// if the call is made on the bound sequence, or posted otherwise.
//
// This means that in general, when making calls from sequences other than the
// bound sequence, a hop is first made *to* the bound sequence; and when
// receiving replies, a hop is made *from* the bound the sequence.
//
// Note that sync calls only block the calling sequence.
template <typename Interface>
class SharedRemote {
public:
SharedRemote() = default;
explicit SharedRemote(PendingRemote<Interface> pending_remote)
: remote_(pending_remote.is_valid()
? SharedRemoteBase<Remote<Interface>>::Create(
std::move(pending_remote))
: nullptr) {}
SharedRemote(PendingRemote<Interface> pending_remote,
scoped_refptr<base::SequencedTaskRunner> bind_task_runner)
: remote_(pending_remote.is_valid()
? SharedRemoteBase<Remote<Interface>>::Create(
std::move(pending_remote),
std::move(bind_task_runner))
: nullptr) {}
bool is_bound() const { return remote_ != nullptr; }
explicit operator bool() const { return is_bound(); }
Interface* get() const { return remote_->get(); }
Interface* operator->() const { return get(); }
Interface& operator*() const { return *get(); }
void set_disconnect_handler(
base::OnceClosure handler,
scoped_refptr<base::SequencedTaskRunner> handler_task_runner) {
remote_->set_disconnect_handler(std::move(handler),
std::move(handler_task_runner));
}
// Clears this SharedRemote. Note that this does *not* necessarily close the
// remote's endpoint as other SharedRemote instances may reference the same
// underlying endpoint.
void reset() { remote_.reset(); }
private:
scoped_refptr<SharedRemoteBase<Remote<Interface>>> remote_;
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_SHARED_REMOTE_H_