Commit 8654b593 authored by Tsuyoshi Horo's avatar Tsuyoshi Horo Committed by Commit Bot

Fix memory leak in BlobRegistryImpl

Bug: 954458
Change-Id: I9980361d8b97c6de90d2784b229630cdabfbf0cd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1575055
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
Reviewed-by: default avatarMarijn Kruisselbrink <mek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#652802}
parent 4e8baad3
...@@ -305,9 +305,6 @@ BlobBuilderFromStream::BlobBuilderFromStream( ...@@ -305,9 +305,6 @@ BlobBuilderFromStream::BlobBuilderFromStream(
base::WeakPtr<BlobStorageContext> context, base::WeakPtr<BlobStorageContext> context,
std::string content_type, std::string content_type,
std::string content_disposition, std::string content_disposition,
uint64_t length_hint,
mojo::ScopedDataPipeConsumerHandle data,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
ResultCallback callback) ResultCallback callback)
: kMemoryBlockSize(std::min( : kMemoryBlockSize(std::min(
kMaxMemoryChunkSize, kMaxMemoryChunkSize,
...@@ -322,17 +319,22 @@ BlobBuilderFromStream::BlobBuilderFromStream( ...@@ -322,17 +319,22 @@ BlobBuilderFromStream::BlobBuilderFromStream(
content_disposition_(std::move(content_disposition)), content_disposition_(std::move(content_disposition)),
weak_factory_(this) { weak_factory_(this) {
DCHECK(context_); DCHECK(context_);
}
BlobBuilderFromStream::~BlobBuilderFromStream() {
DCHECK(!callback_) << "BlobBuilderFromStream was destroyed before finishing";
}
void BlobBuilderFromStream::Start(
uint64_t length_hint,
mojo::ScopedDataPipeConsumerHandle data,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client) {
context_->mutable_memory_controller()->CallWhenStorageLimitsAreKnown( context_->mutable_memory_controller()->CallWhenStorageLimitsAreKnown(
base::BindOnce(&BlobBuilderFromStream::AllocateMoreMemorySpace, base::BindOnce(&BlobBuilderFromStream::AllocateMoreMemorySpace,
weak_factory_.GetWeakPtr(), length_hint, weak_factory_.GetWeakPtr(), length_hint,
std::move(progress_client), std::move(data))); std::move(progress_client), std::move(data)));
} }
BlobBuilderFromStream::~BlobBuilderFromStream() {
DCHECK(!callback_) << "BlobBuilderFromStream was destroyed before finishing";
}
void BlobBuilderFromStream::Abort() { void BlobBuilderFromStream::Abort() {
OnError(Result::kAborted); OnError(Result::kAborted);
} }
......
...@@ -55,12 +55,15 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobBuilderFromStream { ...@@ -55,12 +55,15 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobBuilderFromStream {
base::WeakPtr<BlobStorageContext> context, base::WeakPtr<BlobStorageContext> context,
std::string content_type, std::string content_type,
std::string content_disposition, std::string content_disposition,
uint64_t length_hint,
mojo::ScopedDataPipeConsumerHandle data,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
ResultCallback callback); ResultCallback callback);
~BlobBuilderFromStream(); ~BlobBuilderFromStream();
// This may call |callback| synchronously when |length_hint| is larger than
// the disk space.
void Start(uint64_t length_hint,
mojo::ScopedDataPipeConsumerHandle data,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client);
void Abort(); void Abort();
private: private:
...@@ -148,6 +151,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobBuilderFromStream { ...@@ -148,6 +151,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobBuilderFromStream {
std::string content_type_; std::string content_type_;
std::string content_disposition_; std::string content_disposition_;
std::vector<scoped_refptr<ShareableBlobDataItem>> items_; std::vector<scoped_refptr<ShareableBlobDataItem>> items_;
uint64_t current_total_size_ = 0; uint64_t current_total_size_ = 0;
base::WeakPtr<BlobMemoryController::QuotaAllocationTask> pending_quota_task_; base::WeakPtr<BlobMemoryController::QuotaAllocationTask> pending_quota_task_;
......
...@@ -94,14 +94,14 @@ class BlobBuilderFromStreamTestWithDelayedLimits ...@@ -94,14 +94,14 @@ class BlobBuilderFromStreamTestWithDelayedLimits
uint64_t length_hint = GetLengthHint(data.length()); uint64_t length_hint = GetLengthHint(data.length());
BlobBuilderFromStream* finished_builder = nullptr; BlobBuilderFromStream* finished_builder = nullptr;
BlobBuilderFromStream builder( BlobBuilderFromStream builder(
context_->AsWeakPtr(), kContentType, kContentDisposition, length_hint, context_->AsWeakPtr(), kContentType, kContentDisposition,
std::move(pipe.consumer_handle), nullptr,
base::BindLambdaForTesting([&](BlobBuilderFromStream* result_builder, base::BindLambdaForTesting([&](BlobBuilderFromStream* result_builder,
std::unique_ptr<BlobDataHandle> blob) { std::unique_ptr<BlobDataHandle> blob) {
finished_builder = result_builder; finished_builder = result_builder;
result = std::move(blob); result = std::move(blob);
loop.Quit(); loop.Quit();
})); }));
builder.Start(length_hint, std::move(pipe.consumer_handle), nullptr);
// Make sure the initial memory allocation done by the builder matches the // Make sure the initial memory allocation done by the builder matches the
// length hint passed in. // length hint passed in.
...@@ -195,8 +195,7 @@ TEST_P(BlobBuilderFromStreamTest, CallbackCalledOnAbortBeforeDeletion) { ...@@ -195,8 +195,7 @@ TEST_P(BlobBuilderFromStreamTest, CallbackCalledOnAbortBeforeDeletion) {
base::RunLoop loop; base::RunLoop loop;
BlobBuilderFromStream* builder_ptr = nullptr; BlobBuilderFromStream* builder_ptr = nullptr;
auto builder = std::make_unique<BlobBuilderFromStream>( auto builder = std::make_unique<BlobBuilderFromStream>(
context_->AsWeakPtr(), "", "", GetLengthHint(16), context_->AsWeakPtr(), "", "",
std::move(pipe.consumer_handle), nullptr,
base::BindLambdaForTesting([&](BlobBuilderFromStream* result_builder, base::BindLambdaForTesting([&](BlobBuilderFromStream* result_builder,
std::unique_ptr<BlobDataHandle> blob) { std::unique_ptr<BlobDataHandle> blob) {
EXPECT_EQ(builder_ptr, result_builder); EXPECT_EQ(builder_ptr, result_builder);
...@@ -204,6 +203,7 @@ TEST_P(BlobBuilderFromStreamTest, CallbackCalledOnAbortBeforeDeletion) { ...@@ -204,6 +203,7 @@ TEST_P(BlobBuilderFromStreamTest, CallbackCalledOnAbortBeforeDeletion) {
loop.Quit(); loop.Quit();
})); }));
builder_ptr = builder.get(); builder_ptr = builder.get();
builder->Start(GetLengthHint(16), std::move(pipe.consumer_handle), nullptr);
builder->Abort(); builder->Abort();
builder.reset(); builder.reset();
loop.Run(); loop.Run();
...@@ -361,13 +361,13 @@ TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuota) { ...@@ -361,13 +361,13 @@ TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuota) {
base::RunLoop loop; base::RunLoop loop;
std::unique_ptr<BlobDataHandle> result; std::unique_ptr<BlobDataHandle> result;
BlobBuilderFromStream builder( BlobBuilderFromStream builder(
context_->AsWeakPtr(), "", "", kLengthHint, context_->AsWeakPtr(), "", "",
std::move(pipe.consumer_handle), nullptr,
base::BindLambdaForTesting( base::BindLambdaForTesting(
[&](BlobBuilderFromStream*, std::unique_ptr<BlobDataHandle> blob) { [&](BlobBuilderFromStream*, std::unique_ptr<BlobDataHandle> blob) {
result = std::move(blob); result = std::move(blob);
loop.Quit(); loop.Quit();
})); }));
builder.Start(kLengthHint, std::move(pipe.consumer_handle), nullptr);
pipe.producer_handle.reset(); pipe.producer_handle.reset();
loop.Run(); loop.Run();
...@@ -384,13 +384,13 @@ TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuotaAndNoDisk) { ...@@ -384,13 +384,13 @@ TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuotaAndNoDisk) {
base::RunLoop loop; base::RunLoop loop;
std::unique_ptr<BlobDataHandle> result; std::unique_ptr<BlobDataHandle> result;
BlobBuilderFromStream builder( BlobBuilderFromStream builder(
context_->AsWeakPtr(), "", "", kLengthHint, context_->AsWeakPtr(), "", "",
std::move(pipe.consumer_handle), nullptr,
base::BindLambdaForTesting( base::BindLambdaForTesting(
[&](BlobBuilderFromStream*, std::unique_ptr<BlobDataHandle> blob) { [&](BlobBuilderFromStream*, std::unique_ptr<BlobDataHandle> blob) {
result = std::move(blob); result = std::move(blob);
loop.Quit(); loop.Quit();
})); }));
builder.Start(kLengthHint, std::move(pipe.consumer_handle), nullptr);
pipe.producer_handle.reset(); pipe.producer_handle.reset();
loop.Run(); loop.Run();
...@@ -413,13 +413,14 @@ TEST_P(BlobBuilderFromStreamTest, ProgressEvents) { ...@@ -413,13 +413,14 @@ TEST_P(BlobBuilderFromStreamTest, ProgressEvents) {
base::RunLoop loop; base::RunLoop loop;
std::unique_ptr<BlobDataHandle> result; std::unique_ptr<BlobDataHandle> result;
BlobBuilderFromStream builder( BlobBuilderFromStream builder(
context_->AsWeakPtr(), "", "", GetLengthHint(kData.size()), context_->AsWeakPtr(), "", "",
std::move(pipe.consumer_handle), progress_client_ptr.PassInterface(),
base::BindLambdaForTesting( base::BindLambdaForTesting(
[&](BlobBuilderFromStream*, std::unique_ptr<BlobDataHandle> blob) { [&](BlobBuilderFromStream*, std::unique_ptr<BlobDataHandle> blob) {
result = std::move(blob); result = std::move(blob);
loop.Quit(); loop.Quit();
})); }));
builder.Start(GetLengthHint(kData.size()), std::move(pipe.consumer_handle),
progress_client_ptr.PassInterface());
mojo::BlockingCopyFromString(kData, pipe.producer_handle); mojo::BlockingCopyFromString(kData, pipe.producer_handle);
pipe.producer_handle.reset(); pipe.producer_handle.reset();
...@@ -447,13 +448,13 @@ TEST_F(BlobBuilderFromStreamTestWithDelayedLimits, LargeStream) { ...@@ -447,13 +448,13 @@ TEST_F(BlobBuilderFromStreamTestWithDelayedLimits, LargeStream) {
base::RunLoop loop; base::RunLoop loop;
std::unique_ptr<BlobDataHandle> result; std::unique_ptr<BlobDataHandle> result;
BlobBuilderFromStream builder( BlobBuilderFromStream builder(
context_->AsWeakPtr(), kContentType, kContentDisposition, kData.size(), context_->AsWeakPtr(), kContentType, kContentDisposition,
std::move(pipe.consumer_handle), nullptr,
base::BindLambdaForTesting([&](BlobBuilderFromStream* result_builder, base::BindLambdaForTesting([&](BlobBuilderFromStream* result_builder,
std::unique_ptr<BlobDataHandle> blob) { std::unique_ptr<BlobDataHandle> blob) {
result = std::move(blob); result = std::move(blob);
loop.Quit(); loop.Quit();
})); }));
builder.Start(kData.size(), std::move(pipe.consumer_handle), nullptr);
context_->set_limits_for_testing(limits_); context_->set_limits_for_testing(limits_);
auto data_producer = std::make_unique<mojo::StringDataPipeProducer>( auto data_producer = std::make_unique<mojo::StringDataPipeProducer>(
......
...@@ -567,11 +567,15 @@ void BlobRegistryImpl::RegisterFromStream( ...@@ -567,11 +567,15 @@ void BlobRegistryImpl::RegisterFromStream(
return; return;
} }
blobs_being_streamed_.insert(std::make_unique<BlobBuilderFromStream>( std::unique_ptr<BlobBuilderFromStream> blob_builder =
context_, content_type, content_disposition, expected_length, std::make_unique<BlobBuilderFromStream>(
std::move(data), std::move(progress_client), context_, content_type, content_disposition,
base::BindOnce(&BlobRegistryImpl::StreamingBlobDone, base::BindOnce(&BlobRegistryImpl::StreamingBlobDone,
base::Unretained(this), std::move(callback)))); base::Unretained(this), std::move(callback)));
BlobBuilderFromStream* blob_builder_ptr = blob_builder.get();
blobs_being_streamed_.insert(std::move(blob_builder));
blob_builder_ptr->Start(expected_length, std::move(data),
std::move(progress_client));
} }
void BlobRegistryImpl::GetBlobFromUUID(blink::mojom::BlobRequest blob, void BlobRegistryImpl::GetBlobFromUUID(blink::mojom::BlobRequest blob,
......
...@@ -67,6 +67,10 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobRegistryImpl ...@@ -67,6 +67,10 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobRegistryImpl
return blobs_under_construction_.size(); return blobs_under_construction_.size();
} }
size_t BlobsBeingStreamedForTesting() const {
return blobs_being_streamed_.size();
}
using URLStoreCreationHook = base::RepeatingCallback<void( using URLStoreCreationHook = base::RepeatingCallback<void(
mojo::StrongAssociatedBindingPtr<blink::mojom::BlobURLStore>)>; mojo::StrongAssociatedBindingPtr<blink::mojom::BlobURLStore>)>;
static void SetURLStoreCreationHookForTesting(URLStoreCreationHook* hook); static void SetURLStoreCreationHookForTesting(URLStoreCreationHook* hook);
......
...@@ -171,6 +171,10 @@ class BlobRegistryImplTest : public testing::Test { ...@@ -171,6 +171,10 @@ class BlobRegistryImplTest : public testing::Test {
return registry_impl_->BlobsUnderConstructionForTesting(); return registry_impl_->BlobsUnderConstructionForTesting();
} }
size_t BlobsBeingStreamed() {
return registry_impl_->BlobsBeingStreamedForTesting();
}
protected: protected:
base::ScopedTempDir data_dir_; base::ScopedTempDir data_dir_;
base::test::ScopedTaskEnvironment scoped_task_environment_; base::test::ScopedTaskEnvironment scoped_task_environment_;
...@@ -1014,18 +1018,20 @@ TEST_F(BlobRegistryImplTest, RegisterFromStream) { ...@@ -1014,18 +1018,20 @@ TEST_F(BlobRegistryImplTest, RegisterFromStream) {
mojo::AssociatedBinding<blink::mojom::ProgressClient> progress_binding( mojo::AssociatedBinding<blink::mojom::ProgressClient> progress_binding(
&progress_client, MakeRequest(&progress_client_ptr)); &progress_client, MakeRequest(&progress_client_ptr));
mojo::DataPipe pipe; mojo::ScopedDataPipeProducerHandle producer;
mojo::ScopedDataPipeConsumerHandle consumer;
mojo::CreateDataPipe(nullptr, &producer, &consumer);
blink::mojom::SerializedBlobPtr blob; blink::mojom::SerializedBlobPtr blob;
base::RunLoop loop; base::RunLoop loop;
registry_->RegisterFromStream( registry_->RegisterFromStream(
kContentType, kContentDisposition, kData.length(), kContentType, kContentDisposition, kData.length(), std::move(consumer),
std::move(pipe.consumer_handle), std::move(progress_client_ptr), std::move(progress_client_ptr),
base::BindLambdaForTesting([&](blink::mojom::SerializedBlobPtr result) { base::BindLambdaForTesting([&](blink::mojom::SerializedBlobPtr result) {
blob = std::move(result); blob = std::move(result);
loop.Quit(); loop.Quit();
})); }));
mojo::BlockingCopyFromString(kData, pipe.producer_handle); mojo::BlockingCopyFromString(kData, producer);
pipe.producer_handle.reset(); producer.reset();
loop.Run(); loop.Run();
ASSERT_TRUE(blob); ASSERT_TRUE(blob);
...@@ -1038,6 +1044,39 @@ TEST_F(BlobRegistryImplTest, RegisterFromStream) { ...@@ -1038,6 +1044,39 @@ TEST_F(BlobRegistryImplTest, RegisterFromStream) {
EXPECT_EQ(kData.length(), progress_client.total_size); EXPECT_EQ(kData.length(), progress_client.total_size);
EXPECT_GE(progress_client.call_count, 1); EXPECT_GE(progress_client.call_count, 1);
EXPECT_EQ(0u, BlobsBeingStreamed());
}
TEST_F(BlobRegistryImplTest, RegisterFromStream_NoDiskSpace) {
const std::string kData =
base::RandBytesAsString(kTestBlobStorageMaxDiskSpace + 1);
const std::string kContentType = "content/type";
const std::string kContentDisposition = "disposition";
FakeProgressClient progress_client;
blink::mojom::ProgressClientAssociatedPtrInfo progress_client_ptr;
mojo::AssociatedBinding<blink::mojom::ProgressClient> progress_binding(
&progress_client, MakeRequest(&progress_client_ptr));
mojo::ScopedDataPipeProducerHandle producer;
mojo::ScopedDataPipeConsumerHandle consumer;
mojo::CreateDataPipe(nullptr, &producer, &consumer);
blink::mojom::SerializedBlobPtr blob;
base::RunLoop loop;
registry_->RegisterFromStream(
kContentType, kContentDisposition, kData.length(), std::move(consumer),
std::move(progress_client_ptr),
base::BindLambdaForTesting([&](blink::mojom::SerializedBlobPtr result) {
blob = std::move(result);
loop.Quit();
}));
mojo::BlockingCopyFromString(kData, producer);
producer.reset();
loop.Run();
EXPECT_FALSE(blob);
EXPECT_EQ(0u, BlobsBeingStreamed());
} }
TEST_F(BlobRegistryImplTest, DestroyWithUnfinishedStream) { TEST_F(BlobRegistryImplTest, DestroyWithUnfinishedStream) {
......
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