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

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