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

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

Issue 2339933004: [BlobStorage] BlobMemoryController & tests (Closed)
Patch Set: Created 4 years, 3 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
(Empty)
1 // Copyright 2016 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 "storage/browser/blob/blob_memory_controller.h"
6
7 #include <algorithm>
8
9 #include "base/callback.h"
10 #include "base/callback_helpers.h"
11 #include "base/containers/small_map.h"
12 #include "base/files/file_util.h"
13 #include "base/location.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "base/numerics/safe_math.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/task_runner.h"
23 #include "base/task_runner_util.h"
24 #include "base/time/time.h"
25 #include "base/trace_event/trace_event.h"
26 #include "base/tuple.h"
27 #include "storage/browser/blob/blob_data_builder.h"
28 #include "storage/browser/blob/blob_data_item.h"
29 #include "storage/browser/blob/shareable_blob_data_item.h"
30 #include "storage/browser/blob/shareable_file_reference.h"
31 #include "storage/common/data_element.h"
32
33 using base::File;
34 using base::FilePath;
35 using FileCreationInfo = storage::BlobMemoryController::FileCreationInfo;
36
37 namespace storage {
38 namespace {
39 using PendingMemoryQuotaRequest =
40 BlobMemoryController::PendingMemoryQuotaRequest;
41 using PendingFileQuotaRequest = BlobMemoryController::PendingFileQuotaRequest;
42
43 // Creates a file in the given directory w/ the given filename and size.
44 std::vector<BlobMemoryController::FileCreationInfo> CreateFiles(
45 std::vector<scoped_refptr<ShareableFileReference>> file_references) {
46 LOG(ERROR) << "creating files for renderer";
47 std::vector<BlobMemoryController::FileCreationInfo> result;
48
49 for (scoped_refptr<ShareableFileReference>& file_ref : file_references) {
50 result.push_back(BlobMemoryController::FileCreationInfo());
51 BlobMemoryController::FileCreationInfo& creation_info = result.back();
52 // Try to open our file.
53 File file(file_ref->path(), File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);
54 creation_info.file_reference = std::move(file_ref);
55 creation_info.error = file.error_details();
56 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.TransportFileCreate",
57 -creation_info.error, -File::FILE_ERROR_MAX);
58 if (creation_info.error != File::FILE_OK)
59 return std::vector<BlobMemoryController::FileCreationInfo>();
60
61 // Grab the file info to get the "last modified" time and store the file.
62 File::Info file_info;
63 bool success = file.GetInfo(&file_info);
64 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.TransportFileInfoSuccess", success);
65 creation_info.error = success ? File::FILE_OK : File::FILE_ERROR_FAILED;
66 if (!success)
67 return std::vector<BlobMemoryController::FileCreationInfo>();
68 creation_info.file = std::move(file);
69 }
70 LOG(ERROR) << "Done! Returning " << result.size() << " files.";
71 return result;
72 }
73
74 BlobMemoryController::FileCreationInfo WriteItemsToFile(
75 std::vector<scoped_refptr<ShareableBlobDataItem>>* items,
76 size_t total_size_bytes,
77 scoped_refptr<ShareableFileReference> file_reference) {
78 DCHECK_NE(0u, total_size_bytes);
79 LOG(ERROR) << "writing to file!";
pwnall 2016/09/16 22:45:24 The ERROR level doesn't seem right. Also, is this
dmurph 2016/09/20 00:00:50 Yeah I'll get rid of all these now.
80 UMA_HISTOGRAM_MEMORY_KB("Storage.Blob.PageFileSize", total_size_bytes / 1024);
81
82 // Create our file.
83 BlobMemoryController::FileCreationInfo creation_info;
84 File file(file_reference->path(),
85 File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);
86 creation_info.file_reference = std::move(file_reference);
87 creation_info.error = file.error_details();
88 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.PageFileCreate", -creation_info.error,
89 -File::FILE_ERROR_MAX);
90 if (creation_info.error != File::FILE_OK)
91 return creation_info;
92
93 // Write data.
94 file.SetLength(total_size_bytes);
95 int bytes_written = 0;
96 for (const auto& refptr : *items) {
97 const DataElement& element = refptr->item()->data_element();
98 DCHECK_EQ(DataElement::TYPE_BYTES, element.type());
99 size_t length = base::checked_cast<size_t>(element.length());
100 size_t bytes_left = length;
101 while (bytes_left > 0) {
102 bytes_written =
103 file.WriteAtCurrentPos(element.bytes() + (length - bytes_left),
104 base::saturated_cast<int>(bytes_left));
105 if (bytes_written < 0)
106 break;
107 DCHECK_LE(static_cast<size_t>(bytes_written), bytes_left);
108 bytes_left -= bytes_written;
109 }
110 if (bytes_written < 0)
111 break;
112 }
113 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.PageFileWriteSuccess", bytes_written > 0);
114
115 // Grab our modification time and create our SharedFileReference to manage the
116 // lifetime of the file.
117 File::Info info;
118 bool success = file.GetInfo(&info);
119 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.PageFileInfoSuccess", success);
120 creation_info.error =
121 bytes_written < 0 || !success ? File::FILE_ERROR_FAILED : File::FILE_OK;
122 creation_info.last_modified = info.last_modified;
123 return creation_info;
124 }
125
126 // Here so we can destruct files on the correct thread if the user cancels.
127 void DestructFiles(std::vector<BlobMemoryController::FileCreationInfo> files) {}
128
129 std::vector<scoped_refptr<ShareableBlobDataItem>> WrapInRefPtrs(
130 const std::vector<ShareableBlobDataItem*> items_ptrs) {
131 std::vector<scoped_refptr<ShareableBlobDataItem>> result;
132 for (ShareableBlobDataItem* item : items_ptrs) {
133 result.push_back(make_scoped_refptr(item));
134 }
135 return result;
136 }
137
138 } // namespace
139
140 BlobMemoryController::FileCreationInfo::FileCreationInfo() {}
pwnall 2016/09/16 22:45:24 Can you do `= default` instead? Also, I think you
dmurph 2016/09/20 00:00:50 Done.
141
142 BlobMemoryController::FileCreationInfo::~FileCreationInfo() {}
pwnall 2016/09/16 22:45:24 same as above
dmurph 2016/09/20 00:00:50 Done.
143
144 FileCreationInfo::FileCreationInfo(FileCreationInfo&&) = default;
145 FileCreationInfo& FileCreationInfo::operator=(FileCreationInfo&&) = default;
146
147 PendingMemoryQuotaRequest BlobMemoryController::GetInvalidMemoryQuotaRequest() {
148 return blobs_waiting_for_paging_.end();
149 }
150
151 BlobMemoryController::BlobMemoryController()
152 : recent_item_cache_(
153 base::MRUCache<uint64_t, ShareableBlobDataItem*>::NO_AUTO_EVICT),
154 ptr_factory_(this) {}
155
156 BlobMemoryController::~BlobMemoryController() {}
157
158 void BlobMemoryController::EnableDisk(
159 const base::FilePath& storage_directory,
160 scoped_refptr<base::TaskRunner> file_runner) {
161 LOG(ERROR) << "enabling disk";
pwnall 2016/09/16 22:45:24 Is ERROR correct here?
dmurph 2016/09/20 00:00:50 Done.
162 DCHECK(!storage_directory.empty());
163 file_runner_ = std::move(file_runner);
164 blob_storage_dir_ = storage_directory;
165 disk_enabled_ = true;
166 }
167
168 void BlobMemoryController::DisableDisk() {
169 disk_enabled_ = false;
170 blob_memory_used_ += in_flight_memory_used_;
171 in_flight_memory_used_ = 0;
172 pending_file_request_sizes_.clear();
173 items_saving_to_disk_.clear();
174 for (const auto& size_callback_pair : blobs_waiting_for_paging_)
175 size_callback_pair.second.Run(false);
176 pending_pagings_ = 0;
177 blobs_waiting_for_paging_.clear();
178 blobs_waiting_for_paging_size_ = 0;
179 recent_item_cache_.Clear();
180 recent_item_cache_bytes_ = 0;
181 RecordTracingCounters();
182 }
183
184 BlobMemoryController::Strategy BlobMemoryController::DetermineStrategy(
185 size_t shortcut_bytes,
186 uint64_t total_transportation_bytes) const {
187 // Step 1: Handle case where we have no memory to transport.
188 if (total_transportation_bytes == 0) {
189 return Strategy::NONE_NEEDED;
190 }
191 // Step 2: Check if we have enough memory to store the blob.
192 if (!CanReserveQuota(total_transportation_bytes)) {
193 return Strategy::TOO_LARGE;
194 }
195 // From here on, we know we can fit the blob in memory or on disk.
196 // Step 3: Decide if we're using the shortcut method.
pwnall 2016/09/16 22:45:24 The step comments seem obvious (meaning that the c
dmurph 2016/09/20 00:00:50 Done.
197 if (shortcut_bytes == total_transportation_bytes &&
198 blobs_waiting_for_paging_.empty() &&
199 shortcut_bytes < GetAvailableMemoryForBlobs()) {
200 return Strategy::NONE_NEEDED;
201 }
202 // Step 4: Decide if we're going straight to disk.
203 if (disk_enabled_ &&
204 (total_transportation_bytes > limits_.max_blob_in_memory_space)) {
205 return Strategy::FILE;
206 }
207 // Step 5: Decide if we're using shared memory.
208 if (total_transportation_bytes > limits_.max_ipc_memory_size) {
209 return Strategy::SHARED_MEMORY;
210 }
211 // Step 6: We can fit in IPC.
212 return Strategy::IPC;
213 }
214
215 bool BlobMemoryController::CanReserveQuota(uint64_t size) const {
216 // We check each size independently as a blob can't be constructed in both
217 // disk and memory.
218 return size <= GetAvailableMemoryForBlobs() ||
219 size <= GetAvailableDiskSpaceForBlobs();
220 }
221
222 PendingMemoryQuotaRequest BlobMemoryController::ReserveMemoryQuota(
223 std::vector<ShareableBlobDataItem*> unreserved_memory_items,
224 const MemoryQuotaRequestCallback& success_callback) {
225 uint64_t total_bytes_needed = 0;
226 for (ShareableBlobDataItem* item : unreserved_memory_items) {
227 DCHECK_EQ(ShareableBlobDataItem::QUOTA_NEEDED, item->state());
228 DCHECK(item->item()->type() == DataElement::TYPE_BYTES_DESCRIPTION ||
229 item->item()->type() == DataElement::TYPE_BYTES);
230 total_bytes_needed += item->item()->length();
231 item->state_ = ShareableBlobDataItem::QUOTA_REQUESTED;
232 }
233
234 if (total_bytes_needed == 0) {
235 LOG(ERROR) << "Don't need bytes";
236 success_callback.Run(true);
237 return blobs_waiting_for_paging_.end();
238 }
239
240 if (!disk_enabled_) {
241 LOG(ERROR) << total_bytes_needed << " can fit immediately.";
242 blob_memory_used_ += total_bytes_needed;
243 for (ShareableBlobDataItem* item : unreserved_memory_items) {
244 item->state_ = ShareableBlobDataItem::QUOTA_GRANTED;
245 }
246 success_callback.Run(true);
247 return blobs_waiting_for_paging_.end();
248 }
249
250 // If we're currently waiting for blobs to page already, then we add
251 // ourselves to the end of the queue. Once paging is complete, we'll schedule
252 // more paging for any more pending blobs.
253 if (!blobs_waiting_for_paging_.empty()) {
254 LOG(ERROR) << "putting memory request " << total_bytes_needed
255 << " in queue.";
256 std::vector<scoped_refptr<ShareableBlobDataItem>> item_handles =
257 WrapInRefPtrs(unreserved_memory_items);
258 blobs_waiting_for_paging_.push_back(std::make_pair(
259 total_bytes_needed,
260 base::Bind(&BlobMemoryController::SetStateQuotaGrantedAndCallback,
261 ptr_factory_.GetWeakPtr(), base::Passed(&item_handles),
262 success_callback)));
263 blobs_waiting_for_paging_size_ += total_bytes_needed;
264 return --blobs_waiting_for_paging_.end();
265 }
266
267 // Store right away if we can.
268 if (total_bytes_needed <= GetAvailableMemoryForBlobs()) {
269 // If we're past our blob memory limit, then schedule our paging.
270 LOG(ERROR) << "Yes, " << total_bytes_needed << " can fit in memory now.";
271 blob_memory_used_ += total_bytes_needed;
272 for (ShareableBlobDataItem* item : unreserved_memory_items) {
273 item->state_ = ShareableBlobDataItem::QUOTA_GRANTED;
274 }
275 MaybeSchedulePagingUntilSystemHealthy();
276 success_callback.Run(true);
277 return blobs_waiting_for_paging_.end();
278 }
279
280 // This means we're too big for memory.
281 LOG(ERROR) << "waiting until " << total_bytes_needed << " can fit.";
282 DCHECK(blobs_waiting_for_paging_.empty());
283 DCHECK_EQ(0u, blobs_waiting_for_paging_size_);
284 std::vector<scoped_refptr<ShareableBlobDataItem>> item_handles =
285 WrapInRefPtrs(unreserved_memory_items);
286 blobs_waiting_for_paging_.push_back(std::make_pair(
287 total_bytes_needed,
288 base::Bind(&BlobMemoryController::SetStateQuotaGrantedAndCallback,
289 ptr_factory_.GetWeakPtr(), base::Passed(&item_handles),
290 success_callback)));
291 blobs_waiting_for_paging_size_ = total_bytes_needed;
292 auto it = --blobs_waiting_for_paging_.end();
293 LOG(ERROR) << "Scheduling paging on first item too big";
294 MaybeSchedulePagingUntilSystemHealthy();
295 return it;
296 }
297
298 void BlobMemoryController::CancelMemoryQuotaReservation(
299 const PendingMemoryQuotaRequest& request) {
300 if (request == blobs_waiting_for_paging_.end())
301 return;
302 blobs_waiting_for_paging_size_ -= request->first;
303 blobs_waiting_for_paging_.erase(request);
304 return;
305 }
306
307 PendingFileQuotaRequest BlobMemoryController::ReserveFileQuota(
308 std::vector<ShareableBlobDataItem*> unreserved_file_items,
309 const FileQuotaRequestCallback& success_callback) {
310 uint64_t total_files_size_needed = 0;
311 base::SmallMap<std::map<uint64_t, uint64_t>> file_sizes;
312 std::vector<scoped_refptr<ShareableBlobDataItem>> item_handles;
313 for (ShareableBlobDataItem* item : unreserved_file_items) {
314 DCHECK_EQ(ShareableBlobDataItem::QUOTA_NEEDED, item->state());
315 DCHECK_EQ(DataElement::TYPE_FILE, item->item()->type());
316
317 const DataElement& element = item->item()->data_element();
318 DCHECK(BlobDataBuilder::IsFutureFileItem(element));
319
320 uint64_t file_id = BlobDataBuilder::GetFutureFileID(element);
321 LOG(ERROR) << "file id" << file_id;
322 auto it = file_sizes.find(file_id);
323 if (it != file_sizes.end()) {
324 it->second = std::max(it->second, element.offset() + element.length());
325 } else {
326 file_sizes[file_id] = element.offset() + element.length();
327 }
328 total_files_size_needed += element.length();
329 item->state_ = ShareableBlobDataItem::QUOTA_REQUESTED;
330 item_handles.push_back(make_scoped_refptr(item));
331 }
332
333 DCHECK_LE(total_files_size_needed, GetAvailableDiskSpaceForBlobs());
334 disk_used_ += total_files_size_needed;
335 std::vector<scoped_refptr<ShareableFileReference>> file_refs;
336 std::vector<uint64_t> sizes;
337 for (const auto& size_pair : file_sizes) {
338 std::string file_name = base::Uint64ToString(current_file_num_++);
339 file_refs.push_back(ShareableFileReference::GetOrCreate(
340 blob_storage_dir_.Append(file_name),
341 ShareableFileReference::DELETE_ON_FINAL_RELEASE, file_runner_.get()));
342 sizes.push_back(size_pair.second);
343 LOG(ERROR) << "File " << file_name << " planning on creating with size "
344 << size_pair.second;
345 }
346
347 uint64_t disk_quota_entry = ++curr_disk_save_entry_;
348 if (disk_quota_entry == kInvalidFileQuotaRequest) {
349 disk_quota_entry = ++curr_disk_save_entry_;
350 }
351 pending_file_request_sizes_[disk_quota_entry] = total_files_size_needed;
352 base::PostTaskAndReplyWithResult(
353 file_runner_.get(), FROM_HERE,
354 base::Bind(&CreateFiles, base::Passed(&file_refs)),
355 base::Bind(&BlobMemoryController::OnCreateFiles,
356 ptr_factory_.GetWeakPtr(), base::Passed(&sizes),
357 base::Passed(&item_handles), disk_quota_entry,
358 success_callback));
359 RecordTracingCounters();
360 return disk_quota_entry;
361 }
362
363 void BlobMemoryController::CancelFileQuotaReservation(
364 const PendingFileQuotaRequest& entry) {
365 auto it = pending_file_request_sizes_.find(entry);
366 if (it == pending_file_request_sizes_.end())
367 return;
368 disk_used_ -= it->second;
369 pending_file_request_sizes_.erase(it);
370 }
371
372 void BlobMemoryController::MaybeFreeQuotaForItems(
373 const std::vector<scoped_refptr<ShareableBlobDataItem>>& items) {
374 uint64_t total_memory_freeing = 0;
375 std::unordered_set<uint64_t> visited_items;
376 for (const scoped_refptr<ShareableBlobDataItem>& item : items) {
377 switch (item->state()) {
378 case ShareableBlobDataItem::QUOTA_NEEDED:
379 case ShareableBlobDataItem::QUOTA_REQUESTED:
380 case ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA:
381 continue;
382 case ShareableBlobDataItem::QUOTA_GRANTED:
383 case ShareableBlobDataItem::POPULATED_WITH_QUOTA:
384 break;
385 }
386 // We only care about bytes items that don't have blobs referencing them,
387 // and we remove duplicates.
388 DataElement::Type type = item->item()->type();
389 if (!item->referencing_blobs().empty() ||
390 (type != DataElement::TYPE_BYTES &&
391 type != DataElement::TYPE_BYTES_DESCRIPTION) ||
392 base::ContainsKey(visited_items, item->item_id()))
393 continue;
394 visited_items.insert(item->item_id());
395 total_memory_freeing += item->item()->length();
396 }
397 if (total_memory_freeing != 0) {
398 DCHECK_GE(blob_memory_used_, total_memory_freeing);
399 LOG(ERROR) << "Freeing memory " << total_memory_freeing;
400 blob_memory_used_ -= total_memory_freeing;
401 MaybeGrantPendingQuotaRequests();
402 }
403 }
404
405 void BlobMemoryController::FreeQuotaForPagedItemReference(
406 const scoped_refptr<ShareableBlobDataItem>& item) {
407 CHECK_EQ(item->state(), ShareableBlobDataItem::QUOTA_GRANTED);
408 CHECK_EQ(item->item()->type(), DataElement::TYPE_BYTES_DESCRIPTION);
409 LOG(ERROR) << "Freeing memory " << item->item()->length();
410 blob_memory_used_ -= item->item()->length();
411 MaybeGrantPendingQuotaRequests();
412 }
413
414 void BlobMemoryController::UpdateBlobItemInRecents(
415 ShareableBlobDataItem* item) {
416 DCHECK_EQ(DataElement::TYPE_BYTES, item->item()->type());
417 DCHECK_EQ(ShareableBlobDataItem::POPULATED_WITH_QUOTA, item->state());
418 // We don't want to re-add the item if we're currently paging it to disk.
419 if (items_saving_to_disk_.find(item->item_id()) !=
420 items_saving_to_disk_.end())
421 return;
422 auto iterator = recent_item_cache_.Get(item->item_id());
423 if (iterator == recent_item_cache_.end()) {
424 recent_item_cache_bytes_ += static_cast<size_t>(item->item()->length());
425 recent_item_cache_.Put(item->item_id(), item);
426 MaybeSchedulePagingUntilSystemHealthy();
427 }
428 }
429
430 void BlobMemoryController::RemoveBlobItemInRecents(
431 const ShareableBlobDataItem& item) {
432 auto iterator = recent_item_cache_.Get(item.item_id());
433 if (iterator != recent_item_cache_.end()) {
434 size_t size = static_cast<size_t>(item.item()->length());
435 DCHECK_GE(recent_item_cache_bytes_, size);
436 recent_item_cache_bytes_ -= size;
437 recent_item_cache_.Erase(iterator);
438 }
439 }
440
441 void BlobMemoryController::SetStateQuotaGrantedAndCallback(
442 std::vector<scoped_refptr<ShareableBlobDataItem>> items,
443 const MemoryQuotaRequestCallback& final_callback,
444 bool success) {
445 if (!success) {
446 final_callback.Run(false);
447 return;
448 }
449 for (const auto& item : items) {
450 item->state_ = ShareableBlobDataItem::QUOTA_GRANTED;
451 }
452 final_callback.Run(true);
453 return;
454 }
455
456 void BlobMemoryController::OnCreateFiles(
457 std::vector<uint64_t> file_sizes,
458 std::vector<scoped_refptr<ShareableBlobDataItem>> pending_items,
459 uint64_t disk_quota_entry,
460 const FileQuotaRequestCallback& file_callback,
461 std::vector<FileCreationInfo> result) {
462 if (result.empty()) {
463 LOG(ERROR) << "Error creating files";
464 for (uint64_t size : file_sizes) {
465 disk_used_ -= size;
466 }
467 file_callback.Run(false, std::vector<FileCreationInfo>());
468 DisableDisk();
469 return;
470 }
471 // Check if the user cancelled the file request.
472 if (pending_file_request_sizes_.erase(disk_quota_entry) == 0) {
473 LOG(ERROR) << "user cancelled file write.";
474 file_runner_->PostTask(FROM_HERE,
475 base::Bind(&DestructFiles, base::Passed(&result)));
476 return;
477 }
478 DCHECK_EQ(file_sizes.size(), result.size());
479 for (size_t i = 0; i < result.size(); i++) {
480 result[i].file_reference->AddFinalReleaseCallback(
481 base::Bind(&BlobMemoryController::OnBlobFileDelete,
482 ptr_factory_.GetWeakPtr(), file_sizes[i]));
483 }
484 for (const auto& item : pending_items) {
485 item->state_ = ShareableBlobDataItem::QUOTA_GRANTED;
486 }
487 LOG(ERROR) << "Created " << result.size() << " files!";
488 file_callback.Run(true, std::move(result));
489 }
490
491 void BlobMemoryController::MaybeGrantPendingQuotaRequests() {
492 size_t space_available = limits_.max_blob_in_memory_space - blob_memory_used_;
493 while (!blobs_waiting_for_paging_.empty() &&
494 limits_.max_blob_in_memory_space - blob_memory_used_ >=
495 blobs_waiting_for_paging_.front().first) {
496 auto size_callback_pair = blobs_waiting_for_paging_.front();
497 blobs_waiting_for_paging_.pop_front();
498 space_available -= size_callback_pair.first;
499 blobs_waiting_for_paging_size_ -= size_callback_pair.first;
500 blob_memory_used_ += size_callback_pair.first;
501 size_callback_pair.second.Run(true);
502 }
503 RecordTracingCounters();
504 }
505
506 void BlobMemoryController::MaybeSchedulePagingUntilSystemHealthy() {
507 // Don't do paging when others are happening, as we don't change our
508 // blobs_waiting_for_paging_size_ value until after the paging files have
509 // been written.
510 if (pending_pagings_ != 0 || !disk_enabled_)
511 return;
512
513 // We try to page items to disk until our current system size + requested
514 // memory is below our size limit.
515 while (blobs_waiting_for_paging_size_ + blob_memory_used_ >
516 limits_.max_blob_in_memory_space) {
517 // We only page when we have enough items to fill a while page file.
518 if (recent_item_cache_bytes_ < limits_.min_page_file_size)
519 break;
520 DCHECK_LE(limits_.min_page_file_size,
521 static_cast<uint64_t>(blob_memory_used_));
522 size_t total_items_size = 0;
523 std::unique_ptr<std::vector<scoped_refptr<ShareableBlobDataItem>>>
524 items_for_disk(new std::vector<scoped_refptr<ShareableBlobDataItem>>());
525 // Collect our items.
526 while (total_items_size < limits_.min_page_file_size &&
527 !recent_item_cache_.empty()) {
528 auto iterator = --recent_item_cache_.end();
529 ShareableBlobDataItem* item = iterator->second;
530 DCHECK(item);
531 DCHECK_EQ(item->item()->type(), DataElement::TYPE_BYTES);
532 recent_item_cache_.Erase(iterator);
533 recent_item_cache_bytes_ -= static_cast<size_t>(item->item()->length());
534 items_saving_to_disk_.insert(item->item_id());
535 size_t size = base::checked_cast<size_t>(item->item()->length());
536 total_items_size += size;
537 items_for_disk->push_back(make_scoped_refptr(item));
538 }
539 if (total_items_size == 0)
540 break;
541
542 // Update our bookkeeping.
543 pending_pagings_++;
544 disk_used_ += total_items_size;
545 DCHECK_GE(blob_memory_used_, total_items_size);
546 LOG(ERROR) << "saving " << total_items_size << " to disk.";
547 blob_memory_used_ -= total_items_size;
548 in_flight_memory_used_ += total_items_size;
549 std::string file_name = base::Uint64ToString(current_file_num_++);
550 // Create our file reference.
551 scoped_refptr<ShareableFileReference> file_ref =
552 ShareableFileReference::GetOrCreate(
553 blob_storage_dir_.Append(file_name),
554 ShareableFileReference::DELETE_ON_FINAL_RELEASE,
555 file_runner_.get());
556 // Add the release callback so we decrement our disk usage on file deletion.
557 file_ref->AddFinalReleaseCallback(
558 base::Bind(&BlobMemoryController::OnBlobFileDelete,
559 ptr_factory_.GetWeakPtr(), total_items_size));
560 // Post the file writing task.
561 base::PostTaskAndReplyWithResult(
562 file_runner_.get(), FROM_HERE,
563 base::Bind(&WriteItemsToFile, items_for_disk.get(), total_items_size,
564 base::Passed(&file_ref)),
565 base::Bind(&BlobMemoryController::OnPagingComplete,
566 ptr_factory_.GetWeakPtr(), base::Passed(&items_for_disk),
567 total_items_size));
568 }
569 RecordTracingCounters();
570 }
571
572 void BlobMemoryController::OnPagingComplete(
573 std::unique_ptr<std::vector<scoped_refptr<ShareableBlobDataItem>>> items,
574 size_t total_items_size,
575 FileCreationInfo result) {
576 if (!disk_enabled_)
577 return;
578 if (result.error != File::FILE_OK) {
579 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.PagingError", -result.error,
580 -File::FILE_ERROR_MAX);
581 disk_used_ -= total_items_size;
582 DisableDisk();
583 return;
584 }
585 DCHECK_LT(0u, pending_pagings_);
586 pending_pagings_--;
587
588 // Switch from the data backing item to a new file backing item.
589 uint64_t offset = 0;
590 for (const scoped_refptr<ShareableBlobDataItem>& shareable_item :
591 *items.get()) {
592 scoped_refptr<BlobDataItem> new_item(new BlobDataItem(
593 base::WrapUnique(new DataElement()), result.file_reference));
594 new_item->data_element_ptr()->SetToFilePathRange(
595 result.file_reference->path(), offset, shareable_item->item()->length(),
596 result.last_modified);
597 shareable_item->item_ = new_item;
598 items_saving_to_disk_.erase(shareable_item->item_id());
599 offset += shareable_item->item()->length();
600 }
601 in_flight_memory_used_ -= total_items_size;
602
603 // We want callback on blobs up to the amount we've freed.
604 MaybeGrantPendingQuotaRequests();
605
606 // If we still have more blobs waiting and we're not waiting on more paging
607 // operations, schedule more.
608 MaybeSchedulePagingUntilSystemHealthy();
609 }
610
611 void BlobMemoryController::RecordTracingCounters() {
612 TRACE_COUNTER2("Blob", "MemoryUsage", "RegularStorage", blob_memory_used_,
613 "InFlightToDisk", in_flight_memory_used_);
614 TRACE_COUNTER1("Blob", "DiskUsage", disk_used_);
615 TRACE_COUNTER1("Blob", "TranfersPendingOnDisk",
616 blobs_waiting_for_paging_.size());
617 TRACE_COUNTER1("Blob", "TranfersBytesPendingOnDisk",
618 blobs_waiting_for_paging_size_);
619 }
620
621 size_t BlobMemoryController::GetAvailableMemoryForBlobs() const {
622 // If disk is enabled, then we include |in_flight_memory_used_|. Otherwise we
pwnall 2016/09/16 22:45:24 This comment doesn't seem to agree with the implem
dmurph 2016/09/20 00:00:50 Merged.
623 // combine the in memory and in flight quotas.
624 if (disk_enabled_) {
625 if (limits_.total_memory_space() < memory_usage())
626 return 0;
627 return limits_.total_memory_space() - memory_usage();
628 }
629 if (limits_.total_memory_space() < memory_usage())
630 return 0;
631 return limits_.total_memory_space() - memory_usage();
632 }
633
634 uint64_t BlobMemoryController::GetAvailableDiskSpaceForBlobs() const {
635 return disk_enabled_
pwnall 2016/09/16 22:45:24 This may be easier to read if you have a separate
dmurph 2016/09/20 00:00:50 Done.
636 ? limits_.max_blob_disk_space - disk_used_ -
637 blobs_waiting_for_paging_size_
638 : 0;
639 }
640
641 void BlobMemoryController::OnBlobFileDelete(uint64_t size,
642 const base::FilePath& path) {
643 DCHECK_LE(size, disk_used_);
644 LOG(ERROR) << "File deleted " << size;
645 disk_used_ -= size;
646 }
647
648 } // namespace storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698