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

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

Issue 2448353002: [BlobAsync] Moving async handling into BlobStorageContext & quota out. (Closed)
Patch Set: comments 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 // This means we don't need to transport anything, so just send the
118 // elements along and tell our callback we're done transporting.
119 for (const DataElement& e : elements) {
120 DCHECK_NE(e.type(), DataElement::TYPE_BYTES_DESCRIPTION);
121 state->data_builder.AppendIPCDataElement(e);
122 }
123 temp_handle = context->BuildBlob(
124 state->data_builder, BlobStorageContext::TransportAllowedCallback());
125 completion_callback.Run(BlobStatus::DONE);
126 return;
127 }
128 case MemoryStrategy::IPC:
129 state->strategy = IPCBlobItemRequestStrategy::IPC;
130 state->request_builder.InitializeForIPCRequests(
131 memory_controller.limits().max_ipc_memory_size, transport_memory_size,
132 elements, &(state->data_builder));
133 break;
134 case MemoryStrategy::SHARED_MEMORY:
135 state->strategy = IPCBlobItemRequestStrategy::SHARED_MEMORY;
136 state->request_builder.InitializeForSharedMemoryRequests(
137 memory_controller.limits().max_shared_memory_size,
138 transport_memory_size, elements, &(state->data_builder));
139 break;
140 case MemoryStrategy::FILE:
141 state->strategy = IPCBlobItemRequestStrategy::FILE;
142 state->request_builder.InitializeForFileRequests(
143 memory_controller.limits().max_file_size, transport_memory_size,
144 elements, &(state->data_builder));
145 break;
146 }
147 // We initialize our requests received state now that they are populated.
148 state->request_received.resize(state->request_builder.requests().size(),
149 false);
150 TransportState* state_ptr = state.get();
151 async_blob_map_[uuid] = std::move(state);
152 temp_handle = context->BuildBlob(
153 state_ptr->data_builder,
154 base::Bind(&BlobTransportHost::OnReadyForTransport,
155 ptr_factory_.GetWeakPtr(), uuid, context->AsWeakPtr()));
156 BlobStatus status = temp_handle->GetBlobStatus();
157 if (BlobStatusIsError(status)) {
158 // Don't retain the reference if we have a bad ipc error so the blob is
159 // correctly destructed.
160 if (BlobStatusIsBadIPC(status))
161 increment_runner.Release().Reset();
162 async_blob_map_.erase(uuid);
163 }
164
165 if (!BlobStatusIsPending(status))
166 completion_callback.Run(status);
167 }
168
169 BlobStatus BlobTransportHost::OnMemoryResponses(
170 const std::string& uuid,
171 const std::vector<BlobItemBytesResponse>& responses,
172 BlobStorageContext* context) {
173 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid);
174 if (state_it == async_blob_map_.end()) {
175 DVLOG(1) << "Could not find blob " << uuid;
176 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
177 }
178 if (responses.empty()) {
179 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
180 context);
181 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
182 }
183
184 // Validate response sanity: it should refer to a legal request number, and
185 // we shouldn't have received an answer for that request yet.
186 BlobTransportHost::TransportState* state = state_it->second.get();
187 const auto& requests = state->request_builder.requests();
188 for (const BlobItemBytesResponse& response : responses) {
189 if (response.request_number >= requests.size()) {
190 // Bad IPC, so we delete our record and ignore.
191 DVLOG(1) << "Invalid request number " << response.request_number;
192 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
193 context);
194 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
195 }
196 DCHECK_LT(response.request_number, state->request_received.size());
197 if (state->request_received[response.request_number]) {
198 // Bad IPC, so we delete our record.
199 DVLOG(1) << "Already received response for that request.";
200 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
201 context);
202 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
203 }
204 state->request_received[response.request_number] = true;
205 }
206 switch (state->strategy) {
207 case IPCBlobItemRequestStrategy::IPC:
208 return OnIPCResponses(uuid, state, responses, context);
209 case IPCBlobItemRequestStrategy::SHARED_MEMORY:
210 return OnSharedMemoryResponses(uuid, state, responses, context);
211 case IPCBlobItemRequestStrategy::FILE:
212 return OnFileResponses(uuid, state, responses, context);
213 case IPCBlobItemRequestStrategy::UNKNOWN:
214 break;
215 }
216 NOTREACHED();
217 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
218 }
219
220 void BlobTransportHost::CancelBuildingBlob(const std::string& uuid,
221 BlobStatus code,
222 BlobStorageContext* context) {
223 DCHECK(context);
224 DCHECK(BlobStatusIsError(code));
225 auto state_it = async_blob_map_.find(uuid);
226 if (state_it == async_blob_map_.end())
227 return;
228 // We can have the blob dereferenced by the renderer, but have it still being
229 // 'built'. In this case, it's destructed in the context, but we still have
230 // it in our map. Hence we make sure the context has the entry before
231 // calling cancel.
232 async_blob_map_.erase(state_it);
233 if (context->registry().HasEntry(uuid))
234 context->CancelBuildingBlob(uuid, code);
235 }
236
237 void BlobTransportHost::CancelAll(BlobStorageContext* context) {
238 DCHECK(context);
239 // If the blob still exists in the context, then we know that someone else is
240 // expecting our blob, and we need to cancel it to let the dependency know
241 // it's gone.
242 std::vector<std::unique_ptr<BlobDataHandle>> referenced_pending_blobs;
243 for (const auto& uuid_state_pair : async_blob_map_) {
244 std::unique_ptr<BlobDataHandle> handle =
245 context->GetBlobDataFromUUID(uuid_state_pair.first);
246 if (handle) {
247 referenced_pending_blobs.push_back(
248 context->GetBlobDataFromUUID(uuid_state_pair.first));
249 }
250 }
251 // We clear the map before canceling them to prevent any strange reentry into
252 // our class (see OnReadyForTransport) if any blobs were waiting for others
253 // to construct.
254 async_blob_map_.clear();
255 for (const std::unique_ptr<BlobDataHandle>& handle :
256 referenced_pending_blobs) {
257 context->CancelBuildingBlob(handle->uuid(),
258 BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT);
259 }
260 }
261
262 BlobStatus BlobTransportHost::StartRequests(
263 const std::string& uuid,
264 TransportState* state,
265 BlobStorageContext* context,
266 std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
267 switch (state->strategy) {
268 case IPCBlobItemRequestStrategy::IPC:
269 DCHECK(file_infos.empty());
270 SendIPCRequests(state, context);
271 return BlobStatus::PENDING_TRANSPORT;
272 case IPCBlobItemRequestStrategy::SHARED_MEMORY:
273 DCHECK(file_infos.empty());
274 return ContinueSharedMemoryRequests(uuid, state, context);
275 case IPCBlobItemRequestStrategy::FILE:
276 DCHECK(!file_infos.empty());
277 SendFileRequests(state, context, std::move(file_infos));
278 return BlobStatus::PENDING_TRANSPORT;
279 case IPCBlobItemRequestStrategy::UNKNOWN:
280 break;
281 }
282 NOTREACHED();
283 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
284 }
285
286 // Note: This can be called when we cancel a blob in the context.
287 void BlobTransportHost::OnReadyForTransport(
288 const std::string& uuid,
289 base::WeakPtr<BlobStorageContext> context,
290 BlobStatus status,
291 std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
292 if (!context) {
293 async_blob_map_.erase(uuid);
294 return;
295 }
296 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid);
297 if (state_it == async_blob_map_.end())
298 return;
299
300 TransportState* state = state_it->second.get();
301 if (BlobStatusIsPending(status)) {
302 DCHECK(status == BlobStatus::PENDING_TRANSPORT);
303 status = StartRequests(uuid, state, context.get(), std::move(file_infos));
304 if (BlobStatusIsPending(status))
305 return;
306 }
307 BlobStatusCallback completion_callback = state->completion_callback;
308 async_blob_map_.erase(state_it);
309 completion_callback.Run(status);
310 }
311
312 void BlobTransportHost::SendIPCRequests(TransportState* state,
313 BlobStorageContext* context) {
314 const std::vector<MemoryItemRequest>& requests =
315 state->request_builder.requests();
316 std::vector<BlobItemBytesRequest> byte_requests;
317
318 DCHECK(!requests.empty());
319 for (const MemoryItemRequest& request : requests) {
320 byte_requests.push_back(request.message);
321 }
322
323 state->request_memory_callback.Run(std::move(byte_requests),
324 std::vector<base::SharedMemoryHandle>(),
325 std::vector<base::File>());
326 }
327
328 BlobStatus BlobTransportHost::OnIPCResponses(
329 const std::string& uuid,
330 TransportState* state,
331 const std::vector<BlobItemBytesResponse>& responses,
332 BlobStorageContext* context) {
333 const auto& requests = state->request_builder.requests();
334 size_t num_requests = requests.size();
335 for (const BlobItemBytesResponse& response : responses) {
336 const MemoryItemRequest& request = requests[response.request_number];
337 if (response.inline_data.size() < request.message.size) {
338 DVLOG(1) << "Invalid data size " << response.inline_data.size()
339 << " vs requested size of " << request.message.size;
340 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
341 context);
342 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
343 }
344 bool success = state->data_builder.PopulateFutureData(
345 request.browser_item_index, response.inline_data.data(),
346 request.browser_item_offset, request.message.size);
347 if (!success) {
348 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
349 context);
350 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
351 }
352 state->num_fulfilled_requests++;
353 }
354 if (state->num_fulfilled_requests == num_requests) {
355 CompleteTransport(state, context);
356 return BlobStatus::DONE;
357 }
358 return BlobStatus::PENDING_TRANSPORT;
359 }
360
361 BlobStatus BlobTransportHost::ContinueSharedMemoryRequests(
362 const std::string& uuid,
363 TransportState* state,
364 BlobStorageContext* context) {
365 BlobTransportRequestBuilder& request_builder = state->request_builder;
366 const std::vector<MemoryItemRequest>& requests = request_builder.requests();
367 size_t num_requests = requests.size();
368 DCHECK_LT(state->num_fulfilled_requests, num_requests);
369 if (state->next_request == num_requests) {
370 // We are still waiting on other requests to come back.
371 return BlobStatus::PENDING_TRANSPORT;
372 }
373
374 std::vector<BlobItemBytesRequest> byte_requests;
375 std::vector<base::SharedMemoryHandle> shared_memory;
376
377 for (; state->next_request < num_requests; ++state->next_request) {
378 const MemoryItemRequest& request = requests[state->next_request];
379 bool using_shared_memory_handle = state->num_shared_memory_requests > 0;
380 if (using_shared_memory_handle &&
381 state->current_shared_memory_handle_index !=
382 request.message.handle_index) {
383 // We only want one shared memory per requesting blob.
384 break;
385 }
386 state->current_shared_memory_handle_index = request.message.handle_index;
387 state->num_shared_memory_requests++;
388
389 if (!state->shared_memory_block) {
390 state->shared_memory_block.reset(new base::SharedMemory());
391 size_t size =
392 request_builder.shared_memory_sizes()[request.message.handle_index];
393 if (!state->shared_memory_block->CreateAnonymous(size)) {
394 DVLOG(1) << "Unable to allocate shared memory for blob transfer.";
395 return BlobStatus::ERR_OUT_OF_MEMORY;
396 }
397 }
398 shared_memory.push_back(state->shared_memory_block->handle());
399 byte_requests.push_back(request.message);
400 // Since we are only using one handle at a time, transform our handle
401 // index correctly back to 0.
402 byte_requests.back().handle_index = 0;
403 }
404 DCHECK(!requests.empty());
405
406 state->request_memory_callback.Run(std::move(byte_requests),
407 std::move(shared_memory),
408 std::vector<base::File>());
409 return BlobStatus::PENDING_TRANSPORT;
410 }
411
412 BlobStatus BlobTransportHost::OnSharedMemoryResponses(
413 const std::string& uuid,
414 TransportState* state,
415 const std::vector<BlobItemBytesResponse>& responses,
416 BlobStorageContext* context) {
417 BlobTransportRequestBuilder& request_builder = state->request_builder;
418 const auto& requests = request_builder.requests();
419 for (const BlobItemBytesResponse& response : responses) {
420 const MemoryItemRequest& request = requests[response.request_number];
421 if (state->num_shared_memory_requests == 0) {
422 DVLOG(1) << "Received too many responses for shared memory.";
423 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
424 context);
425 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
426 }
427 state->num_shared_memory_requests--;
428 if (!state->shared_memory_block->memory()) {
429 // We just map the whole block, as we'll probably be accessing the
430 // whole thing in this group of responses.
431 size_t handle_size =
432 request_builder
433 .shared_memory_sizes()[state->current_shared_memory_handle_index];
434 if (!state->shared_memory_block->Map(handle_size)) {
435 DVLOG(1) << "Unable to map memory to size " << handle_size;
436 CancelBuildingBlob(uuid, BlobStatus::ERR_OUT_OF_MEMORY, context);
437 return BlobStatus::ERR_OUT_OF_MEMORY;
438 }
439 }
440
441 bool success = state->data_builder.PopulateFutureData(
442 request.browser_item_index,
443 static_cast<const char*>(state->shared_memory_block->memory()) +
444 request.message.handle_offset,
445 request.browser_item_offset, request.message.size);
446
447 if (!success) {
448 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
449 context);
450 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
451 }
452 state->num_fulfilled_requests++;
453 }
454 if (state->num_fulfilled_requests == requests.size()) {
455 CompleteTransport(state, context);
456 return BlobStatus::DONE;
457 }
458 return ContinueSharedMemoryRequests(uuid, state, context);
459 }
460
461 void BlobTransportHost::SendFileRequests(
462 TransportState* state,
463 BlobStorageContext* context,
464 std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
465 std::vector<base::File> files;
466
467 for (BlobMemoryController::FileCreationInfo& file_info : file_infos) {
468 state->files.push_back(std::move(file_info.file_reference));
469 files.push_back(std::move(file_info.file));
470 }
471
472 const std::vector<MemoryItemRequest>& requests =
473 state->request_builder.requests();
474 std::vector<BlobItemBytesRequest> byte_requests;
475
476 DCHECK(!requests.empty());
477 for (const MemoryItemRequest& request : requests) {
478 byte_requests.push_back(request.message);
479 }
480
481 state->request_memory_callback.Run(std::move(byte_requests),
482 std::vector<base::SharedMemoryHandle>(),
483 std::move(files));
484 }
485
486 BlobStatus BlobTransportHost::OnFileResponses(
487 const std::string& uuid,
488 TransportState* state,
489 const std::vector<BlobItemBytesResponse>& responses,
490 BlobStorageContext* context) {
491 BlobTransportRequestBuilder& request_builder = state->request_builder;
492 const auto& requests = request_builder.requests();
493 for (const BlobItemBytesResponse& response : responses) {
494 const MemoryItemRequest& request = requests[response.request_number];
495 const scoped_refptr<ShareableFileReference>& file_ref =
496 state->files[request.message.handle_index];
497 bool success = state->data_builder.PopulateFutureFile(
498 request.browser_item_index, file_ref, response.time_file_modified);
499 if (!success) {
500 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
501 context);
502 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
503 }
504 state->num_fulfilled_requests++;
505 }
506 if (state->num_fulfilled_requests == requests.size()) {
507 CompleteTransport(state, context);
508 return BlobStatus::DONE;
509 }
510 return BlobStatus::PENDING_TRANSPORT;
511 }
512
513 void BlobTransportHost::CompleteTransport(TransportState* state,
514 BlobStorageContext* context) {
515 std::string uuid = state->data_builder.uuid();
516 async_blob_map_.erase(state->data_builder.uuid());
517 context->NotifyTransportComplete(uuid);
518 }
519
520 } // namespace storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698