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

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

Issue 2055053003: [BlobAsync] Disk support for blob storage (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: half of comments Created 4 years, 4 months 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
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "storage/browser/blob/blob_storage_context.h" 5 #include "storage/browser/blob/blob_storage_context.h"
6 6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <algorithm> 7 #include <algorithm>
11 #include <limits> 8 #include <limits>
12 #include <memory> 9 #include <memory>
10 #include <set>
13 #include <utility> 11 #include <utility>
14 12
15 #include "base/bind.h" 13 #include "base/bind.h"
16 #include "base/callback.h" 14 #include "base/callback.h"
17 #include "base/location.h" 15 #include "base/location.h"
18 #include "base/logging.h" 16 #include "base/logging.h"
19 #include "base/memory/ptr_util.h" 17 #include "base/memory/ptr_util.h"
20 #include "base/message_loop/message_loop.h" 18 #include "base/message_loop/message_loop.h"
21 #include "base/metrics/histogram.h" 19 #include "base/metrics/histogram.h"
20 #include "base/numerics/safe_conversions.h"
21 #include "base/numerics/safe_math.h"
22 #include "base/task_runner.h"
22 #include "base/threading/thread_task_runner_handle.h" 23 #include "base/threading/thread_task_runner_handle.h"
23 #include "base/trace_event/trace_event.h" 24 #include "base/trace_event/trace_event.h"
25 #include "storage/browser/blob/blob_async_transport_request_builder.h"
24 #include "storage/browser/blob/blob_data_builder.h" 26 #include "storage/browser/blob/blob_data_builder.h"
25 #include "storage/browser/blob/blob_data_handle.h"
26 #include "storage/browser/blob/blob_data_item.h" 27 #include "storage/browser/blob/blob_data_item.h"
27 #include "storage/browser/blob/blob_data_snapshot.h" 28 #include "storage/browser/blob/blob_data_snapshot.h"
28 #include "storage/browser/blob/shareable_blob_data_item.h" 29 #include "storage/browser/blob/shareable_blob_data_item.h"
30 #include "storage/common/data_element.h"
29 #include "url/gurl.h" 31 #include "url/gurl.h"
30 32
31 namespace storage { 33 namespace storage {
32 using BlobRegistryEntry = BlobStorageRegistry::Entry; 34 namespace {
33 using BlobState = BlobStorageRegistry::BlobState; 35 using ItemCopyEntry = InternalBlobData::ItemCopyEntry;
34 36
35 BlobStorageContext::BlobStorageContext() : memory_usage_(0) {} 37 bool IsBytes(DataElement::Type type) {
38 return type == DataElement::TYPE_BYTES ||
39 type == DataElement::TYPE_BYTES_DESCRIPTION;
40 }
41 } // namespace
42
43 BlobStorageContext::BlobFlattener::BlobFlattener(
44 const BlobDataBuilder& input_builder,
45 InternalBlobData* output_blob,
46 BlobStorageRegistry* registry) {
47 const std::string& uuid = input_builder.uuid_;
48 std::set<std::string> dependent_blob_uuids;
49 bool contains_pending_content = false;
50
51 size_t num_files_with_unknown_size = 0;
52
53 base::CheckedNumeric<uint64_t> checked_total_size = 0;
54 base::CheckedNumeric<uint64_t> checked_memory_quota_needed = 0;
55 base::CheckedNumeric<uint64_t> checked_file_quota_needed = 0;
56
57 for (scoped_refptr<BlobDataItem> input_item : input_builder.items_) {
58 const DataElement& input_element = input_item->data_element();
59 DataElement::Type type = input_element.type();
60 uint64_t length = input_element.length();
61
62 if (IsBytes(type)) {
63 DCHECK_NE(0 + DataElement::kUnknownSize, input_element.length());
64 contains_pending_content |= type == DataElement::TYPE_BYTES_DESCRIPTION;
65 checked_memory_quota_needed += length;
66 checked_total_size += length;
67 scoped_refptr<ShareableBlobDataItem> item = new ShareableBlobDataItem(
68 std::move(input_item), ShareableBlobDataItem::QUOTA_NEEDED);
69 pending_memory_items.push_back(item.get());
70 user_items.push_back(item.get());
71 output_blob->AppendSharedBlobItem(uuid, std::move(item));
72 continue;
73 }
74 if (type == DataElement::TYPE_BLOB) {
75 InternalBlobData* ref_entry =
76 registry->GetEntry(input_element.blob_uuid());
77
78 if (!ref_entry || input_element.blob_uuid() == uuid) {
79 status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
80 return;
81 }
82
83 if (BlobStatusIsError(ref_entry->status())) {
84 status = BlobStatus::ERR_REFERENCED_BLOB_BROKEN;
85 return;
86 }
87
88 if (ref_entry->total_size() == DataElement::kUnknownSize) {
89 // We can't reference a blob with unknown size.
90 status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
91 return;
92 }
93
94 if (dependent_blob_uuids.find(input_element.blob_uuid()) ==
95 dependent_blob_uuids.end()) {
96 dependent_blobs.push_back(
97 std::make_pair(input_element.blob_uuid(), ref_entry));
98 dependent_blob_uuids.insert(input_element.blob_uuid());
99 }
100
101 length = length == DataElement::kUnknownSize ? ref_entry->total_size()
102 : input_element.length();
103 checked_total_size += length;
104
105 // If we're referencing the whole blob, then we don't need to slice.
106 if (input_element.offset() == 0 && length == ref_entry->total_size()) {
107 for (const auto& shareable_item : ref_entry->items()) {
108 output_blob->AppendSharedBlobItem(uuid, shareable_item);
109 }
110 continue;
111 }
112
113 // Validate our reference has good offset & length.
114 if (input_element.offset() + length >
115 ref_entry->total_size()) {
116 status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
117 return;
118 }
119
120 BlobSlice slice(*ref_entry, input_element.offset(), length);
121
122 if (slice.first_source_item) {
123 copies.push_back(ItemCopyEntry(slice.first_source_item,
124 slice.first_item_slice_offset,
125 slice.dest_items.front()));
126 pending_memory_items.push_back(slice.dest_items.front().get());
127 }
128 if (slice.last_source_item) {
129 copies.push_back(
130 ItemCopyEntry(slice.last_source_item, 0, slice.dest_items.back()));
131 pending_memory_items.push_back(slice.dest_items.back().get());
132 }
133 checked_memory_quota_needed += slice.copying_memory_size;
134
135 for (auto& shareable_item : slice.dest_items) {
136 output_blob->AppendSharedBlobItem(uuid, std::move(shareable_item));
137 }
138 continue;
139 }
140
141 // If the source item is a temporary file item, then we need to keep track
142 // of that and mark is as needing quota.
143 scoped_refptr<ShareableBlobDataItem> item;
144 if (type == DataElement::TYPE_FILE &&
145 BlobDataBuilder::IsFutureFileItem(input_element)) {
146 item = new ShareableBlobDataItem(std::move(input_item),
147 ShareableBlobDataItem::QUOTA_NEEDED);
148 contains_pending_content = true;
149 pending_file_items.push_back(item.get());
150 user_items.push_back(item.get());
151 checked_file_quota_needed += length;
152 } else {
153 item = new ShareableBlobDataItem(
154 std::move(input_item),
155 ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA);
156 }
157 if (length == DataElement::kUnknownSize)
158 num_files_with_unknown_size++;
159
160 checked_total_size += length;
161 output_blob->AppendSharedBlobItem(uuid, std::move(item));
162 }
163
164 if (num_files_with_unknown_size > 1 && input_builder.items_.size() > 1) {
165 LOG(ERROR) << "We only allow an unknown size when it's a single file item";
166 status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
167 return;
168 }
169 if (!checked_total_size.IsValid() || !checked_memory_quota_needed.IsValid() ||
170 !checked_file_quota_needed.IsValid()) {
171 LOG(ERROR) << "Blob sizes aren't valid.";
172 status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
173 return;
174 }
175 total_size = checked_total_size.ValueOrDie();
176 memory_quota_needed = checked_memory_quota_needed.ValueOrDie();
177 file_quota_needed = checked_file_quota_needed.ValueOrDie();
178 status = contains_pending_content ? BlobStatus::PENDING : BlobStatus::DONE;
179 }
180
181 BlobStorageContext::BlobFlattener::~BlobFlattener() {}
182
183 BlobStorageContext::BlobSlice::BlobSlice(const InternalBlobData& source,
184 uint64_t slice_offset,
185 uint64_t slice_size) {
186 const auto& source_items = source.items();
187 const auto& offsets = source.offsets();
188 LOG(ERROR) << "doing a slice at " << slice_offset << " with size "
189 << slice_size;
190 DCHECK_LE(slice_offset + slice_size, source.total_size());
191 size_t item_index =
192 std::upper_bound(offsets.begin(), offsets.end(), slice_offset) -
193 offsets.begin();
194 uint64_t item_offset =
195 item_index == 0 ? slice_offset : slice_offset - offsets[item_index - 1];
196 size_t num_items = source_items.size();
197
198 size_t first_item_index = item_index;
199 copying_memory_size = 0;
200
201 // Read starting from 'first_item_index' and 'item_offset'.
202 for (uint64_t total_sliced = 0;
203 item_index < num_items && total_sliced < slice_size; item_index++) {
204 const scoped_refptr<BlobDataItem>& source_item =
205 source_items[item_index]->item();
206 uint64_t source_length = source_item->length();
207 DCHECK_NE(source_length, std::numeric_limits<uint64_t>::max());
208 DCHECK_NE(source_length, 0ull);
209
210 uint64_t read_size =
211 std::min(source_length - item_offset, slice_size - total_sliced);
212 total_sliced += read_size;
213
214 if (read_size == source_length) {
215 // We can share the entire item.
216 LOG(ERROR) << "we can share";
217 dest_items.push_back(source_items[item_index]);
218 continue;
219 }
220
221 scoped_refptr<BlobDataItem> data_item;
222 ShareableBlobDataItem::State state =
223 ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA;
224 switch (source_item->type()) {
225 case DataElement::TYPE_BYTES_DESCRIPTION:
226 case DataElement::TYPE_BYTES: {
227 if (item_index == first_item_index) {
228 first_item_slice_offset = item_offset;
229 first_source_item = source_items[item_index];
230 } else {
231 last_source_item = source_items[item_index];
232 }
233 copying_memory_size += read_size;
234 // Since we don't have quota yet for memory, we create temporary items
235 // for this data. When our blob is finished constructing, all dependent
236 // blobs are done, and we have enough memory quota, we'll copy the data
237 // over.
238 std::unique_ptr<DataElement> element(new DataElement());
239 element->SetToBytesDescription(base::checked_cast<size_t>(read_size));
240 data_item = new BlobDataItem(std::move(element));
241 state = ShareableBlobDataItem::QUOTA_NEEDED;
242 break;
243 }
244 case DataElement::TYPE_FILE: {
245 std::unique_ptr<DataElement> element(new DataElement());
246 element->SetToFilePathRange(
247 source_item->path(), source_item->offset() + item_offset, read_size,
248 source_item->expected_modification_time());
249 data_item =
250 new BlobDataItem(std::move(element), source_item->data_handle_);
251
252 if (BlobDataBuilder::IsFutureFileItem(source_item->data_element())) {
253 // Since we don't have file path / reference for our future file, we
254 // create another file item with this temporary file name. When our
255 // blob is finished constructing, all dependent blobs are done, and we
256 // can copy the handle over.
257 LOG(ERROR) << "we're slicing a temp file item!";
258 if (item_index == first_item_index) {
259 first_item_slice_offset = item_offset;
260 first_source_item = source_items[item_index];
261 } else {
262 last_source_item = source_items[item_index];
263 }
264 state = ShareableBlobDataItem::QUOTA_NEEDED;
265 }
266 break;
267 }
268 case DataElement::TYPE_FILE_FILESYSTEM: {
269 std::unique_ptr<DataElement> element(new DataElement());
270 element->SetToFileSystemUrlRange(
271 source_item->filesystem_url(), source_item->offset() + item_offset,
272 read_size, source_item->expected_modification_time());
273 data_item = new BlobDataItem(std::move(element));
274 break;
275 }
276 case DataElement::TYPE_DISK_CACHE_ENTRY: {
277 std::unique_ptr<DataElement> element(new DataElement());
278 element->SetToDiskCacheEntryRange(source_item->offset() + item_offset,
279 read_size);
280 data_item =
281 new BlobDataItem(std::move(element), source_item->data_handle_,
282 source_item->disk_cache_entry(),
283 source_item->disk_cache_stream_index(),
284 source_item->disk_cache_side_stream_index());
285 break;
286 }
287 case DataElement::TYPE_BLOB:
288 case DataElement::TYPE_UNKNOWN:
289 CHECK(false) << "Illegal blob item type: " << source_item->type();
290 }
291 dest_items.push_back(
292 new ShareableBlobDataItem(std::move(data_item), state));
293 item_offset = 0;
294 }
295 }
296
297 BlobStorageContext::BlobSlice::~BlobSlice() {}
298
299 BlobStorageContext::BlobStorageContext() : ptr_factory_(this) {}
36 300
37 BlobStorageContext::~BlobStorageContext() { 301 BlobStorageContext::~BlobStorageContext() {
38 } 302 }
39 303
40 std::unique_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID( 304 std::unique_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID(
41 const std::string& uuid) { 305 const std::string& uuid) {
42 BlobRegistryEntry* entry = registry_.GetEntry(uuid); 306 InternalBlobData* entry = registry_.GetEntry(uuid);
43 if (!entry) { 307 if (!entry)
44 return nullptr; 308 return nullptr;
45 } 309 return CreateHandle(uuid, entry);
46 return base::WrapUnique(
47 new BlobDataHandle(uuid, entry->content_type, entry->content_disposition,
48 this, base::ThreadTaskRunnerHandle::Get().get()));
49 } 310 }
50 311
51 std::unique_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL( 312 std::unique_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL(
52 const GURL& url) { 313 const GURL& url) {
53 std::string uuid; 314 std::string uuid;
54 BlobRegistryEntry* entry = registry_.GetEntryFromURL(url, &uuid); 315 InternalBlobData* entry = registry_.GetEntryFromURL(url, &uuid);
55 if (!entry) { 316 if (!entry)
56 return nullptr; 317 return nullptr;
57 } 318 return CreateHandle(uuid, entry);
58 return base::WrapUnique(
59 new BlobDataHandle(uuid, entry->content_type, entry->content_disposition,
60 this, base::ThreadTaskRunnerHandle::Get().get()));
61 } 319 }
62 320
63 std::unique_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( 321 std::unique_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob(
64 const BlobDataBuilder& external_builder) { 322 const BlobDataBuilder& external_builder) {
65 TRACE_EVENT0("Blob", "Context::AddFinishedBlob"); 323 TRACE_EVENT0("Blob", "Context::AddFinishedBlob");
66 CreatePendingBlob(external_builder.uuid(), external_builder.content_type_, 324 return BuildBlob(external_builder, PopulatationAllowedCallback());
67 external_builder.content_disposition_);
68 CompletePendingBlob(external_builder);
69 std::unique_ptr<BlobDataHandle> handle =
70 GetBlobDataFromUUID(external_builder.uuid_);
71 DecrementBlobRefCount(external_builder.uuid_);
72 return handle;
73 } 325 }
74 326
75 std::unique_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( 327 std::unique_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob(
76 const BlobDataBuilder* builder) { 328 const BlobDataBuilder* builder) {
77 DCHECK(builder); 329 DCHECK(builder);
78 return AddFinishedBlob(*builder); 330 return AddFinishedBlob(*builder);
79 } 331 }
80 332
333 std::unique_ptr<BlobDataHandle> BlobStorageContext::AddBrokenBlob(
334 const std::string& uuid,
335 const std::string& content_type,
336 const std::string& content_disposition,
337 BlobStatus reason) {
338 DCHECK(!registry_.HasEntry(uuid));
339 DCHECK(BlobStatusIsError(reason));
340 InternalBlobData* entry =
341 registry_.CreateEntry(uuid, content_type, content_disposition);
342 entry->status_ = reason;
343 FinishBuilding(entry);
344 return CreateHandle(uuid, entry);
345 }
346
81 bool BlobStorageContext::RegisterPublicBlobURL(const GURL& blob_url, 347 bool BlobStorageContext::RegisterPublicBlobURL(const GURL& blob_url,
82 const std::string& uuid) { 348 const std::string& uuid) {
83 if (!registry_.CreateUrlMapping(blob_url, uuid)) { 349 if (!registry_.CreateUrlMapping(blob_url, uuid))
84 return false; 350 return false;
85 }
86 IncrementBlobRefCount(uuid); 351 IncrementBlobRefCount(uuid);
87 return true; 352 return true;
88 } 353 }
89 354
90 void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) { 355 void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) {
91 std::string uuid; 356 std::string uuid;
92 if (!registry_.DeleteURLMapping(blob_url, &uuid)) { 357 if (!registry_.DeleteURLMapping(blob_url, &uuid))
93 return; 358 return;
94 }
95 DecrementBlobRefCount(uuid); 359 DecrementBlobRefCount(uuid);
96 } 360 }
97 361
98 void BlobStorageContext::CreatePendingBlob( 362 void BlobStorageContext::EnableDisk(
99 const std::string& uuid, 363 const base::FilePath& storage_directory,
100 const std::string& content_type, 364 scoped_refptr<base::TaskRunner> file_runner) {
101 const std::string& content_disposition) { 365 memory_controller_.EnableDisk(storage_directory, std::move(file_runner));
102 DCHECK(!registry_.GetEntry(uuid) && !uuid.empty()); 366 }
103 registry_.CreateEntry(uuid, content_type, content_disposition); 367
104 } 368 std::unique_ptr<BlobDataHandle> BlobStorageContext::BuildBlob(
105 369 const BlobDataBuilder& content,
106 void BlobStorageContext::CompletePendingBlob( 370 const PopulatationAllowedCallback& can_populate_memory) {
107 const BlobDataBuilder& external_builder) { 371 DCHECK(!registry_.HasEntry(content.uuid_));
108 BlobRegistryEntry* entry = registry_.GetEntry(external_builder.uuid()); 372
373 InternalBlobData* entry = registry_.CreateEntry(
374 content.uuid_, content.content_type_, content.content_disposition_);
375
376 // This flattens all blob references in the transportion content out and
377 // stores the complete item representation in the internal data.
378 BlobFlattener flattener(content, entry, &registry_);
379
380 // If we contain invalid references we're invalid input.
381 if (flattener.status == BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS) {
382 LOG(ERROR) << "invalid construction args";
383 BreakAndFinishPendingBlob(content.uuid_,
384 BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
385 return CreateHandle(content.uuid_, entry);
386 }
387
388 // If we contain broken references (or we reference ourself) we want to fail.
389 if (flattener.status == BlobStatus::ERR_REFERENCED_BLOB_BROKEN) {
390 LOG(ERROR) << "references don't exist";
391 BreakAndFinishPendingBlob(content.uuid_,
392 BlobStatus::ERR_REFERENCED_BLOB_BROKEN);
393 return CreateHandle(content.uuid_, entry);
394 }
395
396 LOG(ERROR) << "We need " << flattener.memory_quota_needed
397 << " memory quota, and " << flattener.file_quota_needed
398 << " file quota.";
399
400 if (!memory_controller_.CanReserveQuota(flattener.memory_quota_needed +
401 flattener.file_quota_needed)) {
402 LOG(ERROR) << "we can't fit.";
403 BreakAndFinishPendingBlob(content.uuid_, BlobStatus::ERR_OUT_OF_MEMORY);
404 return CreateHandle(content.uuid_, entry);
405 }
406
407 entry->status_ = BlobStatus::PENDING;
408 entry->size_ = flattener.total_size;
409 std::unique_ptr<BlobDataHandle> handle = CreateHandle(content.uuid_, entry);
410
411 size_t num_building_dependent_blobs = 0;
412 std::vector<std::unique_ptr<BlobDataHandle>> dependent_blobs;
413 // We hold a handle to all blobs we're using. This is important, as our memory
414 // accounting can be delayed until OnEnoughSizeForBlobData is called, and we
415 // only free memory on canceling when we've done this accounting. If a
416 // dependent blob is dereferenced, then we're the last blob holding onto that
417 // data item, and we need to account for that. So we prevent that case by
418 // holding onto all blobs.
419 for (const std::pair<std::string, InternalBlobData*>& pending_blob :
420 flattener.dependent_blobs) {
421 dependent_blobs.push_back(
422 CreateHandle(pending_blob.first, pending_blob.second));
423 if (pending_blob.second->status() == BlobStatus::PENDING) {
424 pending_blob.second->building_state_->build_completion_callbacks
425 .push_back(base::Bind(&BlobStorageContext::OnDependentBlobFinished,
426 ptr_factory_.GetWeakPtr(), content.uuid_));
427 num_building_dependent_blobs++;
428 }
429 }
430
431 DCHECK(flattener.status == BlobStatus::PENDING || !can_populate_memory)
432 << "There is no pending content for the user to populate, so the "
433 "callback should be null.";
434 DCHECK(flattener.status == BlobStatus::DONE || can_populate_memory)
435 << "If we have pending content then there needs to be a callback "
436 "present.";
437
438 entry->building_state_.reset(new InternalBlobData::BuildingState(
439 /* user_data_present */ !flattener.user_items.empty(),
440 /* user_data_population_callback */ can_populate_memory,
441 /* dependent_building_blobs_present */
442 num_building_dependent_blobs > 0,
443 num_building_dependent_blobs,
444 /* memory_quota_needed */ flattener.memory_quota_needed != 0,
445 /* file_quota_needed */ flattener.file_quota_needed > 0));
446
447 InternalBlobData::BuildingState* building_state =
448 entry->building_state_.get();
449 std::swap(building_state->copies, flattener.copies);
450 std::swap(building_state->dependent_blobs, dependent_blobs);
451 std::swap(building_state->user_items, flattener.user_items);
452
453 if (flattener.memory_quota_needed) {
454 // We can finish our blob before returning from this method.
455 BlobMemoryController::PendingMemoryQuotaRequest pending_request =
456 memory_controller_.ReserveMemoryQuota(
457 std::move(flattener.pending_memory_items),
458 base::Bind(&BlobStorageContext::OnEnoughSizeForMemory,
459 ptr_factory_.GetWeakPtr(), content.uuid_));
460 if (entry->building_state_)
461 entry->building_state_->memory_quota_request = pending_request;
462 }
463
464 if (flattener.file_quota_needed) {
465 // We can finish our blob before returning from this method.
466 BlobMemoryController::PendingFileQuotaRequest pending_request =
467 memory_controller_.ReserveFileQuota(
468 std::move(flattener.pending_file_items),
469 base::Bind(&BlobStorageContext::OnEnoughSizeForFiles,
470 ptr_factory_.GetWeakPtr(), content.uuid_));
471 if (entry->building_state_)
472 entry->building_state_->file_quota_request = pending_request;
473 }
474
475 if (entry->CanFinishBuilding())
476 FinishBuilding(entry);
477
478 return handle;
479 }
480
481 void BlobStorageContext::BreakAndFinishPendingBlob(const std::string& uuid,
482 BlobStatus reason) {
483 InternalBlobData* entry = registry_.GetEntry(uuid);
109 DCHECK(entry); 484 DCHECK(entry);
110 DCHECK(!entry->data.get()) << "Blob already constructed: " 485 DCHECK(BlobStatusIsError(reason));
111 << external_builder.uuid(); 486 PopulatationAllowedCallback user_data_population_callback;
112 // We want to handle storing our broken blob as well. 487 if (entry->building_state_ &&
113 switch (entry->state) { 488 entry->building_state_->user_data_population_callback) {
114 case BlobState::PENDING: { 489 user_data_population_callback =
115 entry->data_builder.reset(new InternalBlobData::Builder()); 490 entry->building_state_->user_data_population_callback;
116 InternalBlobData::Builder* internal_data_builder = 491 entry->building_state_->user_data_population_callback.Reset();
117 entry->data_builder.get(); 492 }
118 493 ClearAndFreeMemory(uuid, entry);
119 bool broken = false; 494 entry->status_ = reason;
120 for (const auto& blob_item : external_builder.items_) { 495 if (user_data_population_callback) {
121 IPCBlobCreationCancelCode error_code; 496 user_data_population_callback.Run(
122 if (!AppendAllocatedBlobItem(external_builder.uuid_, blob_item, 497 reason, std::vector<BlobMemoryController::FileCreationInfo>());
123 internal_data_builder, &error_code)) { 498 }
124 broken = true; 499 entry->items_.clear();
125 memory_usage_ -= entry->data_builder->GetNonsharedMemoryUsage(); 500 entry->offsets_.clear();
126 entry->state = BlobState::BROKEN; 501 entry->size_ = 0;
127 entry->broken_reason = error_code; 502 FinishBuilding(entry);
128 entry->data_builder.reset(new InternalBlobData::Builder()); 503 }
129 break; 504
130 } 505 void BlobStorageContext::FinishedPopulatingPendingBlob(
131 } 506 const std::string& uuid) {
132 entry->data = entry->data_builder->Build(); 507 InternalBlobData* entry = registry_.GetEntry(uuid);
133 entry->data_builder.reset(); 508 DCHECK_EQ(entry->status(), BlobStatus::PENDING);
134 entry->state = broken ? BlobState::BROKEN : BlobState::COMPLETE; 509 SetItemStateToPopulated(&entry->building_state_->user_items);
135 break; 510 entry->building_state_->user_data_populated = true;
136 } 511 if (entry->CanFinishBuilding())
137 case BlobState::BROKEN: { 512 FinishBuilding(entry);
138 InternalBlobData::Builder builder;
139 entry->data = builder.Build();
140 break;
141 }
142 case BlobState::COMPLETE:
143 DCHECK(false) << "Blob already constructed: " << external_builder.uuid();
144 return;
145 }
146
147 UMA_HISTOGRAM_COUNTS("Storage.Blob.ItemCount", entry->data->items().size());
148 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.Broken",
149 entry->state == BlobState::BROKEN);
150 if (entry->state == BlobState::BROKEN) {
151 UMA_HISTOGRAM_ENUMERATION(
152 "Storage.Blob.BrokenReason", static_cast<int>(entry->broken_reason),
153 (static_cast<int>(IPCBlobCreationCancelCode::LAST) + 1));
154 }
155 size_t total_memory = 0, nonshared_memory = 0;
156 entry->data->GetMemoryUsage(&total_memory, &nonshared_memory);
157 UMA_HISTOGRAM_COUNTS("Storage.Blob.TotalSize", total_memory / 1024);
158 UMA_HISTOGRAM_COUNTS("Storage.Blob.TotalUnsharedSize",
159 nonshared_memory / 1024);
160 TRACE_COUNTER1("Blob", "MemoryStoreUsageBytes", memory_usage_);
161
162 auto runner = base::ThreadTaskRunnerHandle::Get();
163 for (const auto& callback : entry->build_completion_callbacks) {
164 runner->PostTask(FROM_HERE,
165 base::Bind(callback, entry->state == BlobState::COMPLETE,
166 entry->broken_reason));
167 }
168 entry->build_completion_callbacks.clear();
169 }
170
171 void BlobStorageContext::CancelPendingBlob(const std::string& uuid,
172 IPCBlobCreationCancelCode reason) {
173 BlobRegistryEntry* entry = registry_.GetEntry(uuid);
174 DCHECK(entry && entry->state == BlobState::PENDING);
175 entry->state = BlobState::BROKEN;
176 entry->broken_reason = reason;
177 CompletePendingBlob(BlobDataBuilder(uuid));
178 } 513 }
179 514
180 void BlobStorageContext::IncrementBlobRefCount(const std::string& uuid) { 515 void BlobStorageContext::IncrementBlobRefCount(const std::string& uuid) {
181 BlobRegistryEntry* entry = registry_.GetEntry(uuid); 516 InternalBlobData* entry = registry_.GetEntry(uuid);
182 DCHECK(entry); 517 DCHECK(entry);
183 ++(entry->refcount); 518 ++(entry->refcount_);
184 } 519 }
185 520
186 void BlobStorageContext::DecrementBlobRefCount(const std::string& uuid) { 521 void BlobStorageContext::DecrementBlobRefCount(const std::string& uuid) {
187 BlobRegistryEntry* entry = registry_.GetEntry(uuid); 522 InternalBlobData* entry = registry_.GetEntry(uuid);
188 DCHECK(entry); 523 DCHECK(entry);
189 DCHECK_GT(entry->refcount, 0u); 524 DCHECK_GT(entry->refcount_, 0u);
190 if (--(entry->refcount) == 0) { 525 if (--(entry->refcount_) == 0) {
191 size_t memory_freeing = 0; 526 ClearAndFreeMemory(uuid, entry);
192 if (entry->state == BlobState::COMPLETE) {
193 memory_freeing = entry->data->GetUnsharedMemoryUsage();
194 entry->data->RemoveBlobFromShareableItems(uuid);
195 }
196 DCHECK_LE(memory_freeing, memory_usage_);
197 memory_usage_ -= memory_freeing;
198 registry_.DeleteEntry(uuid); 527 registry_.DeleteEntry(uuid);
199 } 528 }
200 } 529 }
201 530
202 std::unique_ptr<BlobDataSnapshot> BlobStorageContext::CreateSnapshot( 531 std::unique_ptr<BlobDataSnapshot> BlobStorageContext::CreateSnapshot(
203 const std::string& uuid) { 532 const std::string& uuid) {
204 std::unique_ptr<BlobDataSnapshot> result; 533 std::unique_ptr<BlobDataSnapshot> result;
205 BlobRegistryEntry* entry = registry_.GetEntry(uuid); 534 InternalBlobData* entry = registry_.GetEntry(uuid);
206 if (entry->state != BlobState::COMPLETE) { 535 if (entry->status() != BlobStatus::DONE)
207 return result; 536 return result;
208 } 537
209
210 const InternalBlobData& data = *entry->data;
211 std::unique_ptr<BlobDataSnapshot> snapshot(new BlobDataSnapshot( 538 std::unique_ptr<BlobDataSnapshot> snapshot(new BlobDataSnapshot(
212 uuid, entry->content_type, entry->content_disposition)); 539 uuid, entry->content_type(), entry->content_disposition()));
213 snapshot->items_.reserve(data.items().size()); 540 snapshot->items_.reserve(entry->items().size());
214 for (const auto& shareable_item : data.items()) { 541 for (const auto& shareable_item : entry->items()) {
215 snapshot->items_.push_back(shareable_item->item()); 542 snapshot->items_.push_back(shareable_item->item());
543 if (shareable_item->item()->type() == DataElement::TYPE_BYTES)
544 memory_controller_.UpdateBlobItemInRecents(shareable_item.get());
216 } 545 }
217 return snapshot; 546 return snapshot;
218 } 547 }
219 548
220 bool BlobStorageContext::IsBroken(const std::string& uuid) const { 549 BlobStatus BlobStorageContext::GetBlobStatus(const std::string& uuid) const {
221 const BlobRegistryEntry* entry = registry_.GetEntry(uuid); 550 const InternalBlobData* entry = registry_.GetEntry(uuid);
222 if (!entry) { 551 if (!entry)
223 return true; 552 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
224 } 553 return entry->status();
225 return entry->state == BlobState::BROKEN;
226 }
227
228 bool BlobStorageContext::IsBeingBuilt(const std::string& uuid) const {
229 const BlobRegistryEntry* entry = registry_.GetEntry(uuid);
230 if (!entry) {
231 return false;
232 }
233 return entry->state == BlobState::PENDING;
234 } 554 }
235 555
236 void BlobStorageContext::RunOnConstructionComplete( 556 void BlobStorageContext::RunOnConstructionComplete(
237 const std::string& uuid, 557 const std::string& uuid,
238 const BlobConstructedCallback& done) { 558 const BlobStatusCallback& done) {
239 BlobRegistryEntry* entry = registry_.GetEntry(uuid); 559 InternalBlobData* entry = registry_.GetEntry(uuid);
240 DCHECK(entry); 560 DCHECK(entry);
241 switch (entry->state) { 561 if (entry->status() == BlobStatus::PENDING) {
242 case BlobState::COMPLETE: 562 entry->building_state_->build_completion_callbacks.push_back(done);
243 done.Run(true, IPCBlobCreationCancelCode::UNKNOWN); 563 return;
244 return; 564 }
245 case BlobState::BROKEN: 565 done.Run(entry->status());
246 done.Run(false, entry->broken_reason); 566 }
247 return; 567
248 case BlobState::PENDING: 568 std::unique_ptr<BlobDataHandle> BlobStorageContext::CreateHandle(
249 entry->build_completion_callbacks.push_back(done); 569 const std::string& uuid,
250 return; 570 InternalBlobData* entry) {
251 } 571 return base::WrapUnique(new BlobDataHandle(
252 NOTREACHED(); 572 uuid, entry->content_type_, entry->content_disposition_, entry->size_,
253 } 573 this, base::ThreadTaskRunnerHandle::Get().get()));
254 574 }
255 bool BlobStorageContext::AppendAllocatedBlobItem( 575
256 const std::string& target_blob_uuid, 576 void BlobStorageContext::FinishBuilding(InternalBlobData* entry) {
257 scoped_refptr<BlobDataItem> blob_item, 577 DCHECK(entry);
258 InternalBlobData::Builder* target_blob_builder, 578
259 IPCBlobCreationCancelCode* error_code) { 579 if (entry->status_ == BlobStatus::PENDING) {
260 DCHECK(error_code); 580 for (const ItemCopyEntry& copy : entry->building_state_->copies) {
261 *error_code = IPCBlobCreationCancelCode::UNKNOWN; 581 // Our source item can be a file if it was a slice of an unpopulated file,
262 bool error = false; 582 // or a slice of data that was then paged to disk.
263 583 size_t dest_size = static_cast<size_t>(copy.dest_item->item()->length());
264 // The blob data is stored in the canonical way which only contains a 584 DataElement::Type dest_type = copy.dest_item->item()->type();
265 // list of Data, File, and FileSystem items. Aggregated TYPE_BLOB items 585 switch (copy.source_item->item()->type()) {
266 // are expanded into the primitive constituent types and reused if possible. 586 case DataElement::TYPE_BYTES: {
267 // 1) The Data item is denoted by the raw data and length. 587 DCHECK_EQ(dest_type, DataElement::TYPE_BYTES_DESCRIPTION);
268 // 2) The File item is denoted by the file path, the range and the expected 588 const char* src_data =
269 // modification time. 589 copy.source_item->item()->bytes() + copy.source_item_offset;
270 // 3) The FileSystem File item is denoted by the FileSystem URL, the range 590 copy.dest_item->item()->item_->SetToBytes(src_data, dest_size);
271 // and the expected modification time. 591 } break;
272 // 4) The Blob item is denoted by the source blob and an offset and size. 592 case DataElement::TYPE_FILE: {
273 // Internal items that are fully used by the new blob (not cut by the 593 LOG(ERROR) << "Source item has been paged or we started as a file, "
274 // offset or size) are shared between the blobs. Otherwise, the relevant 594 "so we're grabbing file ref "
275 // portion of the item is copied. 595 << dest_type;
276 596
277 DCHECK(blob_item->data_element_ptr()); 597 // If we expected a memory item (and our source was paged to disk) we
278 const DataElement& data_element = blob_item->data_element(); 598 // free that memory.
279 uint64_t length = data_element.length(); 599 if (dest_type == DataElement::TYPE_BYTES_DESCRIPTION)
280 uint64_t offset = data_element.offset(); 600 memory_controller_.FreeQuotaForPagedItemReference(copy.dest_item);
281 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeBeforeAppend", 601
282 memory_usage_ / 1024); 602 // We've been paged to disk, so free the memory of our temporary item,
283 switch (data_element.type()) { 603 // and create a new shared item with appropriate offset and length.
284 case DataElement::TYPE_BYTES: 604 const DataElement& source_element =
285 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Bytes", length / 1024); 605 copy.source_item->item()->data_element();
286 DCHECK(!offset); 606 std::unique_ptr<DataElement> new_element(new DataElement());
287 if (memory_usage_ + length > kBlobStorageMaxMemoryUsage) { 607 new_element->SetToFilePathRange(
288 error = true; 608 source_element.path(),
289 *error_code = IPCBlobCreationCancelCode::OUT_OF_MEMORY; 609 source_element.offset() + copy.source_item_offset, dest_size,
290 break; 610 source_element.expected_modification_time());
611 scoped_refptr<BlobDataItem> new_item(new BlobDataItem(
612 std::move(new_element), copy.source_item->item()->data_handle()));
613 copy.dest_item->item_.swap(new_item);
614 } break;
615 case DataElement::TYPE_UNKNOWN:
616 case DataElement::TYPE_BLOB:
617 case DataElement::TYPE_BYTES_DESCRIPTION:
618 case DataElement::TYPE_FILE_FILESYSTEM:
619 case DataElement::TYPE_DISK_CACHE_ENTRY:
620 NOTREACHED();
621 break;
291 } 622 }
292 memory_usage_ += length; 623 copy.dest_item->state_ = ShareableBlobDataItem::POPULATED_WITH_QUOTA;
293 target_blob_builder->AppendSharedBlobItem( 624 }
294 new ShareableBlobDataItem(target_blob_uuid, blob_item)); 625 entry->status_ = BlobStatus::DONE;
295 break; 626 }
296 case DataElement::TYPE_FILE: { 627
297 bool full_file = (length == std::numeric_limits<uint64_t>::max()); 628 LOG(ERROR) << "Finished blob.";
298 UMA_HISTOGRAM_BOOLEAN("Storage.BlobItemSize.File.Unknown", full_file); 629
299 if (!full_file) { 630 std::vector<BlobStatusCallback> callbacks;
300 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.File", 631 if (entry->building_state_.get()) {
301 (length - offset) / 1024); 632 std::swap(callbacks, entry->building_state_->build_completion_callbacks);
302 } 633 entry->building_state_.reset();
303 target_blob_builder->AppendSharedBlobItem( 634 }
304 new ShareableBlobDataItem(target_blob_uuid, blob_item)); 635
305 break; 636 auto runner = base::ThreadTaskRunnerHandle::Get();
306 } 637 for (const auto& callback : callbacks)
307 case DataElement::TYPE_FILE_FILESYSTEM: { 638 runner->PostTask(FROM_HERE, base::Bind(callback, entry->status()));
308 bool full_file = (length == std::numeric_limits<uint64_t>::max()); 639
309 UMA_HISTOGRAM_BOOLEAN("Storage.BlobItemSize.FileSystem.Unknown", 640 for (const auto& shareable_item : entry->items()) {
310 full_file); 641 DCHECK_NE(DataElement::TYPE_BYTES_DESCRIPTION,
311 if (!full_file) { 642 shareable_item->item()->type());
312 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.FileSystem", 643 if (!shareable_item->IsPopulated()) {
313 (length - offset) / 1024); 644 PrintTo(*shareable_item, &LOG_STREAM(ERROR));
314 } 645 DCHECK(shareable_item->IsPopulated()) << shareable_item->state();
315 target_blob_builder->AppendSharedBlobItem( 646 }
316 new ShareableBlobDataItem(target_blob_uuid, blob_item)); 647
317 break; 648 if (shareable_item->item()->type() == DataElement::TYPE_BYTES)
318 } 649 memory_controller_.UpdateBlobItemInRecents(shareable_item.get());
319 case DataElement::TYPE_BLOB: { 650 }
320 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Blob", 651 }
321 (length - offset) / 1024); 652
322 // We grab the handle to ensure it stays around while we copy it. 653 void BlobStorageContext::RequestUserPopulation(
323 std::unique_ptr<BlobDataHandle> src = 654 InternalBlobData::BuildingState* building_state,
324 GetBlobDataFromUUID(data_element.blob_uuid()); 655 std::vector<BlobMemoryController::FileCreationInfo> files) {
325 if (!src || src->IsBroken() || src->IsBeingBuilt()) { 656 if (building_state->user_data_population_callback) {
326 error = true; 657 PopulatationAllowedCallback user_data_population_callback =
327 *error_code = IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN; 658 building_state->user_data_population_callback;
328 break; 659 building_state->user_data_population_callback.Reset();
329 } 660 user_data_population_callback.Run(BlobStatus::PENDING, std::move(files));
330 BlobRegistryEntry* other_entry = 661 return;
331 registry_.GetEntry(data_element.blob_uuid()); 662 }
332 DCHECK(other_entry->data); 663 DCHECK(files.empty());
333 if (!AppendBlob(target_blob_uuid, *other_entry->data, offset, length, 664 SetItemStateToPopulated(&building_state->user_items);
334 target_blob_builder)) { 665 building_state->user_data_populated = true;
335 error = true; 666 }
336 *error_code = IPCBlobCreationCancelCode::OUT_OF_MEMORY; 667
337 } 668 void BlobStorageContext::OnEnoughSizeForMemory(const std::string& uuid,
338 break; 669 bool success) {
339 } 670 if (!success) {
340 case DataElement::TYPE_DISK_CACHE_ENTRY: { 671 BreakAndFinishPendingBlob(uuid, BlobStatus::ERR_OUT_OF_MEMORY);
341 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.CacheEntry", 672 return;
342 (length - offset) / 1024); 673 }
343 target_blob_builder->AppendSharedBlobItem( 674 InternalBlobData* entry = registry_.GetEntry(uuid);
344 new ShareableBlobDataItem(target_blob_uuid, blob_item)); 675 if (!entry || !entry->building_state_.get())
345 break; 676 return;
346 } 677 entry->building_state_->memory_quota_granted = true;
347 case DataElement::TYPE_BYTES_DESCRIPTION: 678 entry->building_state_->memory_quota_request =
348 case DataElement::TYPE_UNKNOWN: 679 memory_controller_.GetInvalidMemoryQuotaRequest();
349 NOTREACHED(); 680
350 break; 681 if (entry->building_state_->CanRequestUserPopulation()) {
351 } 682 RequestUserPopulation(
352 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeAfterAppend", 683 entry->building_state_.get(),
353 memory_usage_ / 1024); 684 std::vector<BlobMemoryController::FileCreationInfo>());
354 return !error; 685 }
355 } 686
356 687 if (entry->CanFinishBuilding())
357 bool BlobStorageContext::AppendBlob( 688 FinishBuilding(entry);
358 const std::string& target_blob_uuid, 689 }
359 const InternalBlobData& blob, 690
360 uint64_t offset, 691 void BlobStorageContext::OnEnoughSizeForFiles(
361 uint64_t length, 692 const std::string& uuid,
362 InternalBlobData::Builder* target_blob_builder) { 693 bool success,
363 DCHECK_GT(length, 0ull); 694 std::vector<BlobMemoryController::FileCreationInfo> files) {
364 695 if (!success) {
365 const std::vector<scoped_refptr<ShareableBlobDataItem>>& items = blob.items(); 696 BreakAndFinishPendingBlob(uuid, BlobStatus::ERR_OUT_OF_MEMORY);
366 auto iter = items.begin(); 697 return;
367 if (offset) { 698 }
368 for (; iter != items.end(); ++iter) { 699 InternalBlobData* entry = registry_.GetEntry(uuid);
369 const BlobDataItem& item = *(iter->get()->item()); 700 if (!entry || !entry->building_state_.get())
370 if (offset >= item.length()) 701 return;
371 offset -= item.length(); 702 entry->building_state_->file_quota_granted = true;
372 else 703 entry->building_state_->file_quota_request =
373 break; 704 BlobMemoryController::kInvalidFileQuotaRequest;
374 } 705
375 } 706 if (entry->building_state_->CanRequestUserPopulation()) {
376 707 RequestUserPopulation(entry->building_state_.get(), std::move(files));
377 for (; iter != items.end() && length > 0; ++iter) { 708 }
378 scoped_refptr<ShareableBlobDataItem> shareable_item = iter->get(); 709
379 const BlobDataItem& item = *(shareable_item->item()); 710 if (entry->CanFinishBuilding())
380 uint64_t item_length = item.length(); 711 FinishBuilding(entry);
381 DCHECK_GT(item_length, offset); 712 }
382 uint64_t current_length = item_length - offset; 713
383 uint64_t new_length = current_length > length ? length : current_length; 714 void BlobStorageContext::OnDependentBlobFinished(
384 715 const std::string& owning_blob_uuid,
385 bool reusing_blob_item = offset == 0 && new_length == item.length(); 716 BlobStatus status) {
386 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.ReusedItem", reusing_blob_item); 717 InternalBlobData* entry = registry_.GetEntry(owning_blob_uuid);
387 if (reusing_blob_item) { 718 if (!entry || !entry->building_state_)
388 shareable_item->referencing_blobs().insert(target_blob_uuid); 719 return;
389 target_blob_builder->AppendSharedBlobItem(shareable_item); 720
390 length -= new_length; 721 if (BlobStatusIsError(status)) {
391 continue; 722 DCHECK_NE(BlobStatus::ERR_BLOB_DEREFERENCED_WHILE_BUILDING, status)
392 } 723 << "Referenced blob should never be dereferenced while we "
393 724 << "are depending on it, as our system holds a handle.";
394 // We need to do copying of the items when we have a different offset or 725 BreakAndFinishPendingBlob(owning_blob_uuid,
395 // length 726 BlobStatus::ERR_REFERENCED_BLOB_BROKEN);
396 switch (item.type()) { 727 return;
397 case DataElement::TYPE_BYTES: { 728 }
398 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.BlobSlice.Bytes", 729 DCHECK_GT(entry->building_state_->num_building_dependent_blobs, 0u);
399 new_length / 1024); 730 --entry->building_state_->num_building_dependent_blobs;
400 if (memory_usage_ + new_length > kBlobStorageMaxMemoryUsage) { 731
401 return false; 732 if (entry->CanFinishBuilding())
402 } 733 FinishBuilding(entry);
403 DCHECK(!item.offset()); 734 }
404 std::unique_ptr<DataElement> element(new DataElement()); 735
405 element->SetToBytes(item.bytes() + offset, 736 void BlobStorageContext::ClearAndFreeMemory(const std::string& uuid,
406 static_cast<int64_t>(new_length)); 737 InternalBlobData* entry) {
407 memory_usage_ += new_length; 738 if (entry->building_state_) {
408 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( 739 InternalBlobData::BuildingState* building_state =
409 target_blob_uuid, new BlobDataItem(std::move(element)))); 740 entry->building_state_.get();
410 } break; 741 if (building_state->memory_quota_needed &&
411 case DataElement::TYPE_FILE: { 742 !building_state->memory_quota_granted) {
412 DCHECK_NE(item.length(), std::numeric_limits<uint64_t>::max()) 743 memory_controller_.CancelMemoryQuotaReservation(
413 << "We cannot use a section of a file with an unknown length"; 744 building_state->memory_quota_request);
414 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.BlobSlice.File", 745 building_state->memory_quota_request =
415 new_length / 1024); 746 memory_controller_.GetInvalidMemoryQuotaRequest();
416 std::unique_ptr<DataElement> element(new DataElement()); 747 }
417 element->SetToFilePathRange(item.path(), item.offset() + offset, 748 if (building_state->file_quota_needed &&
418 new_length, 749 !building_state->file_quota_granted) {
419 item.expected_modification_time()); 750 memory_controller_.CancelFileQuotaReservation(
420 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( 751 building_state->file_quota_request);
421 target_blob_uuid, 752 building_state->file_quota_request =
422 new BlobDataItem(std::move(element), item.data_handle_))); 753 BlobMemoryController::kInvalidFileQuotaRequest;
423 } break; 754 }
424 case DataElement::TYPE_FILE_FILESYSTEM: { 755 }
425 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.BlobSlice.FileSystem", 756 entry->RemoveBlobFromShareableItems(uuid);
426 new_length / 1024); 757 memory_controller_.MaybeFreeQuotaForItems(entry->items());
427 std::unique_ptr<DataElement> element(new DataElement()); 758 for (const auto& item_refptr : entry->items()) {
428 element->SetToFileSystemUrlRange(item.filesystem_url(), 759 if (item_refptr->referencing_blobs().size() == 0)
429 item.offset() + offset, new_length, 760 memory_controller_.RemoveBlobItemInRecents(*item_refptr);
430 item.expected_modification_time()); 761 }
431 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( 762
432 target_blob_uuid, new BlobDataItem(std::move(element)))); 763 entry->items_.clear();
433 } break; 764 entry->offsets_.clear();
434 case DataElement::TYPE_DISK_CACHE_ENTRY: { 765 }
435 std::unique_ptr<DataElement> element(new DataElement()); 766
436 element->SetToDiskCacheEntryRange(item.offset() + offset, 767 void BlobStorageContext::SetItemStateToPopulated(
437 new_length); 768 std::vector<ShareableBlobDataItem*>* items) {
438 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( 769 for (ShareableBlobDataItem* shareable_item : *items) {
439 target_blob_uuid, 770 DCHECK(shareable_item->state() == ShareableBlobDataItem::QUOTA_GRANTED);
440 new BlobDataItem(std::move(element), item.data_handle_, 771 shareable_item->state_ = ShareableBlobDataItem::POPULATED_WITH_QUOTA;
441 item.disk_cache_entry(), 772 }
442 item.disk_cache_stream_index(),
443 item.disk_cache_side_stream_index())));
444 } break;
445 case DataElement::TYPE_BYTES_DESCRIPTION:
446 case DataElement::TYPE_BLOB:
447 case DataElement::TYPE_UNKNOWN:
448 CHECK(false) << "Illegal blob item type: " << item.type();
449 }
450 length -= new_length;
451 offset = 0;
452 }
453 return true;
454 } 773 }
455 774
456 } // namespace storage 775 } // namespace storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698