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 c30b3833e902dff55d669f66baff37664b156c47..43436a4ae8d61592e93ffcb9dd9dc79f0c391e49 100644 |
--- a/storage/browser/blob/blob_async_builder_host.cc |
+++ b/storage/browser/blob/blob_async_builder_host.cc |
@@ -14,70 +14,17 @@ |
#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" |
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; |
-} |
- |
-IPCBlobCreationCancelCode ConvertReferencedBlobErrorToConstructingError( |
- IPCBlobCreationCancelCode referenced_blob_error) { |
- switch (referenced_blob_error) { |
- // For most cases we propagate the error. |
- case IPCBlobCreationCancelCode::FILE_WRITE_FAILED: |
- case IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT: |
- case IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN: |
- case IPCBlobCreationCancelCode::OUT_OF_MEMORY: |
- return referenced_blob_error; |
- // Others we report that the referenced blob is broken, as we don't know |
- // why (the BLOB_DEREFERENCED_WHILE_BUILDING should never happen, as we hold |
- // onto the reference of the blobs we're using). |
- case IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING: |
- DCHECK(false) << "Referenced blob should never be dereferenced while we " |
- << "are depending on it, as our system holds a handle."; |
- case IPCBlobCreationCancelCode::UNKNOWN: |
- return IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN; |
- } |
- NOTREACHED(); |
- return IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN; |
-} |
- |
-} // namespace |
- |
+using MemoryStrategyResult = BlobMemoryController::MemoryStrategyResult; |
using MemoryItemRequest = |
BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest; |
BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState( |
- const std::string& uuid, |
- std::set<std::string> referenced_blob_uuids, |
- std::vector<std::unique_ptr<BlobDataHandle>>* referenced_blob_handles) |
- : data_builder(uuid), |
- referenced_blob_uuids(referenced_blob_uuids), |
- referenced_blob_handles(std::move(*referenced_blob_handles)) {} |
+ const std::string& uuid) |
+ : data_builder(uuid) {} |
BlobAsyncBuilderHost::BlobBuildingState::~BlobBuildingState() {} |
@@ -85,112 +32,113 @@ BlobAsyncBuilderHost::BlobAsyncBuilderHost() : ptr_factory_(this) {} |
BlobAsyncBuilderHost::~BlobAsyncBuilderHost() {} |
-BlobTransportResult BlobAsyncBuilderHost::RegisterBlobUUID( |
+BlobTransportResult BlobAsyncBuilderHost::RegisterBlob( |
const std::string& uuid, |
const std::string& content_type, |
const std::string& content_disposition, |
- const std::set<std::string>& referenced_blob_uuids, |
- BlobStorageContext* context) { |
- if (async_blob_map_.find(uuid) != async_blob_map_.end()) |
- return BlobTransportResult::BAD_IPC; |
- if (referenced_blob_uuids.find(uuid) != referenced_blob_uuids.end()) |
- return BlobTransportResult::BAD_IPC; |
- context->CreatePendingBlob(uuid, content_type, content_disposition); |
- std::vector<std::unique_ptr<BlobDataHandle>> handles; |
- for (const std::string& referenced_uuid : referenced_blob_uuids) { |
- std::unique_ptr<BlobDataHandle> handle = |
- context->GetBlobDataFromUUID(referenced_uuid); |
- if (!handle || handle->IsBroken()) { |
- // We cancel the blob right away, and don't bother storing our state. |
- context->CancelPendingBlob( |
- uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN); |
- return BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN; |
- } |
- handles.emplace_back(std::move(handle)); |
- } |
- async_blob_map_[uuid] = base::WrapUnique( |
- new BlobBuildingState(uuid, referenced_blob_uuids, &handles)); |
- return BlobTransportResult::DONE; |
-} |
- |
-BlobTransportResult BlobAsyncBuilderHost::StartBuildingBlob( |
- const std::string& uuid, |
const std::vector<DataElement>& elements, |
- size_t memory_available, |
BlobStorageContext* context, |
- const RequestMemoryCallback& request_memory) { |
+ std::unique_ptr<BlobDataHandle>* handle_output, |
+ const RequestMemoryCallback& request_memory, |
+ const ErrorCallback& report_error, |
+ const DoneCallback& done_callback) { |
DCHECK(context); |
- DCHECK(async_blob_map_.find(uuid) != async_blob_map_.end()); |
+ DCHECK(async_blob_map_.find(uuid) == async_blob_map_.end()); |
- // Step 1: Get the sizes. |
- size_t shortcut_memory_size_bytes = 0; |
uint64_t total_memory_size_bytes = 0; |
- if (!CalculateBlobMemorySize(elements, &shortcut_memory_size_bytes, |
- &total_memory_size_bytes)) { |
- CancelBuildingBlob(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) { |
- CancelBuildingBlob(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(); |
- if (!state_ptr->request_builder.requests().empty()) { |
- // Check that we're not a duplicate call. |
+ MemoryStrategyResult memory_strategy; |
+ if (!context->memory_controller_.DecideBlobTransportationMemoryStrategy( |
+ elements, &total_memory_size_bytes, &memory_strategy)) { |
return BlobTransportResult::BAD_IPC; |
} |
- state_ptr->request_memory_callback = request_memory; |
- // Step 3: Check to make sure the referenced blob information we received |
- // earlier is correct: |
+ // Validate that our referenced blobs. |
std::set<std::string> extracted_blob_uuids; |
michaeln
2016/07/07 20:05:21
local is no longer used?
|
for (const DataElement& e : elements) { |
if (e.type() == DataElement::TYPE_BLOB) { |
- extracted_blob_uuids.insert(e.blob_uuid()); |
- // We can't depend on ourselves. |
if (e.blob_uuid() == uuid) { |
- CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
return BlobTransportResult::BAD_IPC; |
} |
} |
} |
- if (extracted_blob_uuids != state_ptr->referenced_blob_uuids) { |
- CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
- return BlobTransportResult::BAD_IPC; |
+ |
+ const BlobMemoryController& memory_controller = context->memory_controller(); |
+ std::unique_ptr<BlobBuildingState> state(new BlobBuildingState(uuid)); |
+ state->request_memory_callback = request_memory; |
+ |
+ switch (memory_strategy) { |
+ case MemoryStrategyResult::TOO_LARGE: |
+ *handle_output = |
+ context->BuildBrokenBlob(uuid, content_type, content_disposition, |
+ IPCBlobCreationCancelCode::OUT_OF_MEMORY); |
+ return BlobTransportResult::CANCEL_MEMORY_FULL; |
+ case MemoryStrategyResult::SHORTCUT: |
+ case MemoryStrategyResult::NONE_NEEDED: { |
+ for (const DataElement& e : elements) { |
+ DCHECK_NE(e.type(), DataElement::TYPE_BYTES_DESCRIPTION); |
+ state->data_builder.AppendIPCDataElement(e); |
+ } |
+ *handle_output = context->BuildBlob(state->data_builder, false); |
+ return BlobTransportResult::DONE; |
+ } |
+ case MemoryStrategyResult::IPC: |
+ state->strategy = IPCBlobItemRequestStrategy::IPC; |
+ state->request_builder.InitializeForIPCRequests( |
+ memory_controller.max_ipc_memory_size(), total_memory_size_bytes, |
+ elements, &(state->data_builder)); |
+ break; |
+ case MemoryStrategyResult::SHARED_MEMORY: |
+ state->strategy = IPCBlobItemRequestStrategy::SHARED_MEMORY; |
+ state->request_builder.InitializeForSharedMemoryRequests( |
+ memory_controller.max_shared_memory_size(), total_memory_size_bytes, |
+ elements, &(state->data_builder)); |
+ break; |
+ case MemoryStrategyResult::FILE: |
+ state->strategy = IPCBlobItemRequestStrategy::FILE; |
+ state->request_builder.InitializeForFileRequests( |
+ memory_controller.max_file_size(), total_memory_size_bytes, 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); |
+ |
+ *handle_output = context->BuildBlob(state->data_builder, true); |
+ |
+ if ((*handle_output)->IsBroken()) { |
+ return BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN; |
} |
- // Step 4: 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->data_builder.AppendIPCDataElement(e); |
+ BlobBuildingState* state_ptr = state.get(); |
+ async_blob_map_[uuid] = std::move(state); |
+ |
+ if (memory_strategy == MemoryStrategyResult::FILE) { |
+ LOG(ERROR) << "file!"; |
+ const auto& file_sizes = state_ptr->request_builder.file_sizes(); |
+ state_ptr->files.resize(file_sizes.size()); |
+ for (size_t i = 0; i < file_sizes.size(); i++) { |
+ context->memory_controller_.CreateTemporaryFileForRenderer( |
+ file_sizes[i], base::Bind(&BlobAsyncBuilderHost::OnFileCreated, |
+ ptr_factory_.GetWeakPtr(), uuid, i)); |
} |
- FinishBuildingBlob(state_ptr, context); |
- return BlobTransportResult::DONE; |
+ return BlobTransportResult::PENDING_RESPONSES; |
} |
- // From here on, we know the blob's size is less than |memory_available|, |
- // so we know we're < max(size_t). |
- // Step 5: 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->data_builder)); |
- } else { |
- // Step 6: We can fit in IPC. |
- state_ptr->request_builder.InitializeForIPCRequests( |
- max_ipc_memory_size_, total_memory_size_bytes, elements, |
- &(state_ptr->data_builder)); |
+ size_t memory_needed = base::checked_cast<size_t>(total_memory_size_bytes); |
+ if (context->memory_controller_.MaybeFitInMemoryNow(memory_needed)) { |
+ return StartRequests(uuid, state_ptr, context); |
} |
- // 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); |
+ state_ptr->request_memory_callback = request_memory; |
+ state_ptr->error_callback = report_error; |
+ state_ptr->done_callback = done_callback; |
+ state_ptr->waiting_until_room_for_transport = true; |
+ state_ptr->pending_entry = |
+ context->memory_controller_.NotifyWhenMemoryCanPopulated( |
+ memory_needed, |
+ base::Bind(&BlobAsyncBuilderHost::OnCanStartBuildingBlob, |
+ ptr_factory_.GetWeakPtr(), uuid, context->AsWeakPtr())); |
+ return BlobTransportResult::PENDING_RESPONSES; |
} |
BlobTransportResult BlobAsyncBuilderHost::OnMemoryResponses( |
@@ -206,9 +154,9 @@ BlobTransportResult BlobAsyncBuilderHost::OnMemoryResponses( |
CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
return BlobTransportResult::BAD_IPC; |
} |
+ |
BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); |
- BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; |
- const auto& requests = request_builder.requests(); |
+ 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. |
@@ -217,7 +165,6 @@ BlobTransportResult BlobAsyncBuilderHost::OnMemoryResponses( |
return BlobTransportResult::BAD_IPC; |
} |
DCHECK_LT(response.request_number, state->request_received.size()); |
- const MemoryItemRequest& request = requests[response.request_number]; |
if (state->request_received[response.request_number]) { |
// Bad IPC, so we delete our record. |
DVLOG(1) << "Already received response for that request."; |
@@ -225,67 +172,18 @@ BlobTransportResult BlobAsyncBuilderHost::OnMemoryResponses( |
return BlobTransportResult::BAD_IPC; |
} |
state->request_received[response.request_number] = true; |
- bool invalid_ipc = false; |
- bool memory_error = false; |
- switch (request.message.transport_strategy) { |
- case IPCBlobItemRequestStrategy::IPC: |
- if (response.inline_data.size() < request.message.size) { |
- DVLOG(1) << "Invalid data size " << response.inline_data.size() |
- << " vs requested size of " << request.message.size; |
- invalid_ipc = true; |
- break; |
- } |
- invalid_ipc = !state->data_builder.PopulateFutureData( |
- request.browser_item_index, &response.inline_data[0], |
- request.browser_item_offset, request.message.size); |
- break; |
- case IPCBlobItemRequestStrategy::SHARED_MEMORY: |
- if (state->num_shared_memory_requests == 0) { |
- DVLOG(1) << "Received too many responses for shared memory."; |
- invalid_ipc = true; |
- break; |
- } |
- 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. Another option is to use |
- // MapAt, remove the mapped boolean, and then exclude the |
- // handle_offset below. |
- 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; |
- memory_error = true; |
- break; |
- } |
- } |
- |
- invalid_ipc = !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); |
- break; |
- case IPCBlobItemRequestStrategy::FILE: |
- case IPCBlobItemRequestStrategy::UNKNOWN: |
- DVLOG(1) << "Not implemented."; |
- invalid_ipc = true; |
- break; |
- } |
- if (invalid_ipc) { |
- // Bad IPC, so we delete our record and return false. |
- CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
+ } |
+ 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: |
+ NOTREACHED(); |
return BlobTransportResult::BAD_IPC; |
- } |
- if (memory_error) { |
- DVLOG(1) << "Shared memory error."; |
- CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, |
- context); |
- return BlobTransportResult::CANCEL_MEMORY_FULL; |
- } |
- state->num_fulfilled_requests++; |
} |
- return ContinueBlobMemoryRequests(uuid, context); |
} |
void BlobAsyncBuilderHost::CancelBuildingBlob(const std::string& uuid, |
@@ -300,8 +198,14 @@ void BlobAsyncBuilderHost::CancelBuildingBlob(const std::string& uuid, |
// '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. |
- if (context->registry().HasEntry(uuid)) |
- context->CancelPendingBlob(uuid, code); |
+ if (context->registry().HasEntry(uuid)) { |
+ BlobBuildingState* state = state_it->second.get(); |
+ if (state->waiting_until_room_for_transport) { |
+ context->memory_controller_.RemovePendingConstructionEntry( |
+ state->pending_entry); |
+ } |
+ context->BreakAndFinishBlob(uuid, code); |
+ } |
async_blob_map_.erase(state_it); |
} |
@@ -323,25 +227,129 @@ void BlobAsyncBuilderHost::CancelAll(BlobStorageContext* context) { |
async_blob_map_.clear(); |
for (const std::unique_ptr<BlobDataHandle>& handle : |
referenced_pending_blobs) { |
- context->CancelPendingBlob( |
+ context->BreakAndFinishBlob( |
handle->uuid(), IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT); |
} |
} |
-BlobTransportResult BlobAsyncBuilderHost::ContinueBlobMemoryRequests( |
+BlobTransportResult BlobAsyncBuilderHost::StartRequests( |
const std::string& uuid, |
+ BlobBuildingState* state, |
BlobStorageContext* context) { |
+ switch (state->strategy) { |
+ case IPCBlobItemRequestStrategy::IPC: |
+ SendIPCRequests(state, context); |
+ return BlobTransportResult::DONE; |
+ case IPCBlobItemRequestStrategy::SHARED_MEMORY: |
+ return ContinueSharedMemoryRequests(uuid, state, context); |
+ case IPCBlobItemRequestStrategy::FILE: |
+ case IPCBlobItemRequestStrategy::UNKNOWN: |
+ NOTREACHED(); |
+ return BlobTransportResult::CANCEL_UNKNOWN; |
+ } |
+} |
+ |
+void BlobAsyncBuilderHost::OnCanStartBuildingBlob( |
+ const std::string& uuid, |
+ base::WeakPtr<BlobStorageContext> context, |
+ bool success) { |
+ LOG(ERROR) << "We can fit now!"; |
+ if (!context) { |
+ async_blob_map_.erase(uuid); |
+ return; |
+ } |
AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); |
- DCHECK(state_it != async_blob_map_.end()); |
- BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); |
+ if (state_it == async_blob_map_.end()) { |
+ return; |
+ } |
+ BlobBuildingState* state = state_it->second.get(); |
+ state->waiting_until_room_for_transport = false; |
+ BlobTransportResult result = StartRequests(uuid, state, context.get()); |
+ if (result == BlobTransportResult::PENDING_RESPONSES) |
+ return; |
+ ErrorCallback error_callback = state->error_callback; |
+ DoneCallback done_callback = state->done_callback; |
+ async_blob_map_.erase(state_it); |
+ switch (result) { |
+ case BlobTransportResult::PENDING_RESPONSES: |
+ NOTREACHED(); |
+ case BlobTransportResult::CANCEL_UNKNOWN: |
+ case BlobTransportResult::BAD_IPC: |
+ error_callback.Run(IPCBlobCreationCancelCode::UNKNOWN); |
+ break; |
+ case BlobTransportResult::CANCEL_MEMORY_FULL: |
+ error_callback.Run(IPCBlobCreationCancelCode::OUT_OF_MEMORY); |
+ break; |
+ case BlobTransportResult::CANCEL_FILE_ERROR: |
+ error_callback.Run(IPCBlobCreationCancelCode::FILE_WRITE_FAILED); |
+ break; |
+ case BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN: |
+ error_callback.Run(IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN); |
+ break; |
+ return; |
+ case BlobTransportResult::DONE: |
+ done_callback.Run(); |
+ return; |
+ } |
+} |
- BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; |
- const std::vector<MemoryItemRequest>& requests = request_builder.requests(); |
+void BlobAsyncBuilderHost::SendIPCRequests(BlobBuildingState* state, |
+ BlobStorageContext* context) { |
+ const std::vector<MemoryItemRequest>& requests = |
+ state->request_builder.requests(); |
+ std::unique_ptr<std::vector<BlobItemBytesRequest>> byte_requests( |
+ new std::vector<BlobItemBytesRequest>()); |
+ |
+ DCHECK(!requests.empty()); |
+ for (const MemoryItemRequest& request : requests) { |
+ byte_requests->push_back(request.message); |
+ } |
+ |
+ state->request_memory_callback.Run( |
+ std::move(byte_requests), |
+ base::WrapUnique(new std::vector<base::SharedMemoryHandle>()), |
+ base::WrapUnique(new std::vector<base::File>())); |
+} |
+ |
+BlobTransportResult BlobAsyncBuilderHost::OnIPCResponses( |
+ const std::string& uuid, |
+ BlobBuildingState* 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, IPCBlobCreationCancelCode::UNKNOWN, context); |
+ return BlobTransportResult::BAD_IPC; |
+ break; |
+ } |
+ bool success = state->data_builder.PopulateFutureData( |
+ request.browser_item_index, &response.inline_data[0], |
+ request.browser_item_offset, request.message.size); |
+ if (!success) { |
+ CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
+ return BlobTransportResult::BAD_IPC; |
+ } |
+ state->num_fulfilled_requests++; |
+ } |
if (state->num_fulfilled_requests == num_requests) { |
FinishBuildingBlob(state, context); |
return BlobTransportResult::DONE; |
} |
+ return BlobTransportResult::PENDING_RESPONSES; |
+} |
+ |
+BlobTransportResult BlobAsyncBuilderHost::ContinueSharedMemoryRequests( |
+ const std::string& uuid, |
+ BlobBuildingState* state, |
+ BlobStorageContext* context) { |
+ BlobAsyncTransportRequestBuilder& 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. |
@@ -355,50 +363,30 @@ BlobTransportResult BlobAsyncBuilderHost::ContinueBlobMemoryRequests( |
for (; state->next_request < num_requests; ++state->next_request) { |
const MemoryItemRequest& request = requests[state->next_request]; |
- |
- bool stop_accumulating = false; |
bool using_shared_memory_handle = state->num_shared_memory_requests > 0; |
- switch (request.message.transport_strategy) { |
- case IPCBlobItemRequestStrategy::IPC: |
- byte_requests->push_back(request.message); |
- break; |
- case IPCBlobItemRequestStrategy::SHARED_MEMORY: |
- if (using_shared_memory_handle && |
- state->current_shared_memory_handle_index != |
- request.message.handle_index) { |
- // We only want one shared memory per requesting blob. |
- stop_accumulating = true; |
- break; |
- } |
- using_shared_memory_handle = true; |
- 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 BlobTransportResult::CANCEL_MEMORY_FULL; |
- } |
- } |
- 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; |
- break; |
- case IPCBlobItemRequestStrategy::FILE: |
- case IPCBlobItemRequestStrategy::UNKNOWN: |
- NOTREACHED() << "Not implemented yet."; |
- break; |
- } |
- if (stop_accumulating) { |
+ 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 BlobTransportResult::CANCEL_MEMORY_FULL; |
+ } |
+ } |
+ 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()); |
@@ -408,57 +396,124 @@ BlobTransportResult BlobAsyncBuilderHost::ContinueBlobMemoryRequests( |
return BlobTransportResult::PENDING_RESPONSES; |
} |
-void BlobAsyncBuilderHost::ReferencedBlobFinished( |
- const std::string& owning_blob_uuid, |
- base::WeakPtr<BlobStorageContext> context, |
- bool construction_success, |
- IPCBlobCreationCancelCode reason) { |
- if (!context) { |
- return; |
+BlobTransportResult BlobAsyncBuilderHost::OnSharedMemoryResponses( |
+ const std::string& uuid, |
+ BlobBuildingState* state, |
+ const std::vector<BlobItemBytesResponse>& responses, |
+ BlobStorageContext* context) { |
+ BlobAsyncTransportRequestBuilder& 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, IPCBlobCreationCancelCode::UNKNOWN, context); |
+ return BlobTransportResult::BAD_IPC; |
+ break; |
+ } |
+ 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. Another option is to use |
+ // MapAt, remove the mapped boolean, and then exclude the |
+ // handle_offset below. |
+ 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, IPCBlobCreationCancelCode::OUT_OF_MEMORY, |
+ context); |
+ return BlobTransportResult::CANCEL_MEMORY_FULL; |
+ } |
+ } |
+ |
+ 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, IPCBlobCreationCancelCode::UNKNOWN, context); |
+ return BlobTransportResult::BAD_IPC; |
+ } |
+ state->num_fulfilled_requests++; |
} |
- auto state_it = async_blob_map_.find(owning_blob_uuid); |
- if (state_it == async_blob_map_.end()) { |
- return; |
+ if (state->num_fulfilled_requests == requests.size()) { |
+ FinishBuildingBlob(state, context); |
+ return BlobTransportResult::DONE; |
} |
- if (!construction_success) { |
- CancelBuildingBlob(owning_blob_uuid, |
- ConvertReferencedBlobErrorToConstructingError(reason), |
- context.get()); |
+ return ContinueSharedMemoryRequests(uuid, state, context); |
+} |
+ |
+void BlobAsyncBuilderHost::OnFileCreated( |
+ const std::string& uuid, |
+ size_t handle_index, |
+ BlobMemoryController::FileCreationInfo file_info) { |
+ LOG(ERROR) << "File crated!"; |
+ AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); |
+ if (state_it == async_blob_map_.end()) { |
return; |
} |
BlobBuildingState* state = state_it->second.get(); |
- DCHECK_GT(state->num_referenced_blobs_building, 0u); |
- if (--state->num_referenced_blobs_building == 0) { |
- context->CompletePendingBlob(state->data_builder); |
- async_blob_map_.erase(state->data_builder.uuid()); |
+ DCHECK_LT(handle_index, state->files.size()); |
+ state->files[handle_index] = std::move(file_info.file_reference); |
+ |
+ const BlobAsyncTransportRequestBuilder& request_builder = |
+ state->request_builder; |
+ const std::vector<MemoryItemRequest>& requests = request_builder.requests(); |
+ |
+ std::unique_ptr<std::vector<BlobItemBytesRequest>> byte_requests( |
+ new std::vector<BlobItemBytesRequest>()); |
+ |
+ std::unique_ptr<std::vector<base::File>> files(new std::vector<base::File>()); |
+ files->push_back(std::move(file_info.file.value())); |
+ file_info.file = base::nullopt; |
+ |
+ for (const MemoryItemRequest& request : requests) { |
+ if (request.message.handle_index != handle_index) { |
+ continue; |
+ } |
+ byte_requests->push_back(request.message); |
+ byte_requests->back().handle_index = 0; |
+ } |
+ |
+ state->request_memory_callback.Run( |
+ std::move(byte_requests), |
+ base::WrapUnique(new std::vector<base::SharedMemoryHandle>()), |
+ std::move(files)); |
+} |
+ |
+BlobTransportResult BlobAsyncBuilderHost::OnFileResponses( |
+ const std::string& uuid, |
+ BlobBuildingState* state, |
+ const std::vector<BlobItemBytesResponse>& responses, |
+ BlobStorageContext* context) { |
+ BlobAsyncTransportRequestBuilder& 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, IPCBlobCreationCancelCode::UNKNOWN, context); |
+ return BlobTransportResult::BAD_IPC; |
+ } |
+ state->num_fulfilled_requests++; |
+ } |
+ if (state->num_fulfilled_requests == requests.size()) { |
+ FinishBuildingBlob(state, context); |
+ return BlobTransportResult::DONE; |
} |
+ return BlobTransportResult::PENDING_RESPONSES; |
} |
void BlobAsyncBuilderHost::FinishBuildingBlob(BlobBuildingState* state, |
BlobStorageContext* context) { |
- if (!state->referenced_blob_uuids.empty()) { |
- DCHECK_EQ(0u, state->num_referenced_blobs_building); |
- state->num_referenced_blobs_building = 0; |
- // We assume re-entry is not possible, as RunOnConstructionComplete |
- // will schedule a task when the blob is being built. Thus we can't have the |
- // case where |num_referenced_blobs_building| reaches 0 in the |
- // ReferencedBlobFinished method before we're finished looping. |
- for (const std::string& referenced_uuid : state->referenced_blob_uuids) { |
- if (context->IsBeingBuilt(referenced_uuid)) { |
- state->num_referenced_blobs_building++; |
- context->RunOnConstructionComplete( |
- referenced_uuid, |
- base::Bind(&BlobAsyncBuilderHost::ReferencedBlobFinished, |
- ptr_factory_.GetWeakPtr(), state->data_builder.uuid(), |
- context->AsWeakPtr())); |
- } |
- } |
- if (state->num_referenced_blobs_building > 0) { |
- // We wait until referenced blobs are done. |
- return; |
- } |
- } |
- context->CompletePendingBlob(state->data_builder); |
+ context->FinishedPopulatingBlob(state->data_builder.uuid()); |
async_blob_map_.erase(state->data_builder.uuid()); |
} |