Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "storage/browser/blob/blob_async_builder_host.h" | 5 #include "storage/browser/blob/blob_async_builder_host.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <memory> | 10 #include <memory> |
| 11 #include <utility> | 11 #include <utility> |
| 12 | 12 |
| 13 #include "base/bind.h" | 13 #include "base/bind.h" |
| 14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
| 15 #include "base/memory/shared_memory.h" | 15 #include "base/memory/shared_memory.h" |
| 16 #include "storage/browser/blob/blob_data_handle.h" | 16 #include "storage/browser/blob/blob_data_handle.h" |
| 17 #include "storage/browser/blob/blob_memory_controller.h" | |
| 17 #include "storage/browser/blob/blob_storage_context.h" | 18 #include "storage/browser/blob/blob_storage_context.h" |
| 18 | 19 |
| 19 namespace storage { | 20 namespace storage { |
| 20 namespace { | 21 using MemoryStrategyResult = BlobMemoryController::MemoryStrategyResult; |
| 21 | |
| 22 bool CalculateBlobMemorySize(const std::vector<DataElement>& elements, | |
| 23 size_t* shortcut_bytes, | |
| 24 uint64_t* total_bytes) { | |
| 25 DCHECK(shortcut_bytes); | |
| 26 DCHECK(total_bytes); | |
| 27 base::CheckedNumeric<uint64_t> total_size_checked = 0; | |
| 28 base::CheckedNumeric<size_t> shortcut_size_checked = 0; | |
| 29 for (const auto& e : elements) { | |
| 30 if (e.type() == DataElement::TYPE_BYTES) { | |
| 31 total_size_checked += e.length(); | |
| 32 shortcut_size_checked += e.length(); | |
| 33 } else if (e.type() == DataElement::TYPE_BYTES_DESCRIPTION) { | |
| 34 total_size_checked += e.length(); | |
| 35 } else { | |
| 36 continue; | |
| 37 } | |
| 38 if (!total_size_checked.IsValid() || !shortcut_size_checked.IsValid()) { | |
| 39 return false; | |
| 40 } | |
| 41 } | |
| 42 *shortcut_bytes = shortcut_size_checked.ValueOrDie(); | |
| 43 *total_bytes = total_size_checked.ValueOrDie(); | |
| 44 return true; | |
| 45 } | |
| 46 | |
| 47 IPCBlobCreationCancelCode ConvertReferencedBlobErrorToConstructingError( | |
| 48 IPCBlobCreationCancelCode referenced_blob_error) { | |
| 49 switch (referenced_blob_error) { | |
| 50 // For most cases we propagate the error. | |
| 51 case IPCBlobCreationCancelCode::FILE_WRITE_FAILED: | |
| 52 case IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT: | |
| 53 case IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN: | |
| 54 case IPCBlobCreationCancelCode::OUT_OF_MEMORY: | |
| 55 return referenced_blob_error; | |
| 56 // Others we report that the referenced blob is broken, as we don't know | |
| 57 // why (the BLOB_DEREFERENCED_WHILE_BUILDING should never happen, as we hold | |
| 58 // onto the reference of the blobs we're using). | |
| 59 case IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING: | |
| 60 DCHECK(false) << "Referenced blob should never be dereferenced while we " | |
| 61 << "are depending on it, as our system holds a handle."; | |
| 62 case IPCBlobCreationCancelCode::UNKNOWN: | |
| 63 return IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN; | |
| 64 } | |
| 65 NOTREACHED(); | |
| 66 return IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN; | |
| 67 } | |
| 68 | |
| 69 } // namespace | |
| 70 | |
| 71 using MemoryItemRequest = | 22 using MemoryItemRequest = |
| 72 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest; | 23 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest; |
|
michaeln
2016/07/07 20:05:22
I think less nesting of structs and enum types mig
dmurph
2016/07/11 23:33:26
I consolidated all enums. I feel like keeping this
| |
| 73 | 24 |
| 74 BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState( | 25 BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState( |
| 75 const std::string& uuid, | 26 const std::string& uuid) |
| 76 std::set<std::string> referenced_blob_uuids, | 27 : data_builder(uuid) {} |
| 77 std::vector<std::unique_ptr<BlobDataHandle>>* referenced_blob_handles) | |
| 78 : data_builder(uuid), | |
| 79 referenced_blob_uuids(referenced_blob_uuids), | |
| 80 referenced_blob_handles(std::move(*referenced_blob_handles)) {} | |
| 81 | 28 |
| 82 BlobAsyncBuilderHost::BlobBuildingState::~BlobBuildingState() {} | 29 BlobAsyncBuilderHost::BlobBuildingState::~BlobBuildingState() {} |
| 83 | 30 |
| 84 BlobAsyncBuilderHost::BlobAsyncBuilderHost() : ptr_factory_(this) {} | 31 BlobAsyncBuilderHost::BlobAsyncBuilderHost() : ptr_factory_(this) {} |
| 85 | 32 |
| 86 BlobAsyncBuilderHost::~BlobAsyncBuilderHost() {} | 33 BlobAsyncBuilderHost::~BlobAsyncBuilderHost() {} |
| 87 | 34 |
| 88 BlobTransportResult BlobAsyncBuilderHost::RegisterBlobUUID( | 35 BlobTransportResult BlobAsyncBuilderHost::RegisterBlob( |
| 89 const std::string& uuid, | 36 const std::string& uuid, |
| 90 const std::string& content_type, | 37 const std::string& content_type, |
| 91 const std::string& content_disposition, | 38 const std::string& content_disposition, |
| 92 const std::set<std::string>& referenced_blob_uuids, | 39 const std::vector<DataElement>& elements, |
| 93 BlobStorageContext* context) { | 40 BlobStorageContext* context, |
| 94 if (async_blob_map_.find(uuid) != async_blob_map_.end()) | 41 std::unique_ptr<BlobDataHandle>* handle_output, |
| 95 return BlobTransportResult::BAD_IPC; | 42 const RequestMemoryCallback& request_memory, |
| 96 if (referenced_blob_uuids.find(uuid) != referenced_blob_uuids.end()) | 43 const ErrorCallback& report_error, |
| 97 return BlobTransportResult::BAD_IPC; | 44 const DoneCallback& done_callback) { |
| 98 context->CreatePendingBlob(uuid, content_type, content_disposition); | 45 DCHECK(context); |
| 99 std::vector<std::unique_ptr<BlobDataHandle>> handles; | 46 DCHECK(async_blob_map_.find(uuid) == async_blob_map_.end()); |
| 100 for (const std::string& referenced_uuid : referenced_blob_uuids) { | |
| 101 std::unique_ptr<BlobDataHandle> handle = | |
| 102 context->GetBlobDataFromUUID(referenced_uuid); | |
| 103 if (!handle || handle->IsBroken()) { | |
| 104 // We cancel the blob right away, and don't bother storing our state. | |
| 105 context->CancelPendingBlob( | |
| 106 uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN); | |
| 107 return BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN; | |
| 108 } | |
| 109 handles.emplace_back(std::move(handle)); | |
| 110 } | |
| 111 async_blob_map_[uuid] = base::WrapUnique( | |
| 112 new BlobBuildingState(uuid, referenced_blob_uuids, &handles)); | |
| 113 return BlobTransportResult::DONE; | |
| 114 } | |
| 115 | 47 |
| 116 BlobTransportResult BlobAsyncBuilderHost::StartBuildingBlob( | |
| 117 const std::string& uuid, | |
| 118 const std::vector<DataElement>& elements, | |
| 119 size_t memory_available, | |
| 120 BlobStorageContext* context, | |
| 121 const RequestMemoryCallback& request_memory) { | |
| 122 DCHECK(context); | |
| 123 DCHECK(async_blob_map_.find(uuid) != async_blob_map_.end()); | |
| 124 | |
| 125 // Step 1: Get the sizes. | |
| 126 size_t shortcut_memory_size_bytes = 0; | |
| 127 uint64_t total_memory_size_bytes = 0; | 48 uint64_t total_memory_size_bytes = 0; |
| 128 if (!CalculateBlobMemorySize(elements, &shortcut_memory_size_bytes, | 49 MemoryStrategyResult memory_strategy; |
| 129 &total_memory_size_bytes)) { | 50 if (!context->memory_controller_.DecideBlobTransportationMemoryStrategy( |
| 130 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 51 elements, &total_memory_size_bytes, &memory_strategy)) { |
| 131 return BlobTransportResult::BAD_IPC; | 52 return BlobTransportResult::BAD_IPC; |
| 132 } | 53 } |
| 133 | 54 |
| 134 // Step 2: Check if we have enough memory to store the blob. | 55 // Validate that our referenced blobs. |
| 135 if (total_memory_size_bytes > memory_available) { | |
| 136 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, context); | |
| 137 return BlobTransportResult::CANCEL_MEMORY_FULL; | |
| 138 } | |
| 139 | |
| 140 // From here on, we know we can fit the blob in memory. | |
| 141 BlobBuildingState* state_ptr = async_blob_map_[uuid].get(); | |
| 142 if (!state_ptr->request_builder.requests().empty()) { | |
| 143 // Check that we're not a duplicate call. | |
| 144 return BlobTransportResult::BAD_IPC; | |
| 145 } | |
| 146 state_ptr->request_memory_callback = request_memory; | |
| 147 | |
| 148 // Step 3: Check to make sure the referenced blob information we received | |
| 149 // earlier is correct: | |
| 150 std::set<std::string> extracted_blob_uuids; | 56 std::set<std::string> extracted_blob_uuids; |
|
kinuko
2016/07/07 16:11:44
not used
dmurph
2016/07/08 01:22:12
Done.
| |
| 151 for (const DataElement& e : elements) { | 57 for (const DataElement& e : elements) { |
| 152 if (e.type() == DataElement::TYPE_BLOB) { | 58 if (e.type() == DataElement::TYPE_BLOB) { |
| 153 extracted_blob_uuids.insert(e.blob_uuid()); | |
| 154 // We can't depend on ourselves. | |
| 155 if (e.blob_uuid() == uuid) { | 59 if (e.blob_uuid() == uuid) { |
| 156 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | |
| 157 return BlobTransportResult::BAD_IPC; | 60 return BlobTransportResult::BAD_IPC; |
| 158 } | 61 } |
| 159 } | 62 } |
| 160 } | 63 } |
| 161 if (extracted_blob_uuids != state_ptr->referenced_blob_uuids) { | 64 |
| 162 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 65 const BlobMemoryController& memory_controller = context->memory_controller(); |
| 163 return BlobTransportResult::BAD_IPC; | 66 std::unique_ptr<BlobBuildingState> state(new BlobBuildingState(uuid)); |
| 67 state->request_memory_callback = request_memory; | |
| 68 | |
| 69 switch (memory_strategy) { | |
| 70 case MemoryStrategyResult::TOO_LARGE: | |
| 71 *handle_output = | |
| 72 context->BuildBrokenBlob(uuid, content_type, content_disposition, | |
| 73 IPCBlobCreationCancelCode::OUT_OF_MEMORY); | |
| 74 return BlobTransportResult::CANCEL_MEMORY_FULL; | |
| 75 case MemoryStrategyResult::SHORTCUT: | |
| 76 case MemoryStrategyResult::NONE_NEEDED: { | |
| 77 for (const DataElement& e : elements) { | |
| 78 DCHECK_NE(e.type(), DataElement::TYPE_BYTES_DESCRIPTION); | |
| 79 state->data_builder.AppendIPCDataElement(e); | |
| 80 } | |
| 81 *handle_output = context->BuildBlob( | |
| 82 state->data_builder, BlobStorageRegistry::BlobConstructedCallback()); | |
| 83 return BlobTransportResult::DONE; | |
| 84 } | |
| 85 case MemoryStrategyResult::IPC: | |
| 86 state->strategy = IPCBlobItemRequestStrategy::IPC; | |
| 87 state->request_builder.InitializeForIPCRequests( | |
| 88 memory_controller.max_ipc_memory_size(), total_memory_size_bytes, | |
| 89 elements, &(state->data_builder)); | |
| 90 break; | |
| 91 case MemoryStrategyResult::SHARED_MEMORY: | |
| 92 state->strategy = IPCBlobItemRequestStrategy::SHARED_MEMORY; | |
| 93 state->request_builder.InitializeForSharedMemoryRequests( | |
| 94 memory_controller.max_shared_memory_size(), total_memory_size_bytes, | |
| 95 elements, &(state->data_builder)); | |
| 96 break; | |
| 97 case MemoryStrategyResult::FILE: | |
| 98 state->strategy = IPCBlobItemRequestStrategy::FILE; | |
| 99 state->request_builder.InitializeForFileRequests( | |
| 100 memory_controller.max_file_size(), total_memory_size_bytes, elements, | |
| 101 &(state->data_builder)); | |
| 102 break; | |
| 103 } | |
| 104 // We initialize our requests received state now that they are populated. | |
| 105 state->request_received.resize(state->request_builder.requests().size(), | |
| 106 false); | |
| 107 | |
| 108 BlobBuildingState* state_ptr = state.get(); | |
| 109 async_blob_map_[uuid] = std::move(state); | |
| 110 | |
| 111 if (memory_strategy == MemoryStrategyResult::FILE) { | |
| 112 LOG(ERROR) << "file!"; | |
| 113 const auto& file_sizes = state_ptr->request_builder.file_sizes(); | |
| 114 state_ptr->files.resize(file_sizes.size()); | |
| 115 for (size_t i = 0; i < file_sizes.size(); i++) { | |
| 116 context->memory_controller_.CreateTemporaryFileForRenderer( | |
| 117 file_sizes[i], base::Bind(&BlobAsyncBuilderHost::OnFileCreated, | |
| 118 ptr_factory_.GetWeakPtr(), uuid, i)); | |
| 119 } | |
| 120 *handle_output = context->BuildBlob( | |
| 121 state_ptr->data_builder, | |
| 122 base::Callback<void(bool, IPCBlobCreationCancelCode)>()); | |
|
michaeln
2016/07/07 20:05:22
this and line 81/82 should probably look alike
dmurph
2016/07/11 23:33:26
Done.
| |
| 123 return BlobTransportResult::PENDING_RESPONSES; | |
| 164 } | 124 } |
| 165 | 125 |
| 166 // Step 4: Decide if we're using the shortcut method. This will also catch | 126 state_ptr->request_memory_callback = request_memory; |
| 167 // the case where we don't have any memory items. | 127 state_ptr->error_callback = report_error; |
| 168 if (shortcut_memory_size_bytes == total_memory_size_bytes && | 128 state_ptr->done_callback = done_callback; |
| 169 shortcut_memory_size_bytes <= memory_available) { | 129 |
| 170 for (const DataElement& e : elements) { | 130 *handle_output = context->BuildBlob( |
| 171 state_ptr->data_builder.AppendIPCDataElement(e); | 131 state_ptr->data_builder, |
| 132 base::Bind(&BlobAsyncBuilderHost::OnCanStartBuildingBlob, | |
| 133 ptr_factory_.GetWeakPtr(), uuid, context->AsWeakPtr())); | |
| 134 | |
| 135 if ((*handle_output)->IsBroken()) { | |
| 136 switch ((*handle_output)->GetBrokenReason()) { | |
| 137 case IPCBlobCreationCancelCode::OUT_OF_MEMORY: | |
| 138 return BlobTransportResult::CANCEL_MEMORY_FULL; | |
| 139 case IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN: | |
| 140 return BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN; | |
| 141 // We don't handle the following cases as they can't happen during | |
| 142 // BlobStorageContext::BuildBlob. | |
| 143 case IPCBlobCreationCancelCode::UNKNOWN: | |
| 144 case IPCBlobCreationCancelCode::FILE_WRITE_FAILED: | |
| 145 case IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT: | |
| 146 case IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING: | |
| 147 NOTREACHED(); | |
| 172 } | 148 } |
| 173 FinishBuildingBlob(state_ptr, context); | 149 return BlobTransportResult::CANCEL_UNKNOWN; |
| 174 return BlobTransportResult::DONE; | |
| 175 } | 150 } |
| 176 | 151 return BlobTransportResult::PENDING_RESPONSES; |
| 177 // From here on, we know the blob's size is less than |memory_available|, | |
| 178 // so we know we're < max(size_t). | |
| 179 // Step 5: Decide if we're using shared memory. | |
| 180 if (total_memory_size_bytes > max_ipc_memory_size_) { | |
| 181 state_ptr->request_builder.InitializeForSharedMemoryRequests( | |
| 182 max_shared_memory_size_, total_memory_size_bytes, elements, | |
| 183 &(state_ptr->data_builder)); | |
| 184 } else { | |
| 185 // Step 6: We can fit in IPC. | |
| 186 state_ptr->request_builder.InitializeForIPCRequests( | |
| 187 max_ipc_memory_size_, total_memory_size_bytes, elements, | |
| 188 &(state_ptr->data_builder)); | |
| 189 } | |
| 190 // We initialize our requests received state now that they are populated. | |
| 191 state_ptr->request_received.resize( | |
| 192 state_ptr->request_builder.requests().size(), false); | |
| 193 return ContinueBlobMemoryRequests(uuid, context); | |
| 194 } | 152 } |
| 195 | 153 |
| 196 BlobTransportResult BlobAsyncBuilderHost::OnMemoryResponses( | 154 BlobTransportResult BlobAsyncBuilderHost::OnMemoryResponses( |
| 197 const std::string& uuid, | 155 const std::string& uuid, |
| 198 const std::vector<BlobItemBytesResponse>& responses, | 156 const std::vector<BlobItemBytesResponse>& responses, |
| 199 BlobStorageContext* context) { | 157 BlobStorageContext* context) { |
| 200 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); | 158 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); |
| 201 if (state_it == async_blob_map_.end()) { | 159 if (state_it == async_blob_map_.end()) { |
| 202 DVLOG(1) << "Could not find blob " << uuid; | 160 DVLOG(1) << "Could not find blob " << uuid; |
| 203 return BlobTransportResult::BAD_IPC; | 161 return BlobTransportResult::BAD_IPC; |
| 204 } | 162 } |
| 205 if (responses.empty()) { | 163 if (responses.empty()) { |
| 206 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 164 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
| 207 return BlobTransportResult::BAD_IPC; | 165 return BlobTransportResult::BAD_IPC; |
| 208 } | 166 } |
| 167 | |
| 209 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); | 168 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); |
| 210 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; | 169 const auto& requests = state->request_builder.requests(); |
| 211 const auto& requests = request_builder.requests(); | |
| 212 for (const BlobItemBytesResponse& response : responses) { | 170 for (const BlobItemBytesResponse& response : responses) { |
| 213 if (response.request_number >= requests.size()) { | 171 if (response.request_number >= requests.size()) { |
| 214 // Bad IPC, so we delete our record and ignore. | 172 // Bad IPC, so we delete our record and ignore. |
| 215 DVLOG(1) << "Invalid request number " << response.request_number; | 173 DVLOG(1) << "Invalid request number " << response.request_number; |
| 216 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 174 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
| 217 return BlobTransportResult::BAD_IPC; | 175 return BlobTransportResult::BAD_IPC; |
| 218 } | 176 } |
| 219 DCHECK_LT(response.request_number, state->request_received.size()); | 177 DCHECK_LT(response.request_number, state->request_received.size()); |
| 220 const MemoryItemRequest& request = requests[response.request_number]; | |
| 221 if (state->request_received[response.request_number]) { | 178 if (state->request_received[response.request_number]) { |
| 222 // Bad IPC, so we delete our record. | 179 // Bad IPC, so we delete our record. |
| 223 DVLOG(1) << "Already received response for that request."; | 180 DVLOG(1) << "Already received response for that request."; |
| 224 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 181 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
| 225 return BlobTransportResult::BAD_IPC; | 182 return BlobTransportResult::BAD_IPC; |
| 226 } | 183 } |
| 227 state->request_received[response.request_number] = true; | 184 state->request_received[response.request_number] = true; |
| 228 bool invalid_ipc = false; | 185 } |
| 229 bool memory_error = false; | 186 switch (state->strategy) { |
| 230 switch (request.message.transport_strategy) { | 187 case IPCBlobItemRequestStrategy::IPC: |
| 231 case IPCBlobItemRequestStrategy::IPC: | 188 return OnIPCResponses(uuid, state, responses, context); |
| 232 if (response.inline_data.size() < request.message.size) { | 189 case IPCBlobItemRequestStrategy::SHARED_MEMORY: |
| 233 DVLOG(1) << "Invalid data size " << response.inline_data.size() | 190 return OnSharedMemoryResponses(uuid, state, responses, context); |
| 234 << " vs requested size of " << request.message.size; | 191 case IPCBlobItemRequestStrategy::FILE: |
| 235 invalid_ipc = true; | 192 return OnFileResponses(uuid, state, responses, context); |
| 236 break; | 193 case IPCBlobItemRequestStrategy::UNKNOWN: |
| 237 } | 194 NOTREACHED(); |
| 238 invalid_ipc = !state->data_builder.PopulateFutureData( | |
| 239 request.browser_item_index, &response.inline_data[0], | |
| 240 request.browser_item_offset, request.message.size); | |
| 241 break; | |
| 242 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | |
| 243 if (state->num_shared_memory_requests == 0) { | |
| 244 DVLOG(1) << "Received too many responses for shared memory."; | |
| 245 invalid_ipc = true; | |
| 246 break; | |
| 247 } | |
| 248 state->num_shared_memory_requests--; | |
| 249 if (!state->shared_memory_block->memory()) { | |
| 250 // We just map the whole block, as we'll probably be accessing the | |
| 251 // whole thing in this group of responses. Another option is to use | |
| 252 // MapAt, remove the mapped boolean, and then exclude the | |
| 253 // handle_offset below. | |
| 254 size_t handle_size = request_builder.shared_memory_sizes() | |
| 255 [state->current_shared_memory_handle_index]; | |
| 256 if (!state->shared_memory_block->Map(handle_size)) { | |
| 257 DVLOG(1) << "Unable to map memory to size " << handle_size; | |
| 258 memory_error = true; | |
| 259 break; | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 invalid_ipc = !state->data_builder.PopulateFutureData( | |
| 264 request.browser_item_index, | |
| 265 static_cast<const char*>(state->shared_memory_block->memory()) + | |
| 266 request.message.handle_offset, | |
| 267 request.browser_item_offset, request.message.size); | |
| 268 break; | |
| 269 case IPCBlobItemRequestStrategy::FILE: | |
| 270 case IPCBlobItemRequestStrategy::UNKNOWN: | |
| 271 DVLOG(1) << "Not implemented."; | |
| 272 invalid_ipc = true; | |
| 273 break; | |
| 274 } | |
| 275 if (invalid_ipc) { | |
| 276 // Bad IPC, so we delete our record and return false. | |
| 277 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | |
| 278 return BlobTransportResult::BAD_IPC; | 195 return BlobTransportResult::BAD_IPC; |
| 279 } | |
| 280 if (memory_error) { | |
| 281 DVLOG(1) << "Shared memory error."; | |
| 282 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, | |
| 283 context); | |
| 284 return BlobTransportResult::CANCEL_MEMORY_FULL; | |
| 285 } | |
| 286 state->num_fulfilled_requests++; | |
| 287 } | 196 } |
| 288 return ContinueBlobMemoryRequests(uuid, context); | |
| 289 } | 197 } |
| 290 | 198 |
| 291 void BlobAsyncBuilderHost::CancelBuildingBlob(const std::string& uuid, | 199 void BlobAsyncBuilderHost::CancelBuildingBlob(const std::string& uuid, |
| 292 IPCBlobCreationCancelCode code, | 200 IPCBlobCreationCancelCode code, |
| 293 BlobStorageContext* context) { | 201 BlobStorageContext* context) { |
| 294 DCHECK(context); | 202 DCHECK(context); |
| 295 auto state_it = async_blob_map_.find(uuid); | 203 auto state_it = async_blob_map_.find(uuid); |
| 296 if (state_it == async_blob_map_.end()) { | 204 if (state_it == async_blob_map_.end()) { |
| 297 return; | 205 return; |
| 298 } | 206 } |
| 299 // We can have the blob dereferenced by the renderer, but have it still being | 207 // We can have the blob dereferenced by the renderer, but have it still being |
| 300 // 'built'. In this case, it's destructed in the context, but we still have | 208 // 'built'. In this case, it's destructed in the context, but we still have |
| 301 // it in our map. Hence we make sure the context has the entry before | 209 // it in our map. Hence we make sure the context has the entry before |
| 302 // calling cancel. | 210 // calling cancel. |
| 303 if (context->registry().HasEntry(uuid)) | 211 if (context->registry().HasEntry(uuid)) { |
| 304 context->CancelPendingBlob(uuid, code); | 212 context->BreakAndFinishBlob(uuid, code); |
| 213 } | |
| 305 async_blob_map_.erase(state_it); | 214 async_blob_map_.erase(state_it); |
| 306 } | 215 } |
| 307 | 216 |
| 308 void BlobAsyncBuilderHost::CancelAll(BlobStorageContext* context) { | 217 void BlobAsyncBuilderHost::CancelAll(BlobStorageContext* context) { |
| 309 DCHECK(context); | 218 DCHECK(context); |
| 310 // If the blob still exists in the context (and is being built), then we know | 219 // If the blob still exists in the context (and is being built), then we know |
| 311 // that someone else is expecting our blob, and we need to cancel it to let | 220 // that someone else is expecting our blob, and we need to cancel it to let |
| 312 // the dependency know it's gone. | 221 // the dependency know it's gone. |
| 313 std::vector<std::unique_ptr<BlobDataHandle>> referenced_pending_blobs; | 222 std::vector<std::unique_ptr<BlobDataHandle>> referenced_pending_blobs; |
| 314 for (const auto& uuid_state_pair : async_blob_map_) { | 223 for (const auto& uuid_state_pair : async_blob_map_) { |
| 315 if (context->IsBeingBuilt(uuid_state_pair.first)) { | 224 if (context->IsBeingBuilt(uuid_state_pair.first)) { |
| 316 referenced_pending_blobs.emplace_back( | 225 referenced_pending_blobs.emplace_back( |
| 317 context->GetBlobDataFromUUID(uuid_state_pair.first)); | 226 context->GetBlobDataFromUUID(uuid_state_pair.first)); |
| 318 } | 227 } |
| 319 } | 228 } |
| 320 // We clear the map before canceling them to prevent any strange reentry into | 229 // We clear the map before canceling them to prevent any strange reentry into |
| 321 // our class (see ReferencedBlobFinished) if any blobs were waiting for others | 230 // our class (see ReferencedBlobFinished) if any blobs were waiting for others |
| 322 // to construct. | 231 // to construct. |
| 323 async_blob_map_.clear(); | 232 async_blob_map_.clear(); |
| 324 for (const std::unique_ptr<BlobDataHandle>& handle : | 233 for (const std::unique_ptr<BlobDataHandle>& handle : |
| 325 referenced_pending_blobs) { | 234 referenced_pending_blobs) { |
| 326 context->CancelPendingBlob( | 235 context->BreakAndFinishBlob( |
| 327 handle->uuid(), IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT); | 236 handle->uuid(), IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT); |
| 328 } | 237 } |
| 329 } | 238 } |
| 330 | 239 |
| 331 BlobTransportResult BlobAsyncBuilderHost::ContinueBlobMemoryRequests( | 240 BlobTransportResult BlobAsyncBuilderHost::StartRequests( |
| 332 const std::string& uuid, | 241 const std::string& uuid, |
| 242 BlobBuildingState* state, | |
| 333 BlobStorageContext* context) { | 243 BlobStorageContext* context) { |
| 244 switch (state->strategy) { | |
| 245 case IPCBlobItemRequestStrategy::IPC: | |
| 246 SendIPCRequests(state, context); | |
| 247 return BlobTransportResult::DONE; | |
| 248 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | |
| 249 return ContinueSharedMemoryRequests(uuid, state, context); | |
| 250 case IPCBlobItemRequestStrategy::FILE: | |
| 251 case IPCBlobItemRequestStrategy::UNKNOWN: | |
| 252 NOTREACHED(); | |
| 253 return BlobTransportResult::CANCEL_UNKNOWN; | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 void BlobAsyncBuilderHost::OnCanStartBuildingBlob( | |
| 258 const std::string& uuid, | |
| 259 base::WeakPtr<BlobStorageContext> context, | |
| 260 bool success, | |
| 261 IPCBlobCreationCancelCode reason) { | |
|
michaeln
2016/07/07 20:05:22
where are |success| and |reason| used?
dmurph
2016/07/11 23:33:26
Fixed in new patch.
| |
| 262 LOG(ERROR) << "We can fit now!"; | |
| 263 if (!context) { | |
| 264 async_blob_map_.erase(uuid); | |
| 265 return; | |
| 266 } | |
| 334 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); | 267 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); |
| 335 DCHECK(state_it != async_blob_map_.end()); | 268 if (state_it == async_blob_map_.end()) { |
| 336 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); | 269 return; |
| 270 } | |
| 271 BlobBuildingState* state = state_it->second.get(); | |
| 272 BlobTransportResult result = StartRequests(uuid, state, context.get()); | |
| 273 if (result == BlobTransportResult::PENDING_RESPONSES) | |
| 274 return; | |
| 275 ErrorCallback error_callback = state->error_callback; | |
| 276 DoneCallback done_callback = state->done_callback; | |
| 277 async_blob_map_.erase(state_it); | |
| 278 switch (result) { | |
| 279 case BlobTransportResult::PENDING_RESPONSES: | |
| 280 NOTREACHED(); | |
| 281 case BlobTransportResult::CANCEL_UNKNOWN: | |
| 282 case BlobTransportResult::BAD_IPC: | |
| 283 error_callback.Run(IPCBlobCreationCancelCode::UNKNOWN); | |
| 284 break; | |
| 285 case BlobTransportResult::CANCEL_MEMORY_FULL: | |
| 286 error_callback.Run(IPCBlobCreationCancelCode::OUT_OF_MEMORY); | |
| 287 break; | |
| 288 case BlobTransportResult::CANCEL_FILE_ERROR: | |
| 289 error_callback.Run(IPCBlobCreationCancelCode::FILE_WRITE_FAILED); | |
| 290 break; | |
| 291 case BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN: | |
| 292 error_callback.Run(IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN); | |
| 293 break; | |
| 294 return; | |
| 295 case BlobTransportResult::DONE: | |
| 296 done_callback.Run(); | |
| 297 return; | |
| 298 } | |
| 299 } | |
| 337 | 300 |
| 338 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; | 301 void BlobAsyncBuilderHost::SendIPCRequests(BlobBuildingState* state, |
| 339 const std::vector<MemoryItemRequest>& requests = request_builder.requests(); | 302 BlobStorageContext* context) { |
| 303 const std::vector<MemoryItemRequest>& requests = | |
| 304 state->request_builder.requests(); | |
| 305 std::unique_ptr<std::vector<BlobItemBytesRequest>> byte_requests( | |
| 306 new std::vector<BlobItemBytesRequest>()); | |
| 307 | |
| 308 DCHECK(!requests.empty()); | |
| 309 for (const MemoryItemRequest& request : requests) { | |
| 310 byte_requests->push_back(request.message); | |
| 311 } | |
| 312 | |
| 313 state->request_memory_callback.Run( | |
| 314 std::move(byte_requests), | |
| 315 base::WrapUnique(new std::vector<base::SharedMemoryHandle>()), | |
| 316 base::WrapUnique(new std::vector<base::File>())); | |
| 317 } | |
| 318 | |
| 319 BlobTransportResult BlobAsyncBuilderHost::OnIPCResponses( | |
| 320 const std::string& uuid, | |
| 321 BlobBuildingState* state, | |
| 322 const std::vector<BlobItemBytesResponse>& responses, | |
| 323 BlobStorageContext* context) { | |
| 324 const auto& requests = state->request_builder.requests(); | |
| 340 size_t num_requests = requests.size(); | 325 size_t num_requests = requests.size(); |
| 326 for (const BlobItemBytesResponse& response : responses) { | |
| 327 const MemoryItemRequest& request = requests[response.request_number]; | |
| 328 if (response.inline_data.size() < request.message.size) { | |
| 329 DVLOG(1) << "Invalid data size " << response.inline_data.size() | |
| 330 << " vs requested size of " << request.message.size; | |
| 331 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | |
| 332 return BlobTransportResult::BAD_IPC; | |
| 333 break; | |
| 334 } | |
| 335 bool success = state->data_builder.PopulateFutureData( | |
| 336 request.browser_item_index, &response.inline_data[0], | |
| 337 request.browser_item_offset, request.message.size); | |
| 338 if (!success) { | |
| 339 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | |
| 340 return BlobTransportResult::BAD_IPC; | |
| 341 } | |
| 342 state->num_fulfilled_requests++; | |
| 343 } | |
| 341 if (state->num_fulfilled_requests == num_requests) { | 344 if (state->num_fulfilled_requests == num_requests) { |
| 342 FinishBuildingBlob(state, context); | 345 FinishBuildingBlob(state, context); |
| 343 return BlobTransportResult::DONE; | 346 return BlobTransportResult::DONE; |
| 344 } | 347 } |
| 348 return BlobTransportResult::PENDING_RESPONSES; | |
| 349 } | |
| 350 | |
| 351 BlobTransportResult BlobAsyncBuilderHost::ContinueSharedMemoryRequests( | |
| 352 const std::string& uuid, | |
| 353 BlobBuildingState* state, | |
| 354 BlobStorageContext* context) { | |
| 355 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; | |
| 356 const std::vector<MemoryItemRequest>& requests = request_builder.requests(); | |
| 357 size_t num_requests = requests.size(); | |
| 345 DCHECK_LT(state->num_fulfilled_requests, num_requests); | 358 DCHECK_LT(state->num_fulfilled_requests, num_requests); |
| 346 if (state->next_request == num_requests) { | 359 if (state->next_request == num_requests) { |
| 347 // We are still waiting on other requests to come back. | 360 // We are still waiting on other requests to come back. |
| 348 return BlobTransportResult::PENDING_RESPONSES; | 361 return BlobTransportResult::PENDING_RESPONSES; |
| 349 } | 362 } |
| 350 | 363 |
| 351 std::unique_ptr<std::vector<BlobItemBytesRequest>> byte_requests( | 364 std::unique_ptr<std::vector<BlobItemBytesRequest>> byte_requests( |
| 352 new std::vector<BlobItemBytesRequest>()); | 365 new std::vector<BlobItemBytesRequest>()); |
| 353 std::unique_ptr<std::vector<base::SharedMemoryHandle>> shared_memory( | 366 std::unique_ptr<std::vector<base::SharedMemoryHandle>> shared_memory( |
| 354 new std::vector<base::SharedMemoryHandle>()); | 367 new std::vector<base::SharedMemoryHandle>()); |
| 355 | 368 |
| 356 for (; state->next_request < num_requests; ++state->next_request) { | 369 for (; state->next_request < num_requests; ++state->next_request) { |
| 357 const MemoryItemRequest& request = requests[state->next_request]; | 370 const MemoryItemRequest& request = requests[state->next_request]; |
| 358 | |
| 359 bool stop_accumulating = false; | |
| 360 bool using_shared_memory_handle = state->num_shared_memory_requests > 0; | 371 bool using_shared_memory_handle = state->num_shared_memory_requests > 0; |
| 361 switch (request.message.transport_strategy) { | 372 if (using_shared_memory_handle && |
| 362 case IPCBlobItemRequestStrategy::IPC: | 373 state->current_shared_memory_handle_index != |
| 363 byte_requests->push_back(request.message); | 374 request.message.handle_index) { |
| 364 break; | 375 // We only want one shared memory per requesting blob. |
| 365 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | |
| 366 if (using_shared_memory_handle && | |
| 367 state->current_shared_memory_handle_index != | |
| 368 request.message.handle_index) { | |
| 369 // We only want one shared memory per requesting blob. | |
| 370 stop_accumulating = true; | |
| 371 break; | |
| 372 } | |
| 373 using_shared_memory_handle = true; | |
| 374 state->current_shared_memory_handle_index = | |
| 375 request.message.handle_index; | |
| 376 state->num_shared_memory_requests++; | |
| 377 | |
| 378 if (!state->shared_memory_block) { | |
| 379 state->shared_memory_block.reset(new base::SharedMemory()); | |
| 380 size_t size = | |
| 381 request_builder | |
| 382 .shared_memory_sizes()[request.message.handle_index]; | |
| 383 if (!state->shared_memory_block->CreateAnonymous(size)) { | |
| 384 DVLOG(1) << "Unable to allocate shared memory for blob transfer."; | |
| 385 return BlobTransportResult::CANCEL_MEMORY_FULL; | |
| 386 } | |
| 387 } | |
| 388 shared_memory->push_back(state->shared_memory_block->handle()); | |
| 389 byte_requests->push_back(request.message); | |
| 390 // Since we are only using one handle at a time, transform our handle | |
| 391 // index correctly back to 0. | |
| 392 byte_requests->back().handle_index = 0; | |
| 393 break; | |
| 394 case IPCBlobItemRequestStrategy::FILE: | |
| 395 case IPCBlobItemRequestStrategy::UNKNOWN: | |
| 396 NOTREACHED() << "Not implemented yet."; | |
| 397 break; | |
| 398 } | |
| 399 if (stop_accumulating) { | |
| 400 break; | 376 break; |
| 401 } | 377 } |
| 378 state->current_shared_memory_handle_index = request.message.handle_index; | |
| 379 state->num_shared_memory_requests++; | |
| 380 | |
| 381 if (!state->shared_memory_block) { | |
| 382 state->shared_memory_block.reset(new base::SharedMemory()); | |
| 383 size_t size = | |
| 384 request_builder.shared_memory_sizes()[request.message.handle_index]; | |
| 385 if (!state->shared_memory_block->CreateAnonymous(size)) { | |
| 386 DVLOG(1) << "Unable to allocate shared memory for blob transfer."; | |
| 387 return BlobTransportResult::CANCEL_MEMORY_FULL; | |
| 388 } | |
| 389 } | |
| 390 shared_memory->push_back(state->shared_memory_block->handle()); | |
| 391 byte_requests->push_back(request.message); | |
| 392 // Since we are only using one handle at a time, transform our handle | |
| 393 // index correctly back to 0. | |
| 394 byte_requests->back().handle_index = 0; | |
| 402 } | 395 } |
| 403 DCHECK(!requests.empty()); | 396 DCHECK(!requests.empty()); |
| 404 | 397 |
| 405 state->request_memory_callback.Run( | 398 state->request_memory_callback.Run( |
| 406 std::move(byte_requests), std::move(shared_memory), | 399 std::move(byte_requests), std::move(shared_memory), |
| 407 base::WrapUnique(new std::vector<base::File>())); | 400 base::WrapUnique(new std::vector<base::File>())); |
| 408 return BlobTransportResult::PENDING_RESPONSES; | 401 return BlobTransportResult::PENDING_RESPONSES; |
| 409 } | 402 } |
| 410 | 403 |
| 411 void BlobAsyncBuilderHost::ReferencedBlobFinished( | 404 BlobTransportResult BlobAsyncBuilderHost::OnSharedMemoryResponses( |
| 412 const std::string& owning_blob_uuid, | 405 const std::string& uuid, |
| 413 base::WeakPtr<BlobStorageContext> context, | 406 BlobBuildingState* state, |
| 414 bool construction_success, | 407 const std::vector<BlobItemBytesResponse>& responses, |
| 415 IPCBlobCreationCancelCode reason) { | 408 BlobStorageContext* context) { |
| 416 if (!context) { | 409 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; |
| 417 return; | 410 const auto& requests = request_builder.requests(); |
| 411 for (const BlobItemBytesResponse& response : responses) { | |
| 412 const MemoryItemRequest& request = requests[response.request_number]; | |
| 413 if (state->num_shared_memory_requests == 0) { | |
| 414 DVLOG(1) << "Received too many responses for shared memory."; | |
| 415 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | |
| 416 return BlobTransportResult::BAD_IPC; | |
| 417 break; | |
| 418 } | |
| 419 state->num_shared_memory_requests--; | |
| 420 if (!state->shared_memory_block->memory()) { | |
| 421 // We just map the whole block, as we'll probably be accessing the | |
| 422 // whole thing in this group of responses. Another option is to use | |
| 423 // MapAt, remove the mapped boolean, and then exclude the | |
| 424 // handle_offset below. | |
| 425 size_t handle_size = | |
| 426 request_builder | |
| 427 .shared_memory_sizes()[state->current_shared_memory_handle_index]; | |
| 428 if (!state->shared_memory_block->Map(handle_size)) { | |
| 429 DVLOG(1) << "Unable to map memory to size " << handle_size; | |
| 430 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, | |
| 431 context); | |
| 432 return BlobTransportResult::CANCEL_MEMORY_FULL; | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 bool success = state->data_builder.PopulateFutureData( | |
| 437 request.browser_item_index, | |
| 438 static_cast<const char*>(state->shared_memory_block->memory()) + | |
| 439 request.message.handle_offset, | |
| 440 request.browser_item_offset, request.message.size); | |
| 441 | |
| 442 if (!success) { | |
| 443 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | |
| 444 return BlobTransportResult::BAD_IPC; | |
| 445 } | |
| 446 state->num_fulfilled_requests++; | |
| 418 } | 447 } |
| 419 auto state_it = async_blob_map_.find(owning_blob_uuid); | 448 if (state->num_fulfilled_requests == requests.size()) { |
| 449 FinishBuildingBlob(state, context); | |
| 450 return BlobTransportResult::DONE; | |
| 451 } | |
| 452 return ContinueSharedMemoryRequests(uuid, state, context); | |
| 453 } | |
| 454 | |
| 455 void BlobAsyncBuilderHost::OnFileCreated( | |
| 456 const std::string& uuid, | |
| 457 size_t handle_index, | |
| 458 BlobMemoryController::FileCreationInfo file_info) { | |
| 459 LOG(ERROR) << "File crated!"; | |
| 460 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); | |
| 420 if (state_it == async_blob_map_.end()) { | 461 if (state_it == async_blob_map_.end()) { |
| 421 return; | 462 return; |
| 422 } | 463 } |
| 423 if (!construction_success) { | 464 BlobBuildingState* state = state_it->second.get(); |
| 424 CancelBuildingBlob(owning_blob_uuid, | 465 DCHECK_LT(handle_index, state->files.size()); |
| 425 ConvertReferencedBlobErrorToConstructingError(reason), | 466 state->files[handle_index] = std::move(file_info.file_reference); |
| 426 context.get()); | 467 |
| 427 return; | 468 const BlobAsyncTransportRequestBuilder& request_builder = |
| 469 state->request_builder; | |
| 470 const std::vector<MemoryItemRequest>& requests = request_builder.requests(); | |
| 471 | |
| 472 std::unique_ptr<std::vector<BlobItemBytesRequest>> byte_requests( | |
| 473 new std::vector<BlobItemBytesRequest>()); | |
| 474 | |
| 475 std::unique_ptr<std::vector<base::File>> files(new std::vector<base::File>()); | |
| 476 files->push_back(std::move(file_info.file.value())); | |
| 477 file_info.file = base::nullopt; | |
| 478 | |
| 479 for (const MemoryItemRequest& request : requests) { | |
| 480 if (request.message.handle_index != handle_index) { | |
| 481 continue; | |
| 482 } | |
| 483 byte_requests->push_back(request.message); | |
| 484 byte_requests->back().handle_index = 0; | |
| 428 } | 485 } |
| 429 BlobBuildingState* state = state_it->second.get(); | 486 |
| 430 DCHECK_GT(state->num_referenced_blobs_building, 0u); | 487 state->request_memory_callback.Run( |
| 431 if (--state->num_referenced_blobs_building == 0) { | 488 std::move(byte_requests), |
| 432 context->CompletePendingBlob(state->data_builder); | 489 base::WrapUnique(new std::vector<base::SharedMemoryHandle>()), |
| 433 async_blob_map_.erase(state->data_builder.uuid()); | 490 std::move(files)); |
| 491 } | |
| 492 | |
| 493 BlobTransportResult BlobAsyncBuilderHost::OnFileResponses( | |
| 494 const std::string& uuid, | |
| 495 BlobBuildingState* state, | |
| 496 const std::vector<BlobItemBytesResponse>& responses, | |
| 497 BlobStorageContext* context) { | |
| 498 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; | |
| 499 const auto& requests = request_builder.requests(); | |
| 500 for (const BlobItemBytesResponse& response : responses) { | |
| 501 const MemoryItemRequest& request = requests[response.request_number]; | |
| 502 const scoped_refptr<ShareableFileReference>& file_ref = | |
| 503 state->files[request.message.handle_index]; | |
| 504 bool success = state->data_builder.PopulateFutureFile( | |
| 505 request.browser_item_index, file_ref, response.time_file_modified); | |
| 506 if (!success) { | |
| 507 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | |
| 508 return BlobTransportResult::BAD_IPC; | |
| 509 } | |
| 510 state->num_fulfilled_requests++; | |
| 434 } | 511 } |
| 512 if (state->num_fulfilled_requests == requests.size()) { | |
| 513 FinishBuildingBlob(state, context); | |
| 514 return BlobTransportResult::DONE; | |
| 515 } | |
| 516 return BlobTransportResult::PENDING_RESPONSES; | |
| 435 } | 517 } |
| 436 | 518 |
| 437 void BlobAsyncBuilderHost::FinishBuildingBlob(BlobBuildingState* state, | 519 void BlobAsyncBuilderHost::FinishBuildingBlob(BlobBuildingState* state, |
| 438 BlobStorageContext* context) { | 520 BlobStorageContext* context) { |
| 439 if (!state->referenced_blob_uuids.empty()) { | 521 context->FinishedPopulatingBlob(state->data_builder.uuid()); |
| 440 DCHECK_EQ(0u, state->num_referenced_blobs_building); | |
| 441 state->num_referenced_blobs_building = 0; | |
| 442 // We assume re-entry is not possible, as RunOnConstructionComplete | |
| 443 // will schedule a task when the blob is being built. Thus we can't have the | |
| 444 // case where |num_referenced_blobs_building| reaches 0 in the | |
| 445 // ReferencedBlobFinished method before we're finished looping. | |
| 446 for (const std::string& referenced_uuid : state->referenced_blob_uuids) { | |
| 447 if (context->IsBeingBuilt(referenced_uuid)) { | |
| 448 state->num_referenced_blobs_building++; | |
| 449 context->RunOnConstructionComplete( | |
| 450 referenced_uuid, | |
| 451 base::Bind(&BlobAsyncBuilderHost::ReferencedBlobFinished, | |
| 452 ptr_factory_.GetWeakPtr(), state->data_builder.uuid(), | |
| 453 context->AsWeakPtr())); | |
| 454 } | |
| 455 } | |
| 456 if (state->num_referenced_blobs_building > 0) { | |
| 457 // We wait until referenced blobs are done. | |
| 458 return; | |
| 459 } | |
| 460 } | |
| 461 context->CompletePendingBlob(state->data_builder); | |
| 462 async_blob_map_.erase(state->data_builder.uuid()); | 522 async_blob_map_.erase(state->data_builder.uuid()); |
| 463 } | 523 } |
| 464 | 524 |
| 465 } // namespace storage | 525 } // namespace storage |
| OLD | NEW |