Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(135)

Unified Diff: storage/browser/blob/blob_transport_host.cc

Issue 2448353002: [BlobAsync] Moving async handling into BlobStorageContext & quota out. (Closed)
Patch Set: comments from Marijn Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: storage/browser/blob/blob_transport_host.cc
diff --git a/storage/browser/blob/blob_transport_host.cc b/storage/browser/blob/blob_transport_host.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d5998e29b01b37f887926801b4dcfa17abb30de8
--- /dev/null
+++ b/storage/browser/blob/blob_transport_host.cc
@@ -0,0 +1,513 @@
+// Copyright 2015 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/shared_memory.h"
+#include "storage/browser/blob/blob_data_handle.h"
+#include "storage/browser/blob/blob_memory_controller.h"
+#include "storage/browser/blob/blob_storage_context.h"
+#include "storage/browser/blob/blob_transport_host.h"
+
+namespace storage {
+namespace {
+using MemoryStrategy = BlobMemoryController::Strategy;
+using MemoryItemRequest =
+ BlobTransportRequestBuilder::RendererMemoryItemRequest;
+
+bool CalculateBlobMemorySize(const std::vector<DataElement>& elements,
+ size_t* shortcut_bytes,
+ uint64_t* total_bytes) {
+ DCHECK(shortcut_bytes);
+ DCHECK(total_bytes);
+
+ base::CheckedNumeric<uint64_t> total_size_checked = 0;
+ base::CheckedNumeric<size_t> shortcut_size_checked = 0;
+ for (const auto& e : elements) {
+ if (e.type() == DataElement::TYPE_BYTES) {
+ total_size_checked += e.length();
+ shortcut_size_checked += e.length();
+ } else if (e.type() == DataElement::TYPE_BYTES_DESCRIPTION) {
+ total_size_checked += e.length();
+ } else {
+ continue;
+ }
+ if (!total_size_checked.IsValid() || !shortcut_size_checked.IsValid())
+ return false;
+ }
+ *shortcut_bytes = shortcut_size_checked.ValueOrDie();
+ *total_bytes = total_size_checked.ValueOrDie();
+ return true;
+}
+} // namespace
+
+BlobTransportHost::TransportState::TransportState(
+ const std::string& uuid,
+ const std::string& content_type,
+ const std::string& content_disposition,
+ RequestMemoryCallback request_memory_callback,
+ BlobStatusCallback completion_callback)
+ : data_builder(uuid),
+ request_memory_callback(std::move(request_memory_callback)),
+ completion_callback(std::move(completion_callback)) {
+ data_builder.set_content_type(content_type);
+ data_builder.set_content_disposition(content_disposition);
+}
+
+BlobTransportHost::TransportState::~TransportState() {}
+
+BlobTransportHost::BlobTransportHost() : ptr_factory_(this) {}
+
+BlobTransportHost::~BlobTransportHost() {}
+
+void BlobTransportHost::StartBuildingBlob(
+ const std::string& uuid,
+ const std::string& content_type,
+ const std::string& content_disposition,
+ const std::vector<DataElement>& elements,
+ BlobStorageContext* context,
+ const RequestMemoryCallback& request_memory,
+ const BlobStatusCallback& completion_callback) {
+ DCHECK(context);
+ if (async_blob_map_.find(uuid) != async_blob_map_.end()) {
+ completion_callback.Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
+ return;
+ }
+ // Validate that our referenced blobs aren't us.
+ for (const DataElement& e : elements) {
+ if (e.type() == DataElement::TYPE_BLOB && e.blob_uuid() == uuid) {
+ completion_callback.Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
+ return;
+ }
+ }
+ uint64_t transport_memory_size = 0;
+ size_t shortcut_size = 0;
+ if (!CalculateBlobMemorySize(elements, &shortcut_size,
+ &transport_memory_size)) {
+ completion_callback.Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
+ return;
+ }
+
+ const BlobMemoryController& memory_controller = context->memory_controller();
+ MemoryStrategy memory_strategy =
+ memory_controller.DetermineStrategy(shortcut_size, transport_memory_size);
+ std::unique_ptr<TransportState> state(
+ new TransportState(uuid, content_type, content_disposition,
+ request_memory, completion_callback));
+ std::unique_ptr<BlobDataHandle> temp_handle;
+ base::ScopedClosureRunner increment_runner(
+ base::Bind(&BlobStorageContext::IncrementBlobRefCount,
+ base::Unretained(context), uuid));
+ switch (memory_strategy) {
+ case MemoryStrategy::TOO_LARGE:
+ temp_handle =
+ context->AddBrokenBlob(uuid, content_type, content_disposition,
+ BlobStatus::ERR_OUT_OF_MEMORY);
+ completion_callback.Run(BlobStatus::ERR_OUT_OF_MEMORY);
+ return;
+ case MemoryStrategy::NONE_NEEDED: {
+ for (const DataElement& e : elements) {
+ DCHECK_NE(e.type(), DataElement::TYPE_BYTES_DESCRIPTION);
+ state->data_builder.AppendIPCDataElement(e);
+ }
+ temp_handle = context->BuildBlob(
+ state->data_builder, BlobStorageContext::TransportAllowedCallback());
+ completion_callback.Run(BlobStatus::DONE);
kinuko 2016/11/10 05:16:37 BuildBlob could result in a broken blob, is callin
dmurph 2016/11/10 19:53:20 We don't need transportation, so we're done with t
+ return;
+ }
+ case MemoryStrategy::IPC:
+ state->strategy = IPCBlobItemRequestStrategy::IPC;
+ state->request_builder.InitializeForIPCRequests(
+ memory_controller.limits().max_ipc_memory_size, transport_memory_size,
+ elements, &(state->data_builder));
+ break;
+ case MemoryStrategy::SHARED_MEMORY:
+ state->strategy = IPCBlobItemRequestStrategy::SHARED_MEMORY;
+ state->request_builder.InitializeForSharedMemoryRequests(
+ memory_controller.limits().max_shared_memory_size,
+ transport_memory_size, elements, &(state->data_builder));
+ break;
+ case MemoryStrategy::FILE:
+ state->strategy = IPCBlobItemRequestStrategy::FILE;
+ state->request_builder.InitializeForFileRequests(
+ memory_controller.limits().max_file_size, transport_memory_size,
+ elements, &(state->data_builder));
+ break;
+ }
+ // We initialize our requests received state now that they are populated.
+ state->request_received.resize(state->request_builder.requests().size(),
+ false);
+ TransportState* state_ptr = state.get();
+ async_blob_map_[uuid] = std::move(state);
+ temp_handle = context->BuildBlob(
+ state_ptr->data_builder,
+ base::Bind(&BlobTransportHost::OnReadyForTransport,
+ ptr_factory_.GetWeakPtr(), uuid, context->AsWeakPtr()));
+ BlobStatus status = temp_handle->GetBlobStatus();
+ if (BlobStatusIsError(status))
+ async_blob_map_.erase(uuid);
+
+ if (!BlobStatusIsPending(status))
+ completion_callback.Run(status);
kinuko 2016/11/10 05:16:37 BuildBlob could return INVALID_CONSTRUCTION_ARGUME
dmurph 2016/11/10 19:53:20 Fixed. We now correctly remove the blob if it's a
+}
+
+BlobStatus BlobTransportHost::OnMemoryResponses(
+ const std::string& uuid,
+ const std::vector<BlobItemBytesResponse>& responses,
+ BlobStorageContext* context) {
+ AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid);
+ if (state_it == async_blob_map_.end()) {
+ DVLOG(1) << "Could not find blob " << uuid;
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+ }
+ if (responses.empty()) {
+ CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+ context);
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+ }
+
+ // Validate response sanity: it should refer to a legal request number, and
+ // we shouldn't have received an answer for that request yet.
+ BlobTransportHost::TransportState* state = state_it->second.get();
+ const auto& requests = state->request_builder.requests();
+ for (const BlobItemBytesResponse& response : responses) {
+ if (response.request_number >= requests.size()) {
+ // Bad IPC, so we delete our record and ignore.
+ DVLOG(1) << "Invalid request number " << response.request_number;
+ CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+ context);
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+ }
+ DCHECK_LT(response.request_number, state->request_received.size());
+ if (state->request_received[response.request_number]) {
+ // Bad IPC, so we delete our record.
+ DVLOG(1) << "Already received response for that request.";
+ CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+ context);
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+ }
+ state->request_received[response.request_number] = true;
+ }
+ switch (state->strategy) {
+ case IPCBlobItemRequestStrategy::IPC:
+ return OnIPCResponses(uuid, state, responses, context);
+ case IPCBlobItemRequestStrategy::SHARED_MEMORY:
+ return OnSharedMemoryResponses(uuid, state, responses, context);
+ case IPCBlobItemRequestStrategy::FILE:
+ return OnFileResponses(uuid, state, responses, context);
+ case IPCBlobItemRequestStrategy::UNKNOWN:
+ break;
+ }
+ NOTREACHED();
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+}
+
+void BlobTransportHost::CancelBuildingBlob(const std::string& uuid,
+ BlobStatus code,
+ BlobStorageContext* context) {
+ DCHECK(context);
+ DCHECK(BlobStatusIsError(code));
+ auto state_it = async_blob_map_.find(uuid);
+ if (state_it == async_blob_map_.end())
+ return;
+ // We can have the blob dereferenced by the renderer, but have it still being
+ // 'built'. In this case, it's destructed in the context, but we still have
+ // it in our map. Hence we make sure the context has the entry before
+ // calling cancel.
+ async_blob_map_.erase(state_it);
+ if (context->registry().HasEntry(uuid))
+ context->CancelBuildingBlob(uuid, code);
+}
+
+void BlobTransportHost::CancelAll(BlobStorageContext* context) {
+ DCHECK(context);
+ // If the blob still exists in the context, then we know that someone else is
+ // expecting our blob, and we need to cancel it to let the dependency know
+ // it's gone.
+ std::vector<std::unique_ptr<BlobDataHandle>> referenced_pending_blobs;
+ for (const auto& uuid_state_pair : async_blob_map_) {
+ std::unique_ptr<BlobDataHandle> handle =
+ context->GetBlobDataFromUUID(uuid_state_pair.first);
+ if (handle) {
+ referenced_pending_blobs.push_back(
+ context->GetBlobDataFromUUID(uuid_state_pair.first));
+ }
+ }
+ // We clear the map before canceling them to prevent any strange reentry into
+ // our class (see OnReadyForTransport) if any blobs were waiting for others
+ // to construct.
+ async_blob_map_.clear();
+ for (const std::unique_ptr<BlobDataHandle>& handle :
+ referenced_pending_blobs) {
+ context->CancelBuildingBlob(handle->uuid(),
+ BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT);
+ }
+}
+
+BlobStatus BlobTransportHost::StartRequests(
+ const std::string& uuid,
+ TransportState* state,
+ BlobStorageContext* context,
+ std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
+ switch (state->strategy) {
+ case IPCBlobItemRequestStrategy::IPC:
+ DCHECK(file_infos.empty());
+ SendIPCRequests(state, context);
+ return BlobStatus::PENDING_TRANSPORT;
+ case IPCBlobItemRequestStrategy::SHARED_MEMORY:
+ DCHECK(file_infos.empty());
+ return ContinueSharedMemoryRequests(uuid, state, context);
+ case IPCBlobItemRequestStrategy::FILE:
+ DCHECK(!file_infos.empty());
+ SendFileRequests(state, context, std::move(file_infos));
+ return BlobStatus::PENDING_TRANSPORT;
+ case IPCBlobItemRequestStrategy::UNKNOWN:
+ break;
+ }
+ NOTREACHED();
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+}
+
+// Note: This can be called when we cancel a blob in the context.
+void BlobTransportHost::OnReadyForTransport(
+ const std::string& uuid,
+ base::WeakPtr<BlobStorageContext> context,
+ BlobStatus status,
+ std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
+ if (!context) {
+ async_blob_map_.erase(uuid);
+ return;
+ }
+ AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid);
+ if (state_it == async_blob_map_.end())
+ return;
+
+ TransportState* state = state_it->second.get();
+ if (BlobStatusIsPending(status)) {
+ DCHECK(status == BlobStatus::PENDING_TRANSPORT);
+ status = StartRequests(uuid, state, context.get(), std::move(file_infos));
+ if (BlobStatusIsPending(status))
+ return;
+ }
+ BlobStatusCallback completion_callback = state->completion_callback;
+ async_blob_map_.erase(state_it);
+ completion_callback.Run(status);
+}
+
+void BlobTransportHost::SendIPCRequests(TransportState* state,
+ BlobStorageContext* context) {
+ const std::vector<MemoryItemRequest>& requests =
+ state->request_builder.requests();
+ std::vector<BlobItemBytesRequest> byte_requests;
+
+ DCHECK(!requests.empty());
+ for (const MemoryItemRequest& request : requests) {
+ byte_requests.push_back(request.message);
+ }
+
+ state->request_memory_callback.Run(std::move(byte_requests),
+ std::vector<base::SharedMemoryHandle>(),
+ std::vector<base::File>());
+}
+
+BlobStatus BlobTransportHost::OnIPCResponses(
+ const std::string& uuid,
+ TransportState* state,
+ const std::vector<BlobItemBytesResponse>& responses,
+ BlobStorageContext* context) {
+ const auto& requests = state->request_builder.requests();
+ size_t num_requests = requests.size();
+ for (const BlobItemBytesResponse& response : responses) {
+ const MemoryItemRequest& request = requests[response.request_number];
+ if (response.inline_data.size() < request.message.size) {
+ DVLOG(1) << "Invalid data size " << response.inline_data.size()
+ << " vs requested size of " << request.message.size;
+ CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+ context);
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+ }
+ bool success = state->data_builder.PopulateFutureData(
+ request.browser_item_index, response.inline_data.data(),
+ request.browser_item_offset, request.message.size);
+ if (!success) {
+ CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+ context);
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+ }
+ state->num_fulfilled_requests++;
+ }
+ if (state->num_fulfilled_requests == num_requests) {
+ CompleteTransport(state, context);
+ return BlobStatus::DONE;
+ }
+ return BlobStatus::PENDING_TRANSPORT;
+}
+
+BlobStatus BlobTransportHost::ContinueSharedMemoryRequests(
+ const std::string& uuid,
+ TransportState* state,
+ BlobStorageContext* context) {
+ BlobTransportRequestBuilder& request_builder = state->request_builder;
+ const std::vector<MemoryItemRequest>& requests = request_builder.requests();
+ size_t num_requests = requests.size();
+ DCHECK_LT(state->num_fulfilled_requests, num_requests);
+ if (state->next_request == num_requests) {
+ // We are still waiting on other requests to come back.
+ return BlobStatus::PENDING_TRANSPORT;
+ }
+
+ std::vector<BlobItemBytesRequest> byte_requests;
+ std::vector<base::SharedMemoryHandle> shared_memory;
+
+ for (; state->next_request < num_requests; ++state->next_request) {
+ const MemoryItemRequest& request = requests[state->next_request];
+ bool using_shared_memory_handle = state->num_shared_memory_requests > 0;
+ if (using_shared_memory_handle &&
+ state->current_shared_memory_handle_index !=
+ request.message.handle_index) {
+ // We only want one shared memory per requesting blob.
+ break;
+ }
+ state->current_shared_memory_handle_index = request.message.handle_index;
+ state->num_shared_memory_requests++;
+
+ if (!state->shared_memory_block) {
+ state->shared_memory_block.reset(new base::SharedMemory());
+ size_t size =
+ request_builder.shared_memory_sizes()[request.message.handle_index];
+ if (!state->shared_memory_block->CreateAnonymous(size)) {
+ DVLOG(1) << "Unable to allocate shared memory for blob transfer.";
+ return BlobStatus::ERR_OUT_OF_MEMORY;
+ }
+ }
+ shared_memory.push_back(state->shared_memory_block->handle());
+ byte_requests.push_back(request.message);
+ // Since we are only using one handle at a time, transform our handle
+ // index correctly back to 0.
+ byte_requests.back().handle_index = 0;
+ }
+ DCHECK(!requests.empty());
+
+ state->request_memory_callback.Run(std::move(byte_requests),
+ std::move(shared_memory),
+ std::vector<base::File>());
+ return BlobStatus::PENDING_TRANSPORT;
+}
+
+BlobStatus BlobTransportHost::OnSharedMemoryResponses(
+ const std::string& uuid,
+ TransportState* state,
+ const std::vector<BlobItemBytesResponse>& responses,
+ BlobStorageContext* context) {
+ BlobTransportRequestBuilder& request_builder = state->request_builder;
+ const auto& requests = request_builder.requests();
+ for (const BlobItemBytesResponse& response : responses) {
+ const MemoryItemRequest& request = requests[response.request_number];
+ if (state->num_shared_memory_requests == 0) {
+ DVLOG(1) << "Received too many responses for shared memory.";
+ CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+ context);
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+ }
+ state->num_shared_memory_requests--;
+ if (!state->shared_memory_block->memory()) {
+ // We just map the whole block, as we'll probably be accessing the
+ // whole thing in this group of responses.
+ size_t handle_size =
+ request_builder
+ .shared_memory_sizes()[state->current_shared_memory_handle_index];
+ if (!state->shared_memory_block->Map(handle_size)) {
+ DVLOG(1) << "Unable to map memory to size " << handle_size;
+ CancelBuildingBlob(uuid, BlobStatus::ERR_OUT_OF_MEMORY, context);
+ return BlobStatus::ERR_OUT_OF_MEMORY;
+ }
+ }
+
+ bool success = state->data_builder.PopulateFutureData(
+ request.browser_item_index,
+ static_cast<const char*>(state->shared_memory_block->memory()) +
+ request.message.handle_offset,
+ request.browser_item_offset, request.message.size);
+
+ if (!success) {
+ CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+ context);
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+ }
+ state->num_fulfilled_requests++;
+ }
+ if (state->num_fulfilled_requests == requests.size()) {
+ CompleteTransport(state, context);
+ return BlobStatus::DONE;
+ }
+ return ContinueSharedMemoryRequests(uuid, state, context);
+}
+
+void BlobTransportHost::SendFileRequests(
+ TransportState* state,
+ BlobStorageContext* context,
+ std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
+ std::vector<base::File> files;
+
+ for (BlobMemoryController::FileCreationInfo& file_info : file_infos) {
+ state->files.push_back(std::move(file_info.file_reference));
+ files.push_back(std::move(file_info.file));
+ }
+
+ const std::vector<MemoryItemRequest>& requests =
+ state->request_builder.requests();
+ std::vector<BlobItemBytesRequest> byte_requests;
+
+ DCHECK(!requests.empty());
+ for (const MemoryItemRequest& request : requests) {
+ byte_requests.push_back(request.message);
+ }
+
+ state->request_memory_callback.Run(std::move(byte_requests),
+ std::vector<base::SharedMemoryHandle>(),
+ std::move(files));
+}
+
+BlobStatus BlobTransportHost::OnFileResponses(
+ const std::string& uuid,
+ TransportState* state,
+ const std::vector<BlobItemBytesResponse>& responses,
+ BlobStorageContext* context) {
+ BlobTransportRequestBuilder& request_builder = state->request_builder;
+ const auto& requests = request_builder.requests();
+ for (const BlobItemBytesResponse& response : responses) {
+ const MemoryItemRequest& request = requests[response.request_number];
+ const scoped_refptr<ShareableFileReference>& file_ref =
+ state->files[request.message.handle_index];
+ bool success = state->data_builder.PopulateFutureFile(
+ request.browser_item_index, file_ref, response.time_file_modified);
+ if (!success) {
+ CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
+ context);
+ return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
+ }
+ state->num_fulfilled_requests++;
+ }
+ if (state->num_fulfilled_requests == requests.size()) {
+ CompleteTransport(state, context);
+ return BlobStatus::DONE;
+ }
+ return BlobStatus::PENDING_TRANSPORT;
+}
+
+void BlobTransportHost::CompleteTransport(TransportState* state,
+ BlobStorageContext* context) {
+ std::string uuid = state->data_builder.uuid();
+ async_blob_map_.erase(state->data_builder.uuid());
+ context->NotifyTransportComplete(uuid);
+}
+
+} // namespace storage

Powered by Google App Engine
This is Rietveld 408576698