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

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

Powered by Google App Engine
This is Rietveld 408576698