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

Side by Side Diff: storage/browser/blob/blob_async_builder_host.cc

Issue 2055053003: [BlobAsync] Disk support for blob storage (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: comments, simplifications Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698