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

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

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

Powered by Google App Engine
This is Rietveld 408576698