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

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

Issue 2448353002: [BlobAsync] Moving async handling into BlobStorageContext & quota out. (Closed)
Patch Set: Fixed unused function Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include <stddef.h>
6 #include <stdint.h>
7
8 #include <memory>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/shared_memory.h"
14 #include "storage/browser/blob/blob_data_handle.h"
15 #include "storage/browser/blob/blob_memory_controller.h"
16 #include "storage/browser/blob/blob_storage_context.h"
17 #include "storage/browser/blob/blob_transport_host.h"
18
19 namespace storage {
20 namespace {
21 using MemoryStrategy = BlobMemoryController::Strategy;
22 using MemoryItemRequest =
23 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest;
24
25 bool CalculateBlobMemorySize(const std::vector<DataElement>& elements,
26 size_t* shortcut_bytes,
27 uint64_t* total_bytes) {
28 DCHECK(shortcut_bytes);
29 DCHECK(total_bytes);
30
31 base::CheckedNumeric<uint64_t> total_size_checked = 0;
32 base::CheckedNumeric<size_t> shortcut_size_checked = 0;
33 for (const auto& e : elements) {
34 if (e.type() == DataElement::TYPE_BYTES) {
35 total_size_checked += e.length();
36 shortcut_size_checked += e.length();
37 } else if (e.type() == DataElement::TYPE_BYTES_DESCRIPTION) {
38 total_size_checked += e.length();
39 } else {
40 continue;
41 }
42 if (!total_size_checked.IsValid() || !shortcut_size_checked.IsValid())
43 return false;
44 }
45 *shortcut_bytes = shortcut_size_checked.ValueOrDie();
46 *total_bytes = total_size_checked.ValueOrDie();
47 return true;
48 }
49 } // namespace
50
51 BlobTransportHost::TransportState::TransportState(
52 const std::string& uuid, const std::string& content_type,
53 const std::string& content_disposition,
54 RequestMemoryCallback request_memory_callback,
55 BlobStatusCallback completion_callback)
56 : data_builder(uuid),
57 request_memory_callback(std::move(request_memory_callback)),
58 completion_callback(std::move(completion_callback)){
59 data_builder.set_content_type(content_type);
60 data_builder.set_content_disposition(content_disposition);
61 }
62
63 BlobTransportHost::TransportState::~TransportState() {}
64
65 BlobTransportHost::BlobTransportHost() : ptr_factory_(this) {}
66
67 BlobTransportHost::~BlobTransportHost() {}
68
69 void BlobTransportHost::StartBuildingBlob(
70 const std::string& uuid, const std::string& content_type,
71 const std::string& content_disposition,
72 const std::vector<DataElement>& elements, BlobStorageContext* context,
73 const RequestMemoryCallback& request_memory,
74 const BlobStatusCallback& completion_callback) {
75 DCHECK(context);
76 if (async_blob_map_.find(uuid) != async_blob_map_.end()) {
77 completion_callback.Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
78 return;
79 }
80 // Validate that our referenced blobs aren't us.
81 for (const DataElement& e : elements) {
82 if (e.type() == DataElement::TYPE_BLOB && e.blob_uuid() == uuid) {
83 completion_callback.Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
84 return;
85 }
86 }
87 uint64_t transport_memory_size = 0;
88 size_t shortcut_size = 0;
89 if (!CalculateBlobMemorySize(elements, &shortcut_size,
90 &transport_memory_size)) {
91 completion_callback.Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
92 return;
93 }
94
95 const BlobMemoryController& memory_controller = context->memory_controller();
96 MemoryStrategy memory_strategy =
97 memory_controller.DetermineStrategy(shortcut_size, transport_memory_size);
98 std::unique_ptr<TransportState> state(
99 new TransportState(uuid, content_type, content_disposition,
100 request_memory, completion_callback));
101 std::unique_ptr<BlobDataHandle> temp_handle;
102 switch (memory_strategy) {
103 case MemoryStrategy::TOO_LARGE:
104 temp_handle =
105 context->AddBrokenBlob(uuid, content_type, content_disposition,
106 BlobStatus::ERR_OUT_OF_MEMORY);
107 context->IncrementBlobRefCount(uuid);
108 completion_callback.Run(BlobStatus::ERR_OUT_OF_MEMORY);
109 return;
110 case MemoryStrategy::NONE_NEEDED: {
111 for (const DataElement& e : elements) {
112 DCHECK_NE(e.type(), DataElement::TYPE_BYTES_DESCRIPTION);
113 state->data_builder.AppendIPCDataElement(e);
114 }
115 temp_handle =
116 context->BuildBlob(state->data_builder,
michaeln 2016/11/07 21:47:05 naming nit: similar naming across levels might hel
117 BlobStorageContext::PopulatationAllowedCallback());
118 context->IncrementBlobRefCount(uuid);
119 completion_callback.Run(BlobStatus::DONE);
120 return;
121 }
122 case MemoryStrategy::IPC:
123 state->strategy = IPCBlobItemRequestStrategy::IPC;
124 state->request_builder.InitializeForIPCRequests(
125 memory_controller.limits().max_ipc_memory_size, transport_memory_size,
126 elements, &(state->data_builder));
127 break;
128 case MemoryStrategy::SHARED_MEMORY:
129 state->strategy = IPCBlobItemRequestStrategy::SHARED_MEMORY;
130 state->request_builder.InitializeForSharedMemoryRequests(
131 memory_controller.limits().max_shared_memory_size,
132 transport_memory_size, elements, &(state->data_builder));
133 break;
134 case MemoryStrategy::FILE:
135 state->strategy = IPCBlobItemRequestStrategy::FILE;
136 state->request_builder.InitializeForFileRequests(
137 memory_controller.limits().max_file_size, transport_memory_size,
138 elements, &(state->data_builder));
139 break;
140 }
141 // We initialize our requests received state now that they are populated.
142 state->request_received.resize(state->request_builder.requests().size(),
143 false);
144 TransportState* state_ptr = state.get();
145 async_blob_map_[uuid] = std::move(state);
146 temp_handle = context->BuildBlob(
147 state_ptr->data_builder,
148 base::Bind(&BlobTransportHost::OnPopulationAllowed,
149 ptr_factory_.GetWeakPtr(), uuid, context->AsWeakPtr()));
150 context->IncrementBlobRefCount(uuid);
151 BlobStatus status = temp_handle->GetBlobStatus();
152 if (BlobStatusIsError(status)) async_blob_map_.erase(uuid);
153
154 if (!BlobStatusIsPending(status))
155 completion_callback.Run(temp_handle->GetBlobStatus());
156
157 return;
158 }
159
160 BlobStatus BlobTransportHost::OnMemoryResponses(
161 const std::string& uuid,
162 const std::vector<BlobItemBytesResponse>& responses,
163 BlobStorageContext* context) {
164 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid);
165 if (state_it == async_blob_map_.end()) {
166 DVLOG(1) << "Could not find blob " << uuid;
167 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
168 }
169 if (responses.empty()) {
170 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
171 context);
172 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
173 }
174
175 // Validate response sanity: it should refer to a legal request number, and
176 // we shouldn't have received an answer for that request yet.
177 BlobTransportHost::TransportState* state = state_it->second.get();
178 const auto& requests = state->request_builder.requests();
179 for (const BlobItemBytesResponse& response : responses) {
180 if (response.request_number >= requests.size()) {
181 // Bad IPC, so we delete our record and ignore.
182 DVLOG(1) << "Invalid request number " << response.request_number;
183 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
184 context);
185 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
186 }
187 DCHECK_LT(response.request_number, state->request_received.size());
188 if (state->request_received[response.request_number]) {
189 // Bad IPC, so we delete our record.
190 DVLOG(1) << "Already received response for that request.";
191 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
192 context);
193 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
194 }
195 state->request_received[response.request_number] = true;
196 }
197 switch (state->strategy) {
198 case IPCBlobItemRequestStrategy::IPC:
199 return OnIPCResponses(uuid, state, responses, context);
200 case IPCBlobItemRequestStrategy::SHARED_MEMORY:
201 return OnSharedMemoryResponses(uuid, state, responses, context);
202 case IPCBlobItemRequestStrategy::FILE:
203 return OnFileResponses(uuid, state, responses, context);
204 case IPCBlobItemRequestStrategy::UNKNOWN:
205 break;
206 }
207 NOTREACHED();
208 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
209 }
210
211 void BlobTransportHost::CancelBuildingBlob(const std::string& uuid,
212 BlobStatus code,
213 BlobStorageContext* context) {
214 DCHECK(context);
215 DCHECK(BlobStatusIsError(code));
216 auto state_it = async_blob_map_.find(uuid);
217 if (state_it == async_blob_map_.end())
218 return;
219 // We can have the blob dereferenced by the renderer, but have it still being
220 // 'built'. In this case, it's destructed in the context, but we still have
221 // it in our map. Hence we make sure the context has the entry before
222 // calling cancel.
223 async_blob_map_.erase(state_it);
224 if (context->registry().HasEntry(uuid))
225 context->BreakAndFinishPendingBlob(uuid, code);
michaeln 2016/11/07 21:47:05 naming nit: similar naming across levels might hel
dmurph 2016/11/08 21:19:59 Done.
226 }
227
228 void BlobTransportHost::CancelAll(BlobStorageContext* context) {
229 DCHECK(context);
230 // If the blob still exists in the context, then we know that someone else is
231 // expecting our blob, and we need to cancel it to let the dependency know
232 // it's gone.
233 std::vector<std::unique_ptr<BlobDataHandle>> referenced_pending_blobs;
234 for (const auto& uuid_state_pair : async_blob_map_) {
235 std::unique_ptr<BlobDataHandle> handle =
236 context->GetBlobDataFromUUID(uuid_state_pair.first);
237 if (handle) {
238 referenced_pending_blobs.push_back(
239 context->GetBlobDataFromUUID(uuid_state_pair.first));
240 }
241 }
242 // We clear the map before canceling them to prevent any strange reentry into
243 // our class (see OnPopulationAllowed) if any blobs were waiting for others
244 // to construct.
245 async_blob_map_.clear();
246 for (const std::unique_ptr<BlobDataHandle>& handle :
247 referenced_pending_blobs) {
248 context->BreakAndFinishPendingBlob(handle->uuid(),
249 BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT);
250 }
251 }
252
253 BlobStatus BlobTransportHost::StartRequests(
254 const std::string& uuid,
255 TransportState* state,
256 BlobStorageContext* context,
257 std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
258 switch (state->strategy) {
259 case IPCBlobItemRequestStrategy::IPC:
260 DCHECK(file_infos.empty());
261 SendIPCRequests(state, context);
262 return BlobStatus::PENDING_TRANSPORT;
263 case IPCBlobItemRequestStrategy::SHARED_MEMORY:
264 DCHECK(file_infos.empty());
265 return ContinueSharedMemoryRequests(uuid, state, context);
266 case IPCBlobItemRequestStrategy::FILE:
267 DCHECK(!file_infos.empty());
268 SendFileRequests(state, context, std::move(file_infos));
269 return BlobStatus::PENDING_TRANSPORT;
270 case IPCBlobItemRequestStrategy::UNKNOWN:
271 break;
272 }
273 NOTREACHED();
274 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
275 }
276
277 // Note: This can be called when we cancel a blob in the context.
278 void BlobTransportHost::OnPopulationAllowed(
michaeln 2016/11/07 21:47:05 OnReadyForTransport()
dmurph 2016/11/08 21:19:58 Done.
279 const std::string& uuid,
280 base::WeakPtr<BlobStorageContext> context,
281 BlobStatus status,
282 std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
283 if (!context) {
284 async_blob_map_.erase(uuid);
285 return;
286 }
287 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid);
288 if (state_it == async_blob_map_.end())
289 return;
290
291 TransportState* state = state_it->second.get();
292 if (BlobStatusIsPending(status)) {
293 DCHECK(status == BlobStatus::PENDING_TRANSPORT);
294 status = StartRequests(uuid, state, context.get(), std::move(file_infos));
295 if (BlobStatusIsPending(status))
296 return;
297 }
298 BlobStatusCallback completion_callback = state->completion_callback;
299 async_blob_map_.erase(state_it);
300 completion_callback.Run(status);
301 }
302
303 void BlobTransportHost::SendIPCRequests(TransportState* state,
304 BlobStorageContext* context) {
305 const std::vector<MemoryItemRequest>& requests =
306 state->request_builder.requests();
307 std::vector<BlobItemBytesRequest> byte_requests;
308
309 DCHECK(!requests.empty());
310 for (const MemoryItemRequest& request : requests) {
311 byte_requests.push_back(request.message);
312 }
313
314 state->request_memory_callback.Run(std::move(byte_requests),
315 std::vector<base::SharedMemoryHandle>(),
316 std::vector<base::File>());
317 }
318
319 BlobStatus BlobTransportHost::OnIPCResponses(
320 const std::string& uuid,
321 TransportState* state,
322 const std::vector<BlobItemBytesResponse>& responses,
323 BlobStorageContext* context) {
324 const auto& requests = state->request_builder.requests();
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, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
332 context);
333 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
334 }
335 bool success = state->data_builder.PopulateFutureData(
336 request.browser_item_index, response.inline_data.data(),
337 request.browser_item_offset, request.message.size);
338 if (!success) {
339 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
340 context);
341 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
342 }
343 state->num_fulfilled_requests++;
344 }
345 if (state->num_fulfilled_requests == num_requests) {
346 FinishBuildingBlob(state, context);
347 return BlobStatus::DONE;
348 }
349 return BlobStatus::PENDING_TRANSPORT;
350 }
351
352 BlobStatus BlobTransportHost::ContinueSharedMemoryRequests(
353 const std::string& uuid,
354 TransportState* state,
355 BlobStorageContext* context) {
356 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder;
357 const std::vector<MemoryItemRequest>& requests = request_builder.requests();
358 size_t num_requests = requests.size();
359 DCHECK_LT(state->num_fulfilled_requests, num_requests);
360 if (state->next_request == num_requests) {
361 // We are still waiting on other requests to come back.
362 return BlobStatus::PENDING_TRANSPORT;
363 }
364
365 std::vector<BlobItemBytesRequest> byte_requests;
366 std::vector<base::SharedMemoryHandle> shared_memory;
367
368 for (; state->next_request < num_requests; ++state->next_request) {
369 const MemoryItemRequest& request = requests[state->next_request];
370 bool using_shared_memory_handle = state->num_shared_memory_requests > 0;
371 if (using_shared_memory_handle &&
372 state->current_shared_memory_handle_index !=
373 request.message.handle_index) {
374 // We only want one shared memory per requesting blob.
375 break;
376 }
377 state->current_shared_memory_handle_index = request.message.handle_index;
378 state->num_shared_memory_requests++;
379
380 if (!state->shared_memory_block) {
381 state->shared_memory_block.reset(new base::SharedMemory());
382 size_t size =
383 request_builder.shared_memory_sizes()[request.message.handle_index];
384 if (!state->shared_memory_block->CreateAnonymous(size)) {
385 DVLOG(1) << "Unable to allocate shared memory for blob transfer.";
386 return BlobStatus::ERR_OUT_OF_MEMORY;
387 }
388 }
389 shared_memory.push_back(state->shared_memory_block->handle());
390 byte_requests.push_back(request.message);
391 // Since we are only using one handle at a time, transform our handle
392 // index correctly back to 0.
393 byte_requests.back().handle_index = 0;
394 }
395 DCHECK(!requests.empty());
396
397 state->request_memory_callback.Run(std::move(byte_requests),
398 std::move(shared_memory),
399 std::vector<base::File>());
400 return BlobStatus::PENDING_TRANSPORT;
401 }
402
403 BlobStatus BlobTransportHost::OnSharedMemoryResponses(
404 const std::string& uuid,
405 TransportState* state,
406 const std::vector<BlobItemBytesResponse>& responses,
407 BlobStorageContext* context) {
408 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder;
409 const auto& requests = request_builder.requests();
410 for (const BlobItemBytesResponse& response : responses) {
411 const MemoryItemRequest& request = requests[response.request_number];
412 if (state->num_shared_memory_requests == 0) {
413 DVLOG(1) << "Received too many responses for shared memory.";
414 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
415 context);
416 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
417 }
418 state->num_shared_memory_requests--;
419 if (!state->shared_memory_block->memory()) {
420 // We just map the whole block, as we'll probably be accessing the
421 // whole thing in this group of responses.
422 size_t handle_size =
423 request_builder
424 .shared_memory_sizes()[state->current_shared_memory_handle_index];
425 if (!state->shared_memory_block->Map(handle_size)) {
426 DVLOG(1) << "Unable to map memory to size " << handle_size;
427 CancelBuildingBlob(uuid, BlobStatus::ERR_OUT_OF_MEMORY, context);
428 return BlobStatus::ERR_OUT_OF_MEMORY;
429 }
430 }
431
432 bool success = state->data_builder.PopulateFutureData(
433 request.browser_item_index,
434 static_cast<const char*>(state->shared_memory_block->memory()) +
435 request.message.handle_offset,
436 request.browser_item_offset, request.message.size);
437
438 if (!success) {
439 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
440 context);
441 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
442 }
443 state->num_fulfilled_requests++;
444 }
445 if (state->num_fulfilled_requests == requests.size()) {
446 FinishBuildingBlob(state, context);
447 return BlobStatus::DONE;
448 }
449 return ContinueSharedMemoryRequests(uuid, state, context);
450 }
451
452 void BlobTransportHost::SendFileRequests(
453 TransportState* state,
454 BlobStorageContext* context,
455 std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
456 std::vector<base::File> files;
457
458 for (BlobMemoryController::FileCreationInfo& file_info : file_infos) {
459 state->files.push_back(std::move(file_info.file_reference));
460 files.push_back(std::move(file_info.file));
461 }
462
463 const std::vector<MemoryItemRequest>& requests =
464 state->request_builder.requests();
465 std::vector<BlobItemBytesRequest> byte_requests;
466
467 DCHECK(!requests.empty());
468 for (const MemoryItemRequest& request : requests) {
469 byte_requests.push_back(request.message);
470 }
471
472 state->request_memory_callback.Run(std::move(byte_requests),
473 std::vector<base::SharedMemoryHandle>(),
474 std::move(files));
475 }
476
477 BlobStatus BlobTransportHost::OnFileResponses(
478 const std::string& uuid,
479 TransportState* state,
480 const std::vector<BlobItemBytesResponse>& responses,
481 BlobStorageContext* context) {
482 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder;
483 const auto& requests = request_builder.requests();
484 for (const BlobItemBytesResponse& response : responses) {
485 const MemoryItemRequest& request = requests[response.request_number];
486 const scoped_refptr<ShareableFileReference>& file_ref =
487 state->files[request.message.handle_index];
488 bool success = state->data_builder.PopulateFutureFile(
489 request.browser_item_index, file_ref, response.time_file_modified);
490 if (!success) {
491 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
492 context);
493 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
494 }
495 state->num_fulfilled_requests++;
496 }
497 if (state->num_fulfilled_requests == requests.size()) {
498 FinishBuildingBlob(state, context);
499 return BlobStatus::DONE;
500 }
501 return BlobStatus::PENDING_TRANSPORT;
502 }
503
504 void BlobTransportHost::FinishBuildingBlob(TransportState* state,
michaeln 2016/11/07 21:47:05 CompleteTransport() might be more clear since the
dmurph 2016/11/08 21:19:58 Done.
505 BlobStorageContext* context) {
michaeln 2016/11/07 21:47:05 nit: indent is off
dmurph 2016/11/08 21:19:58 Done.
506 std::string uuid = state->data_builder.uuid();
507 async_blob_map_.erase(state->data_builder.uuid());
508 context->OnPopulationComplete(uuid);
michaeln 2016/11/07 21:47:05 it probably would be a small improvement to consis
dmurph 2016/11/08 21:19:58 Done.
509 }
510
511 } // namespace storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698