Chromium Code Reviews| Index: storage/browser/blob/blob_async_builder_host.cc |
| diff --git a/storage/browser/blob/blob_async_builder_host.cc b/storage/browser/blob/blob_async_builder_host.cc |
| index 6d4432748c358b3348700cb00979cf1f6c5bfd97..3912ec1e0a87937269a8373f4224ec90db611caa 100644 |
| --- a/storage/browser/blob/blob_async_builder_host.cc |
| +++ b/storage/browser/blob/blob_async_builder_host.cc |
| @@ -10,13 +10,43 @@ |
| #include <utility> |
| #include "base/memory/shared_memory.h" |
| +#include "storage/browser/blob/blob_storage_context.h" |
| namespace storage { |
| +namespace { |
| +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 |
| -using MemoryItemRequest = BlobAsyncTransportStrategy::RendererMemoryItemRequest; |
| +using MemoryItemRequest = |
| + BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest; |
| -BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState() |
| - : next_request(0), |
| +BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState( |
| + const std::string& uuid) |
| + : builder(uuid), |
| + next_request(0), |
| num_fulfilled_requests(0), |
| num_shared_memory_requests(0), |
| current_shared_memory_handle_index(0) {} |
| @@ -27,97 +57,116 @@ BlobAsyncBuilderHost::BlobAsyncBuilderHost() {} |
| BlobAsyncBuilderHost::~BlobAsyncBuilderHost() {} |
| -bool BlobAsyncBuilderHost::StartBuildingBlob( |
| +bool BlobAsyncBuilderHost::RegisterBlobUUID( |
| const std::string& uuid, |
| - const std::string& type, |
| - const std::vector<DataElement>& descriptions, |
| - size_t memory_available, |
| - const base::Callback<void(const std::vector<storage::BlobItemBytesRequest>&, |
| - const std::vector<base::SharedMemoryHandle>&, |
| - const std::vector<uint64_t>&)>& request_memory, |
| - const base::Callback<void(const BlobDataBuilder&)>& done, |
| - const base::Callback<void(IPCBlobCreationCancelCode)>& cancel) { |
| + const std::string& content_type, |
| + const std::string& content_disposition, |
| + BlobStorageContext* context) { |
| if (async_blob_map_.find(uuid) != async_blob_map_.end()) |
| return false; |
| - if (BlobAsyncTransportStrategy::ShouldBeShortcut(descriptions, |
| - memory_available)) { |
| - // We have enough memory, and all the data is here, so we use the shortcut |
| - // method and populate the old way. |
| - BlobDataBuilder builder(uuid); |
| - builder.set_content_type(type); |
| - for (const DataElement& element : descriptions) { |
| - builder.AppendIPCDataElement(element); |
| - } |
| - done.Run(builder); |
| - return true; |
| + async_blob_map_[uuid] = make_scoped_ptr(new BlobBuildingState(uuid)); |
| + context->RegisterBlobUUID(uuid, content_type, content_disposition); |
| + return true; |
| +} |
| + |
| +BlobTransportResult BlobAsyncBuilderHost::StartBuildingBlob( |
| + const std::string& uuid, |
| + const std::vector<DataElement>& elements, |
| + size_t memory_available, |
| + BlobStorageContext* context, |
| + const RequestMemoryCallback& request_memory) { |
| + DCHECK(context); |
| + if (async_blob_map_.find(uuid) == async_blob_map_.end()) { |
| + return BlobTransportResult::BAD_IPC; |
| } |
| - scoped_ptr<BlobBuildingState> state(new BlobBuildingState()); |
| - BlobBuildingState* state_ptr = state.get(); |
| - async_blob_map_[uuid] = std::move(state); |
| - state_ptr->type = type; |
| + // Step 1: Get the sizes. |
| + size_t shortcut_memory_size_bytes; |
| + uint64_t total_memory_size_bytes; |
| + if (!CalculateBlobMemorySize(elements, &shortcut_memory_size_bytes, |
| + &total_memory_size_bytes)) { |
| + MaybeCancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
| + return BlobTransportResult::BAD_IPC; |
| + } |
| + |
| + // Step 2: Check if we have enough memory to store the blob. |
| + if (total_memory_size_bytes > memory_available) { |
| + MaybeCancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, |
| + context); |
| + return BlobTransportResult::CANCEL_MEMORY_FULL; |
| + } |
| + |
| + // From here on, we know we can fit the blob in memory. |
| + BlobBuildingState* state_ptr = async_blob_map_[uuid].get(); |
| state_ptr->request_memory_callback = request_memory; |
| - state_ptr->done_callback = done; |
| - state_ptr->cancel_callback = cancel; |
| - // We are currently only operating in 'no disk' mode. This will change in |
| - // future patches to enable disk storage. |
| - // Since we don't have a disk yet, we put 0 for disk_space_left. |
| - state_ptr->transport_strategy.Initialize( |
| - max_ipc_memory_size_, max_shared_memory_size_, max_file_size_, |
| - 0 /* disk_space_left */, memory_available, uuid, descriptions); |
| + // Step 3: Decide if we're using the shortcut method. This will also catch |
| + // the case where we don't have any memory items. |
| + if (shortcut_memory_size_bytes == total_memory_size_bytes && |
| + shortcut_memory_size_bytes <= memory_available) { |
| + for (const DataElement& e : elements) { |
| + state_ptr->builder.AppendIPCDataElement(e); |
| + } |
| + context->CompletePendingBlob(state_ptr->builder); |
| + async_blob_map_.erase(uuid); |
| + return BlobTransportResult::DONE; |
| + } |
| - switch (state_ptr->transport_strategy.error()) { |
| - case BlobAsyncTransportStrategy::ERROR_TOO_LARGE: |
| - // Cancel cleanly, we're out of memory. |
| - CancelAndCleanup(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY); |
| - return true; |
| - case BlobAsyncTransportStrategy::ERROR_INVALID_PARAMS: |
| - // Bad IPC, so we ignore and clean up. |
| - VLOG(1) << "Error initializing transport strategy: " |
| - << state_ptr->transport_strategy.error(); |
| - async_blob_map_.erase(async_blob_map_.find(uuid)); |
| - return false; |
| - case BlobAsyncTransportStrategy::ERROR_NONE: |
| - ContinueBlobMemoryRequests(uuid); |
| - return true; |
| + // From here on, we know the blob's size is less than |memory_available|, |
| + // so we know we're < max(size_t). |
| + // Step 4: Decide if we're using shared memory. |
| + if (total_memory_size_bytes > max_ipc_memory_size_) { |
| + state_ptr->request_builder.InitializeForSharedMemoryRequests( |
| + max_shared_memory_size_, total_memory_size_bytes, elements, |
| + &(state_ptr->builder)); |
| + } else { |
| + // Step 5: We can fit in IPC. |
| + state_ptr->request_builder.InitializeForIPCRequests( |
| + max_ipc_memory_size_, total_memory_size_bytes, elements, |
| + &(state_ptr->builder)); |
| } |
| - return false; |
| + // We initialize our requests received state now that they are populated. |
| + state_ptr->request_received.resize( |
| + state_ptr->request_builder.requests().size(), false); |
| + return ContinueBlobMemoryRequests(uuid, context); |
| } |
| -bool BlobAsyncBuilderHost::OnMemoryResponses( |
| +BlobTransportResult BlobAsyncBuilderHost::OnMemoryResponses( |
| const std::string& uuid, |
| - const std::vector<BlobItemBytesResponse>& responses) { |
| + const std::vector<BlobItemBytesResponse>& responses, |
| + BlobStorageContext* context) { |
| if (responses.empty()) { |
| - return false; |
| + MaybeCancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
|
michaeln
2016/02/06 00:08:39
moving this early return down below line 146 would
dmurph
2016/02/09 00:52:25
Done.
|
| + return BlobTransportResult::BAD_IPC; |
| } |
| AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); |
| if (state_it == async_blob_map_.end()) { |
| - // There's a possibility that we had a shared memory error, and there were |
| - // still responses in flight. So we don't fail here, we just ignore. |
| DVLOG(1) << "Could not find blob " << uuid; |
| - return true; |
| + return BlobTransportResult::BAD_IPC; |
| } |
| BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); |
| - BlobAsyncTransportStrategy& strategy = state->transport_strategy; |
| + BlobAsyncTransportRequestBuilder& builder = state->request_builder; |
| bool invalid_ipc = false; |
| bool memory_error = false; |
| - const auto& requests = strategy.requests(); |
| + const auto& requests = 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; |
| - async_blob_map_.erase(state_it); |
| - return false; |
| + MaybeCancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, |
| + context); |
| + return BlobTransportResult::BAD_IPC; |
| } |
| + DCHECK_LT(response.request_number, state->request_received.size()); |
| const MemoryItemRequest& request = requests[response.request_number]; |
| - if (request.received) { |
| + if (state->request_received[response.request_number]) { |
| // Bad IPC, so we delete our record. |
| DVLOG(1) << "Already received response for that request."; |
| - async_blob_map_.erase(state_it); |
| - return false; |
| + MaybeCancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, |
| + context); |
| + return BlobTransportResult::BAD_IPC; |
| } |
| - strategy.MarkRequestAsReceived(response.request_number); |
| + state->request_received[response.request_number] = true; |
| switch (request.message.transport_strategy) { |
| case IPCBlobItemRequestStrategy::IPC: |
| if (response.inline_data.size() < request.message.size) { |
| @@ -126,7 +175,7 @@ bool BlobAsyncBuilderHost::OnMemoryResponses( |
| invalid_ipc = true; |
| break; |
| } |
| - invalid_ipc = !strategy.blob_builder()->PopulateFutureData( |
| + invalid_ipc = !state->builder.PopulateFutureData( |
| request.browser_item_index, &response.inline_data[0], |
| request.browser_item_offset, request.message.size); |
| break; |
| @@ -142,8 +191,8 @@ bool BlobAsyncBuilderHost::OnMemoryResponses( |
| // whole thing in this group of responses. Another option is to use |
| // MapAt, remove the mapped boolean, and then exclude the |
| // handle_offset below. |
| - size_t handle_size = strategy.handle_sizes().at( |
| - state->current_shared_memory_handle_index); |
| + size_t handle_size = 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; |
| memory_error = true; |
| @@ -151,7 +200,7 @@ bool BlobAsyncBuilderHost::OnMemoryResponses( |
| } |
| } |
| - invalid_ipc = !strategy.blob_builder()->PopulateFutureData( |
| + invalid_ipc = !state->builder.PopulateFutureData( |
| request.browser_item_index, |
| static_cast<const char*>(state->shared_memory_block->memory()) + |
| request.message.handle_offset, |
| @@ -165,46 +214,67 @@ bool BlobAsyncBuilderHost::OnMemoryResponses( |
| } |
| if (invalid_ipc) { |
| // Bad IPC, so we delete our record and return false. |
| - async_blob_map_.erase(state_it); |
| - return false; |
| + MaybeCancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, |
| + context); |
| + return BlobTransportResult::BAD_IPC; |
| } |
| if (memory_error) { |
| DVLOG(1) << "Shared memory error."; |
| - CancelAndCleanup(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY); |
| - return true; |
| + MaybeCancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, |
| + context); |
| + return BlobTransportResult::CANCEL_MEMORY_FULL; |
| } |
| state->num_fulfilled_requests++; |
| } |
| - ContinueBlobMemoryRequests(uuid); |
| - return true; |
| + return ContinueBlobMemoryRequests(uuid, context); |
| +} |
| + |
| +void BlobAsyncBuilderHost::MaybeCancelBuildingBlob( |
| + const std::string& uuid, |
| + IPCBlobCreationCancelCode code, |
| + BlobStorageContext* context) { |
| + DCHECK(context); |
| + if (async_blob_map_.erase(uuid)) { |
| + context->FlagBlobAsBrokenAndBuild(uuid, code); |
| + } |
| } |
| -void BlobAsyncBuilderHost::StopBuildingBlob(const std::string& uuid) { |
| - async_blob_map_.erase(async_blob_map_.find(uuid)); |
| +void BlobAsyncBuilderHost::ClearAndBreakPendingBlobConstruction( |
| + BlobStorageContext* context) { |
| + DCHECK(context); |
| + for (const auto& uuid_state_pair : async_blob_map_) { |
| + context->FlagBlobAsBrokenAndBuild( |
| + uuid_state_pair.first, |
| + IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT); |
| + } |
| + async_blob_map_.clear(); |
| } |
| -void BlobAsyncBuilderHost::ContinueBlobMemoryRequests(const std::string& uuid) { |
| +BlobTransportResult BlobAsyncBuilderHost::ContinueBlobMemoryRequests( |
| + const std::string& uuid, |
| + BlobStorageContext* context) { |
| AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); |
| DCHECK(state_it != async_blob_map_.end()); |
| BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); |
| + BlobAsyncTransportRequestBuilder& builder = state->request_builder; |
|
michaeln
2016/02/06 00:08:39
oh,this is different then state->builder!
maybe ca
dmurph
2016/02/09 00:52:25
Done.
|
| const std::vector<MemoryItemRequest>& requests = |
| - state->transport_strategy.requests(); |
| - BlobAsyncTransportStrategy& strategy = state->transport_strategy; |
| + builder.requests(); |
| size_t num_requests = requests.size(); |
| if (state->num_fulfilled_requests == num_requests) { |
| - DoneAndCleanup(uuid); |
| - return; |
| + context->CompletePendingBlob(state->builder); |
| + async_blob_map_.erase(uuid); |
| + return BlobTransportResult::DONE; |
| } |
| 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; |
| + return BlobTransportResult::PENDING_RESPONSES; |
| } |
| std::vector<BlobItemBytesRequest> byte_requests; |
| std::vector<base::SharedMemoryHandle> shared_memory; |
| - std::vector<uint64_t> files; |
| + std::vector<base::File> files; |
| for (; state->next_request < num_requests; ++state->next_request) { |
| const MemoryItemRequest& request = requests[state->next_request]; |
| @@ -230,11 +300,11 @@ void BlobAsyncBuilderHost::ContinueBlobMemoryRequests(const std::string& uuid) { |
| if (!state->shared_memory_block) { |
| state->shared_memory_block.reset(new base::SharedMemory()); |
| - size_t size = strategy.handle_sizes()[request.message.handle_index]; |
| + size_t size = |
| + 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."; |
| - CancelAndCleanup(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY); |
| - return; |
| + return BlobTransportResult::CANCEL_MEMORY_FULL; |
| } |
| } |
| shared_memory.push_back(state->shared_memory_block->handle()); |
| @@ -252,25 +322,10 @@ void BlobAsyncBuilderHost::ContinueBlobMemoryRequests(const std::string& uuid) { |
| break; |
| } |
| } |
| - |
| DCHECK(!requests.empty()); |
| - state->request_memory_callback.Run(byte_requests, shared_memory, files); |
| -} |
| - |
| -void BlobAsyncBuilderHost::CancelAndCleanup(const std::string& uuid, |
| - IPCBlobCreationCancelCode code) { |
| - scoped_ptr<BlobBuildingState> state = std::move(async_blob_map_[uuid]); |
| - async_blob_map_.erase(uuid); |
| - state->cancel_callback.Run(code); |
| -} |
| - |
| -void BlobAsyncBuilderHost::DoneAndCleanup(const std::string& uuid) { |
| - scoped_ptr<BlobBuildingState> state = std::move(async_blob_map_[uuid]); |
| - async_blob_map_.erase(uuid); |
| - BlobDataBuilder* builder = state->transport_strategy.blob_builder(); |
| - builder->set_content_type(state->type); |
| - state->done_callback.Run(*builder); |
| + state->request_memory_callback.Run(byte_requests, shared_memory, &files); |
| + return BlobTransportResult::PENDING_RESPONSES; |
| } |
| } // namespace storage |