Commit 14c0359b authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

Mojo: Introduce BinderMap

This is a stripped down incarnation of Service Manager's BinderMap type.
Unlike that type, there is no intent here to support the complexity of
variadic binder arguments since such use cases are generally now limited
to the Content layer or above.

This is a straightforward mapping of interface name to callback + task
runner.

At first this will at least be used by Blink and various Content child
process types to expose interfaces to the browser through a common path
in ChildThreadImpl.

Bug: 977637
Change-Id: I7aa7cf40995b1898090202bd4e4377285c776421
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1905047
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: default avatarOksana Zhuravlova <oksamyt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714815}
parent cddafce1
...@@ -129,6 +129,8 @@ component("bindings") { ...@@ -129,6 +129,8 @@ component("bindings") {
"associated_receiver.h", "associated_receiver.h",
"associated_receiver_set.h", "associated_receiver_set.h",
"associated_remote.h", "associated_remote.h",
"binder_map.cc",
"binder_map.h",
"binding.h", "binding.h",
"binding_set.h", "binding_set.h",
"callback_helpers.h", "callback_helpers.h",
......
// 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.
#include "mojo/public/cpp/bindings/binder_map.h"
#include "base/stl_util.h"
namespace mojo {
BinderMap::BinderMap() = default;
BinderMap::BinderMap(BinderMap&&) = default;
BinderMap::BinderMap(const BinderMap&) = default;
BinderMap::~BinderMap() = default;
BinderMap& BinderMap::operator=(BinderMap&&) = default;
BinderMap& BinderMap::operator=(const BinderMap&) = default;
bool BinderMap::Bind(GenericPendingReceiver* receiver) {
DCHECK(receiver && *receiver) << "Attempted to bind null or invalid receiver";
auto iter = binders_.find(*receiver->interface_name());
if (iter == binders_.end())
return false;
iter->second.Run(std::move(*receiver));
return true;
}
bool BinderMap::CanBind(const GenericPendingReceiver& receiver) const {
DCHECK(receiver);
return base::Contains(binders_, *receiver.interface_name());
}
void BinderMap::AddGenericBinder(base::StringPiece name, GenericBinder binder) {
auto result = binders_.emplace(name.as_string(), std::move(binder));
DCHECK(result.second) << "Binder already registered for " << name;
}
} // namespace mojo
// 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_BINDER_MAP_H_
#define MOJO_PUBLIC_CPP_BINDINGS_BINDER_MAP_H_
#include <map>
#include <string>
#include "base/callback.h"
#include "base/component_export.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_piece.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
namespace mojo {
// BinderMap is a simple helper class which maintains a registry of callbacks
// that can bind receivers for arbitrary Mojo interfaces. By default a BinderMap
// is empty and cannot bind any interfaces.
//
// Call |Add()| to register a new binder for a specific interface.
// Call |Bind()| to attempt to run a registered binder on the generic input
// receiver.
//
// BinderMap instances are safe to move across sequences but must be used from
// only once sequence at a time; they are also copyable.
class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) BinderMap {
public:
BinderMap();
BinderMap(const BinderMap&);
BinderMap(BinderMap&&);
~BinderMap();
BinderMap& operator=(const BinderMap&);
BinderMap& operator=(BinderMap&&);
template <typename Interface>
using Binder = base::RepeatingCallback<void(PendingReceiver<Interface>)>;
// Adds a binder for Interface to this BinderMap. If |Bind()| is ever called
// with a GenericPendingReceiver which matches Interface, this binder will be
// invoked asynchronously on |task_runner|.
//
// It's an error to call |Add()| multiple times for the same interface.
template <typename Interface>
void Add(Binder<Interface> binder,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
AddGenericBinder(
Interface::Name_,
MakeGenericBinder(std::move(binder), std::move(task_runner)));
}
// Temporary helper during the transition to new Mojo types.
template <typename Interface>
using LegacyBinder =
base::RepeatingCallback<void(InterfaceRequest<Interface>)>;
template <typename Interface>
void Add(LegacyBinder<Interface> binder,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
AddGenericBinder(
Interface::Name_,
MakeGenericBinder(std::move(binder), std::move(task_runner)));
}
// Attempts to bind |receiver| using one of the binders registered in this
// BinderMap. If a matching binder is found, it is scheduled to run
// asynchronously on its associated SequencedTaskRunner; the value in
// |*receiver| is consumed immediately and this method returns |true|.
//
// If no matching binder is found, |*receiver| is left intact and this method
// returns |false|.
bool Bind(GenericPendingReceiver* receiver) WARN_UNUSED_RESULT;
// Indicates whether or not |Bind()| would succeed if given |receiver|.
bool CanBind(const GenericPendingReceiver& receiver) const;
private:
using GenericBinder = base::RepeatingCallback<void(GenericPendingReceiver)>;
template <typename Interface>
GenericBinder MakeGenericBinder(
Binder<Interface> binder,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
return base::BindRepeating(
[](Binder<Interface> binder,
scoped_refptr<base::SequencedTaskRunner> task_runner,
GenericPendingReceiver receiver) {
task_runner->PostTask(
FROM_HERE, base::BindOnce(binder, receiver.As<Interface>()));
},
std::move(binder), std::move(task_runner));
}
template <typename Interface>
GenericBinder MakeGenericBinder(
LegacyBinder<Interface> binder,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
return base::BindRepeating(
[](LegacyBinder<Interface> binder,
scoped_refptr<base::SequencedTaskRunner> task_runner,
GenericPendingReceiver receiver) {
InterfaceRequest<Interface> request = receiver.As<Interface>();
task_runner->PostTask(FROM_HERE,
base::BindOnce(binder, std::move(request)));
},
std::move(binder), std::move(task_runner));
}
void AddGenericBinder(base::StringPiece name, GenericBinder binder);
std::map<std::string, GenericBinder> binders_;
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_BINDER_MAP_H_
...@@ -10,6 +10,7 @@ source_set("tests") { ...@@ -10,6 +10,7 @@ source_set("tests") {
sources = [ sources = [
"associated_interface_unittest.cc", "associated_interface_unittest.cc",
"bind_task_runner_unittest.cc", "bind_task_runner_unittest.cc",
"binder_map_unittest.cc",
"bindings_test_base.cc", "bindings_test_base.cc",
"bindings_test_base.h", "bindings_test_base.h",
"buffer_unittest.cc", "buffer_unittest.cc",
...@@ -137,6 +138,7 @@ source_set("struct_with_traits_impl") { ...@@ -137,6 +138,7 @@ source_set("struct_with_traits_impl") {
mojom("test_mojom") { mojom("test_mojom") {
testonly = true testonly = true
sources = [ sources = [
"binder_map_unittest.test-mojom",
"connection_group_unittest.test-mojom", "connection_group_unittest.test-mojom",
"enum_headers_unittest.test-mojom", "enum_headers_unittest.test-mojom",
"idle_tracking_unittest.test-mojom", "idle_tracking_unittest.test-mojom",
......
// 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.
#include "mojo/public/cpp/bindings/binder_map.h"
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/bindings/tests/binder_map_unittest.test-mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace test {
namespace binder_map_unittest {
class BinderMapTest : public testing::Test {
public:
BinderMapTest() = default;
private:
base::test::TaskEnvironment task_environment_;
DISALLOW_COPY_AND_ASSIGN(BinderMapTest);
};
class TestInterface1Impl : public mojom::TestInterface1 {
public:
TestInterface1Impl() = default;
~TestInterface1Impl() override = default;
void Bind(scoped_refptr<base::SequencedTaskRunner> expected_task_runner,
mojo::PendingReceiver<mojom::TestInterface1> receiver) {
if (expected_task_runner)
EXPECT_TRUE(expected_task_runner->RunsTasksInCurrentSequence());
receiver_.Bind(std::move(receiver));
}
void BindFromRequest(
scoped_refptr<base::SequencedTaskRunner> expected_task_runner,
mojom::TestInterface1Request request) {
if (expected_task_runner)
EXPECT_TRUE(expected_task_runner->RunsTasksInCurrentSequence());
receiver_.Bind(std::move(request));
}
private:
mojo::Receiver<mojom::TestInterface1> receiver_{this};
};
class TestInterface2Impl : public mojom::TestInterface2 {
public:
TestInterface2Impl() = default;
~TestInterface2Impl() override = default;
void Bind(scoped_refptr<base::SequencedTaskRunner> expected_task_runner,
mojo::PendingReceiver<mojom::TestInterface2> receiver) {
if (expected_task_runner)
EXPECT_TRUE(expected_task_runner->RunsTasksInCurrentSequence());
receiver_.Bind(std::move(receiver));
}
private:
mojo::Receiver<mojom::TestInterface2> receiver_{this};
};
TEST_F(BinderMapTest, NoMatch) {
Remote<mojom::TestInterface1> remote;
GenericPendingReceiver receiver(remote.BindNewPipeAndPassReceiver());
BinderMap empty_map;
EXPECT_FALSE(empty_map.CanBind(receiver));
EXPECT_FALSE(empty_map.Bind(&receiver));
}
TEST_F(BinderMapTest, BasicMatch) {
Remote<mojom::TestInterface1> remote;
GenericPendingReceiver receiver(remote.BindNewPipeAndPassReceiver());
TestInterface1Impl impl;
BinderMap map;
map.Add(base::BindRepeating(&TestInterface1Impl::Bind,
base::Unretained(&impl), nullptr),
base::SequencedTaskRunnerHandle::Get());
EXPECT_TRUE(map.CanBind(receiver));
EXPECT_TRUE(map.Bind(&receiver));
remote.FlushForTesting();
EXPECT_TRUE(remote.is_connected());
}
TEST_F(BinderMapTest, BasicMatchWithInterfaceRequestCallback) {
Remote<mojom::TestInterface1> remote;
GenericPendingReceiver receiver(remote.BindNewPipeAndPassReceiver());
TestInterface1Impl impl;
BinderMap map;
map.Add(base::BindRepeating(&TestInterface1Impl::BindFromRequest,
base::Unretained(&impl), nullptr),
base::SequencedTaskRunnerHandle::Get());
EXPECT_TRUE(map.CanBind(receiver));
EXPECT_TRUE(map.Bind(&receiver));
remote.FlushForTesting();
EXPECT_TRUE(remote.is_connected());
}
TEST_F(BinderMapTest, CorrectSequence) {
Remote<mojom::TestInterface1> remote1;
GenericPendingReceiver receiver1(remote1.BindNewPipeAndPassReceiver());
Remote<mojom::TestInterface2> remote2;
GenericPendingReceiver receiver2(remote2.BindNewPipeAndPassReceiver());
auto task_runner1 = base::CreateSequencedTaskRunner({base::CurrentThread()});
auto task_runner2 = base::CreateSequencedTaskRunner({base::ThreadPool()});
TestInterface1Impl impl1;
std::unique_ptr<TestInterface2Impl> impl2;
// Create |impl2| on the ThreadPool task runner.
base::RunLoop create_impl2_loop;
task_runner2->PostTask(FROM_HERE, base::BindLambdaForTesting([&] {
impl2 = std::make_unique<TestInterface2Impl>();
create_impl2_loop.Quit();
}));
create_impl2_loop.Run();
BinderMap map;
map.Add(base::BindRepeating(&TestInterface1Impl::Bind,
base::Unretained(&impl1), task_runner1),
task_runner1);
map.Add(base::BindRepeating(&TestInterface2Impl::Bind,
base::Unretained(impl2.get()), task_runner2),
task_runner2);
EXPECT_TRUE(map.Bind(&receiver1));
EXPECT_TRUE(map.Bind(&receiver2));
remote1.FlushForTesting();
remote2.FlushForTesting();
EXPECT_TRUE(remote1.is_connected());
EXPECT_TRUE(remote2.is_connected());
// Destroy |impl2| on the ThreadPool task runner.
base::RunLoop destroy_impl2_loop;
task_runner2->PostTask(FROM_HERE, base::BindLambdaForTesting([&] {
impl2.reset();
destroy_impl2_loop.Quit();
}));
destroy_impl2_loop.Run();
}
} // namespace binder_map_unittest
} // namespace test
} // namespace mojo
// 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.
module mojo.test.binder_map_unittest.mojom;
interface TestInterface1 {};
interface TestInterface2 {};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment