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

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

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

Powered by Google App Engine
This is Rietveld 408576698