Commit ca6057dc authored by kmarshall's avatar kmarshall Committed by Commit bot

Use shared memory for moving data over BlobChannel Mojo interface.

* Change BlobChannel PutBlob() Mojo IDL to use shared memory handles
  instead of a byte array.
* Create "SharedMemoryBlob" class for encapsulating Mojo shared
  memory initialization and lifetime management semantics.
* Add BlobChannelSenderProxy class, which manages interactions with the renderer side of the BlobChannel IPC interface.
* Add Blimp-specific run_all_unittests.cc, which bootstraps the Mojo environment before executing unittests.

R=wez@chromium.org
CC=maniscalco@chromium.org
BUG=600719,614564

Review-Url: https://codereview.chromium.org/2033013003
Cr-Commit-Position: refs/heads/master@{#406045}
parent 14bc66f8
...@@ -67,6 +67,7 @@ test("blimp_unittests") { ...@@ -67,6 +67,7 @@ test("blimp_unittests") {
"//blimp/client/core:unit_tests", "//blimp/client/core:unit_tests",
"//blimp/common:unit_tests", "//blimp/common:unit_tests",
"//blimp/net:unit_tests", "//blimp/net:unit_tests",
"//blimp/test:run_all_unittests",
] ]
if (is_linux) { if (is_linux) {
......
...@@ -89,7 +89,6 @@ source_set("blimp_client_unit_tests") { ...@@ -89,7 +89,6 @@ source_set("blimp_client_unit_tests") {
":session", ":session",
":switches", ":switches",
"//base", "//base",
"//base/test:run_all_unittests",
"//base/test:test_support", "//base/test:test_support",
"//blimp/common", "//blimp/common",
"//testing/gmock", "//testing/gmock",
...@@ -125,7 +124,6 @@ source_set("app_unit_tests") { ...@@ -125,7 +124,6 @@ source_set("app_unit_tests") {
deps = [ deps = [
":client", ":client",
"//base", "//base",
"//base/test:run_all_unittests",
"//base/test:test_support", "//base/test:test_support",
"//testing/gmock", "//testing/gmock",
"//testing/gtest", "//testing/gtest",
...@@ -258,7 +256,6 @@ source_set("feature_unit_tests") { ...@@ -258,7 +256,6 @@ source_set("feature_unit_tests") {
":compositor", ":compositor",
":test_support", ":test_support",
"//base", "//base",
"//base/test:run_all_unittests",
"//base/test:test_support", "//base/test:test_support",
"//blimp/common", "//blimp/common",
"//blimp/common/proto", "//blimp/common/proto",
......
...@@ -399,7 +399,6 @@ source_set("app_unit_tests") { ...@@ -399,7 +399,6 @@ source_set("app_unit_tests") {
":app_net", ":app_net",
":app_settings", ":app_settings",
"//base", "//base",
"//base/test:run_all_unittests",
"//base/test:test_support", "//base/test:test_support",
"//blimp/common", "//blimp/common",
"//blimp/common:test_support", "//blimp/common:test_support",
...@@ -427,7 +426,6 @@ source_set("common_unit_tests") { ...@@ -427,7 +426,6 @@ source_set("common_unit_tests") {
deps = [ deps = [
":common", ":common",
"//base", "//base",
"//base/test:run_all_unittests",
"//base/test:test_support", "//base/test:test_support",
"//components/metrics:metrics", "//components/metrics:metrics",
"//content/public/browser", "//content/public/browser",
...@@ -449,7 +447,6 @@ source_set("feature_unit_tests") { ...@@ -449,7 +447,6 @@ source_set("feature_unit_tests") {
deps = [ deps = [
":feature", ":feature",
"//base", "//base",
"//base/test:run_all_unittests",
"//base/test:test_support", "//base/test:test_support",
"//blimp/common", "//blimp/common",
"//blimp/common/proto", "//blimp/common/proto",
...@@ -472,11 +469,19 @@ source_set("renderer_unit_tests") { ...@@ -472,11 +469,19 @@ source_set("renderer_unit_tests") {
sources = [ sources = [
"renderer/blimp_engine_picture_cache_unittest.cc", "renderer/blimp_engine_picture_cache_unittest.cc",
"renderer/blob_channel_sender_proxy_unittest.cc",
] ]
deps = [ deps = [
":renderer", ":renderer",
"//base",
"//blimp/common",
"//blimp/common:test_support",
"//blimp/engine:blob_channel_mojo_cpp_sources",
"//blimp/engine:blob_channel_service",
"//blimp/net:test_support",
"//blimp/test:support", "//blimp/test:support",
"//mojo/public",
"//skia", "//skia",
"//testing/gmock", "//testing/gmock",
"//testing/gtest", "//testing/gtest",
......
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
...@@ -4,11 +4,17 @@ ...@@ -4,11 +4,17 @@
module blimp.engine.mojom; module blimp.engine.mojom;
// Exposes BlobChannel data producer methods to the renderer processes.
// The renderer can use this service to push bulk data such as images to
// the client.
interface BlobChannel { interface BlobChannel {
// Stores the blob |id| in the BlobCache. // Stores the blob |id| in the BlobCache.
// TODO(kmarshall): Use shared memory for more efficient data transfer, see // Because the IPC channel is a shared resource and payloads can be quite
// crbug.com/614564 . // large, we use shared memory to reduce channel contention and associated
PutBlob(string id, string data); // latency with other components/features.
//
// The buffer is released when the callee discards the handle for |data|.
PutBlob(string id, handle<shared_buffer> data, uint32 size);
// Requests that the BlobChannel push the blob |id| over the wire. // Requests that the BlobChannel push the blob |id| over the wire.
// The request will be ignored if the BlobChannel knows that // The request will be ignored if the BlobChannel knows that
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
#include "blimp/engine/mojo/blob_channel_service.h" #include "blimp/engine/mojo/blob_channel_service.h"
#include <utility>
#include "blimp/net/blob_channel/blob_channel_sender.h" #include "blimp/net/blob_channel/blob_channel_sender.h"
#include "mojo/public/cpp/system/buffer.h"
namespace blimp { namespace blimp {
namespace engine { namespace engine {
...@@ -19,8 +22,25 @@ BlobChannelService::BlobChannelService(BlobChannelSender* blob_channel_sender, ...@@ -19,8 +22,25 @@ BlobChannelService::BlobChannelService(BlobChannelSender* blob_channel_sender,
BlobChannelService::~BlobChannelService() {} BlobChannelService::~BlobChannelService() {}
void BlobChannelService::PutBlob(const mojo::String& id, void BlobChannelService::PutBlob(const mojo::String& id,
const mojo::String& data) { mojo::ScopedSharedBufferHandle data,
blob_channel_sender_->PutBlob(id, new BlobData(data)); uint32_t size) {
// Map |data| into the address space and copy out its contents.
if (!data.is_valid()) {
LOG(ERROR) << "Invalid data handle received from renderer process.";
return;
}
if (size > kMaxBlobSizeBytes) {
LOG(ERROR) << "Blob size too big: " << size;
return;
}
mojo::ScopedSharedBufferMapping mapping = data->Map(size);
CHECK(mapping) << "Failed to mmap region of " << size << " bytes.";
scoped_refptr<BlobData> new_blob(new BlobData);
new_blob->data.assign(reinterpret_cast<const char*>(mapping.get()), size);
blob_channel_sender_->PutBlob(id, std::move(new_blob));
} }
void BlobChannelService::DeliverBlob(const mojo::String& id) { void BlobChannelService::DeliverBlob(const mojo::String& id) {
......
...@@ -31,7 +31,9 @@ class BlobChannelService : public mojom::BlobChannel { ...@@ -31,7 +31,9 @@ class BlobChannelService : public mojom::BlobChannel {
mojom::BlobChannelRequest request); mojom::BlobChannelRequest request);
// BlobChannel implementation. // BlobChannel implementation.
void PutBlob(const mojo::String& id, const mojo::String& data) override; void PutBlob(const mojo::String& id,
mojo::ScopedSharedBufferHandle data,
uint32_t size) override;
void DeliverBlob(const mojo::String& id) override; void DeliverBlob(const mojo::String& id) override;
// Binds |this| and its object lifetime to a Mojo connection. // Binds |this| and its object lifetime to a Mojo connection.
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
#include "blimp/engine/renderer/blob_channel_sender_proxy.h" #include "blimp/engine/renderer/blob_channel_sender_proxy.h"
#include <utility>
#include "blimp/common/blob_cache/id_util.h"
#include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_thread.h"
#include "services/shell/public/cpp/interface_provider.h" #include "services/shell/public/cpp/interface_provider.h"
...@@ -19,30 +22,85 @@ mojom::BlobChannelPtr GetConnectedBlobChannel() { ...@@ -19,30 +22,85 @@ mojom::BlobChannelPtr GetConnectedBlobChannel() {
return blob_channel_ptr; return blob_channel_ptr;
} }
// Manages the creation and lifetime of Mojo shared memory buffers for blobs.
class SharedMemoryBlob {
public:
explicit SharedMemoryBlob(BlobDataPtr data);
~SharedMemoryBlob();
mojo::ScopedSharedBufferHandle CreateRemoteHandle();
private:
mojo::ScopedSharedBufferHandle local_handle_;
DISALLOW_COPY_AND_ASSIGN(SharedMemoryBlob);
};
SharedMemoryBlob::SharedMemoryBlob(BlobDataPtr data) {
DCHECK_GE(kMaxBlobSizeBytes, data->data.size());
local_handle_ = mojo::SharedBufferHandle::Create(data->data.size());
DCHECK(local_handle_.is_valid());
mojo::ScopedSharedBufferMapping mapped =
local_handle_->Map(data->data.size());
DCHECK(mapped);
memcpy(mapped.get(), data->data.data(), data->data.size());
}
SharedMemoryBlob::~SharedMemoryBlob() {}
mojo::ScopedSharedBufferHandle SharedMemoryBlob::CreateRemoteHandle() {
mojo::ScopedSharedBufferHandle remote_handle =
local_handle_->Clone(mojo::SharedBufferHandle::AccessMode::READ_ONLY);
CHECK(remote_handle.is_valid())
<< "Mojo error when creating read-only buffer handle.";
return remote_handle;
}
} // namespace } // namespace
BlobChannelSenderProxy::BlobChannelSenderProxy() BlobChannelSenderProxy::BlobChannelSenderProxy()
: blob_channel_(GetConnectedBlobChannel()) {} : BlobChannelSenderProxy(GetConnectedBlobChannel()) {}
BlobChannelSenderProxy::~BlobChannelSenderProxy() {} BlobChannelSenderProxy::~BlobChannelSenderProxy() {}
BlobChannelSenderProxy::BlobChannelSenderProxy(
mojom::BlobChannelPtr blob_channel)
: blob_channel_(std::move(blob_channel)) {}
// static
std::unique_ptr<BlobChannelSenderProxy> BlobChannelSenderProxy::CreateForTest(
mojom::BlobChannelPtr blob_channel) {
return base::WrapUnique(new BlobChannelSenderProxy(std::move(blob_channel)));
}
bool BlobChannelSenderProxy::IsInEngineCache(const std::string& id) const { bool BlobChannelSenderProxy::IsInEngineCache(const std::string& id) const {
return replication_state_.find(id) != replication_state_.end(); return replication_state_.find(id) != replication_state_.end();
} }
bool BlobChannelSenderProxy::IsInClientCache(const std::string& id) const { bool BlobChannelSenderProxy::IsInClientCache(const std::string& id) const {
return replication_state_.find(id)->second; auto found = replication_state_.find(id);
return found != replication_state_.end() && found->second;
} }
void BlobChannelSenderProxy::PutBlob(const BlobId& id, BlobDataPtr data) { void BlobChannelSenderProxy::PutBlob(const BlobId& id, BlobDataPtr data) {
DCHECK(!IsInEngineCache(id)); DCHECK(!IsInEngineCache(id));
size_t size = data->data.size();
CHECK(!data->data.empty()) << "Zero length blob sent: " << BlobIdToString(id);
replication_state_[id] = false; replication_state_[id] = false;
blob_channel_->PutBlob(id, data->data); std::unique_ptr<SharedMemoryBlob> shared_mem_blob(
new SharedMemoryBlob(std::move(data)));
blob_channel_->PutBlob(id, shared_mem_blob->CreateRemoteHandle(), size);
} }
void BlobChannelSenderProxy::DeliverBlob(const std::string& id) { void BlobChannelSenderProxy::DeliverBlob(const std::string& id) {
DCHECK(!IsInClientCache(id)); DCHECK(IsInEngineCache(id)) << "Attempted to deliver an invalid blob: "
<< BlobIdToString(id);
DCHECK(!IsInClientCache(id)) << "Blob is already in the remote cache:"
<< BlobIdToString(id);
// We assume that the client will have the blob if we push it. // We assume that the client will have the blob if we push it.
// TODO(kmarshall): Revisit this assumption when asynchronous blob transport // TODO(kmarshall): Revisit this assumption when asynchronous blob transport
......
...@@ -21,8 +21,12 @@ namespace engine { ...@@ -21,8 +21,12 @@ namespace engine {
class BLIMP_COMMON_EXPORT BlobChannelSenderProxy : public BlobChannelSender { class BLIMP_COMMON_EXPORT BlobChannelSenderProxy : public BlobChannelSender {
public: public:
BlobChannelSenderProxy(); BlobChannelSenderProxy();
~BlobChannelSenderProxy() override; ~BlobChannelSenderProxy() override;
static std::unique_ptr<BlobChannelSenderProxy> CreateForTest(
mojom::BlobChannelPtr blob_channel);
// Returns true if the blob is known to exist within the Engine cache. // Returns true if the blob is known to exist within the Engine cache.
bool IsInEngineCache(const std::string& id) const; bool IsInEngineCache(const std::string& id) const;
...@@ -34,6 +38,9 @@ class BLIMP_COMMON_EXPORT BlobChannelSenderProxy : public BlobChannelSender { ...@@ -34,6 +38,9 @@ class BLIMP_COMMON_EXPORT BlobChannelSenderProxy : public BlobChannelSender {
void DeliverBlob(const BlobId& id) override; void DeliverBlob(const BlobId& id) override;
private: private:
// Testing constructor, used to supply a BlobChannel Mojo proxy directly.
explicit BlobChannelSenderProxy(mojom::BlobChannelPtr blob_channel);
// BlobChannel Mojo IPC stub, used for delivering blobs to the browser // BlobChannel Mojo IPC stub, used for delivering blobs to the browser
// process. // process.
mojom::BlobChannelPtr blob_channel_; mojom::BlobChannelPtr blob_channel_;
......
// Copyright 2016 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 "blimp/engine/renderer/blob_channel_sender_proxy.h"
#include <utility>
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "blimp/common/blob_cache/id_util.h"
#include "blimp/common/blob_cache/test_util.h"
#include "blimp/engine/mojo/blob_channel.mojom.h"
#include "blimp/engine/mojo/blob_channel_service.h"
#include "blimp/net/blob_channel/mock_blob_channel_sender.h"
#include "blimp/net/test_common.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blimp {
namespace engine {
namespace {
const char kBlobId[] = "foo";
const char kBlobPayload[] = "bar";
class BlobChannelSenderProxyTest : public testing::Test {
public:
BlobChannelSenderProxyTest() {}
void SetUp() override {
// Set up communication path from the Proxy to a sender mock:
// blob_channel_sender_proxy_ => (mojo) => mojo_service_impl_ =>
// mock_sender_;
mojom::BlobChannelPtr mojo_ptr;
BlobChannelService::Create(&mock_sender_, GetProxy(&mojo_ptr));
blob_channel_sender_proxy_ =
BlobChannelSenderProxy::CreateForTest(std::move(mojo_ptr));
}
protected:
void DeliverMessages() { base::RunLoop().RunUntilIdle(); }
const std::string blob_id_ = CalculateBlobId(kBlobId);
base::MessageLoop message_loop_;
std::unique_ptr<BlobChannelService> mojo_service_impl_;
MockBlobChannelSender mock_sender_;
std::unique_ptr<BlobChannelSenderProxy> blob_channel_sender_proxy_;
private:
DISALLOW_COPY_AND_ASSIGN(BlobChannelSenderProxyTest);
};
TEST_F(BlobChannelSenderProxyTest, PutBlob) {
EXPECT_CALL(mock_sender_,
PutBlob(blob_id_, BlobDataPtrEqualsString(kBlobPayload)));
blob_channel_sender_proxy_->PutBlob(blob_id_,
CreateBlobDataPtr(kBlobPayload));
DeliverMessages();
EXPECT_TRUE(blob_channel_sender_proxy_->IsInEngineCache(blob_id_));
EXPECT_FALSE(blob_channel_sender_proxy_->IsInClientCache(blob_id_));
}
TEST_F(BlobChannelSenderProxyTest, DeliverBlob) {
EXPECT_CALL(mock_sender_,
PutBlob(blob_id_, BlobDataPtrEqualsString(kBlobPayload)));
EXPECT_CALL(mock_sender_, DeliverBlob(blob_id_));
blob_channel_sender_proxy_->PutBlob(blob_id_,
CreateBlobDataPtr(kBlobPayload));
DeliverMessages();
EXPECT_TRUE(blob_channel_sender_proxy_->IsInEngineCache(blob_id_));
EXPECT_FALSE(blob_channel_sender_proxy_->IsInClientCache(blob_id_));
blob_channel_sender_proxy_->DeliverBlob(blob_id_);
DeliverMessages();
EXPECT_TRUE(blob_channel_sender_proxy_->IsInEngineCache(blob_id_));
EXPECT_TRUE(blob_channel_sender_proxy_->IsInClientCache(blob_id_));
}
} // namespace
} // namespace engine
} // namespace blimp
...@@ -93,16 +93,21 @@ source_set("test_support") { ...@@ -93,16 +93,21 @@ source_set("test_support") {
sources = [ sources = [
"blob_channel/mock_blob_channel_receiver.cc", "blob_channel/mock_blob_channel_receiver.cc",
"blob_channel/mock_blob_channel_receiver.h", "blob_channel/mock_blob_channel_receiver.h",
"blob_channel/mock_blob_channel_sender.cc",
"blob_channel/mock_blob_channel_sender.h",
"test_common.cc", "test_common.cc",
"test_common.h", "test_common.h",
] ]
deps = [ deps = [
":net", ":net",
"//blimp/common/proto",
"//net:test_support", "//net:test_support",
"//testing/gmock", "//testing/gmock",
] ]
public_deps = [
"//blimp/common/proto",
]
} }
source_set("unit_tests") { source_set("unit_tests") {
...@@ -138,7 +143,6 @@ source_set("unit_tests") { ...@@ -138,7 +143,6 @@ source_set("unit_tests") {
":net", ":net",
":test_support", ":test_support",
"//base", "//base",
"//base/test:run_all_unittests",
"//base/test:test_support", "//base/test:test_support",
"//blimp/common", "//blimp/common",
"//blimp/common:test_support", "//blimp/common:test_support",
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
namespace blimp { namespace blimp {
// Blob size upper limit, for abuse prevention.
const size_t kMaxBlobSizeBytes = 10 * 1024 * 1024;
class BLIMP_NET_EXPORT BlobChannelSender { class BLIMP_NET_EXPORT BlobChannelSender {
public: public:
virtual ~BlobChannelSender() {} virtual ~BlobChannelSender() {}
......
// Copyright 2016 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 "blimp/net/blob_channel/mock_blob_channel_sender.h"
namespace blimp {
MockBlobChannelSender::MockBlobChannelSender() {}
MockBlobChannelSender::~MockBlobChannelSender() {}
} // namespace blimp
// Copyright 2016 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 BLIMP_NET_BLOB_CHANNEL_MOCK_BLOB_CHANNEL_SENDER_H_
#define BLIMP_NET_BLOB_CHANNEL_MOCK_BLOB_CHANNEL_SENDER_H_
#include <string>
#include <vector>
#include "blimp/net/blob_channel/blob_channel_sender.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace blimp {
class BLIMP_NET_EXPORT MockBlobChannelSender : public BlobChannelSender {
public:
MockBlobChannelSender();
~MockBlobChannelSender() override;
MOCK_METHOD2(PutBlob, void(const BlobId& id, BlobDataPtr data));
MOCK_METHOD1(DeliverBlob, void(const BlobId& id));
private:
DISALLOW_COPY_AND_ASSIGN(MockBlobChannelSender);
};
} // namespace blimp
#endif // BLIMP_NET_BLOB_CHANNEL_MOCK_BLOB_CHANNEL_SENDER_H_
...@@ -2,6 +2,21 @@ ...@@ -2,6 +2,21 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import("//testing/test.gni")
source_set("run_all_unittests") {
testonly = true
sources = [
"run_all_unittests.cc",
]
deps = [
"//base/test:test_support",
"//mojo/edk/system",
]
}
source_set("support") { source_set("support") {
testonly = true testonly = true
......
...@@ -3,6 +3,7 @@ include_rules = [ ...@@ -3,6 +3,7 @@ include_rules = [
"+cc", "+cc",
"-chrome", "-chrome",
"+content/public", "+content/public",
"+mojo/edk/embedder",
"+net", "+net",
"+ui/gfx", "+ui/gfx",
] ]
// Copyright (c) 2012 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 "base/bind.h"
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
#include "build/build_config.h"
#include "mojo/edk/embedder/embedder.h"
#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
#include "base/test/test_file_util.h"
#endif
int main(int argc, char** argv) {
#if defined(OS_ANDROID)
JNIEnv* env = base::android::AttachCurrentThread();
base::RegisterContentUriTestUtils(env);
#endif
mojo::edk::Init();
base::TestSuite test_suite(argc, argv);
return base::LaunchUnitTests(
argc, argv,
base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
}
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