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

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

Issue 2339933004: [BlobStorage] BlobMemoryController & tests (Closed)
Patch Set: switched back from file literal for windows compile Created 4 years, 2 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/guid.h"
14 #include "base/location.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "base/numerics/safe_math.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/threading/thread_restrictions.h"
25 #include "base/time/time.h"
26 #include "base/trace_event/trace_event.h"
27 #include "base/tuple.h"
28 #include "storage/browser/blob/blob_data_builder.h"
29 #include "storage/browser/blob/blob_data_item.h"
30 #include "storage/browser/blob/shareable_blob_data_item.h"
31 #include "storage/browser/blob/shareable_file_reference.h"
32 #include "storage/common/data_element.h"
33
34 using base::File;
35 using base::FilePath;
36 using FileCreationInfo = storage::BlobMemoryController::FileCreationInfo;
37
38 namespace storage {
39 namespace {
40 using QuotaAllocationTask = BlobMemoryController::QuotaAllocationTask;
41
42 File::Error CreateBlobDirectory(const FilePath& blob_storage_dir) {
43 File::Error error;
44 bool success = base::CreateDirectoryAndGetError(blob_storage_dir, &error);
45 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.CreateDirectroyResult", -error,
46 -File::FILE_ERROR_MAX);
47 if (!success) {
48 return error;
49 }
50 return File::FILE_OK;
51 }
52
53 void DestructFile(File infos_without_references) {}
54
55 // Used for new unpopulated file items. Caller must populate file reference in
56 // returned FileCreationInfos.
57 std::pair<std::vector<FileCreationInfo>, File::Error> CreateEmptyFiles(
58 const FilePath& blob_storage_dir,
59 scoped_refptr<base::TaskRunner> file_task_runner,
60 std::vector<base::FilePath> file_paths) {
61 base::ThreadRestrictions::AssertIOAllowed();
62
63 File::Error dir_create_status = CreateBlobDirectory(blob_storage_dir);
64 if (dir_create_status != File::FILE_OK)
65 return std::make_pair(std::vector<FileCreationInfo>(), dir_create_status);
66
67 std::vector<FileCreationInfo> result;
68 for (const base::FilePath& file_path : file_paths) {
69 FileCreationInfo creation_info;
70 // Try to open our file.
71 File file(file_path, File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);
72 creation_info.path = std::move(file_path);
73 creation_info.file_deletion_runner = file_task_runner;
74 creation_info.error = file.error_details();
75 if (creation_info.error != File::FILE_OK)
76 return std::make_pair(std::vector<FileCreationInfo>(),
77 creation_info.error);
78
79 // Grab the file info to get the "last modified" time and store the file.
80 File::Info file_info;
81 bool success = file.GetInfo(&file_info);
82 creation_info.error = success ? File::FILE_OK : File::FILE_ERROR_FAILED;
83 if (!success)
84 return std::make_pair(std::vector<FileCreationInfo>(),
85 creation_info.error);
86 creation_info.file = std::move(file);
87
88 result.push_back(std::move(creation_info));
89 }
90 return std::make_pair(std::move(result), File::FILE_OK);
91 }
92
93 // Used to evict multiple memory items out to a single file. Caller must
94 // populate file reference in returned FileCreationInfo.
95 FileCreationInfo CreateFileAndWriteItems(
96 const FilePath& blob_storage_dir,
97 const FilePath& file_path,
98 scoped_refptr<base::TaskRunner> file_task_runner,
99 std::vector<DataElement*> items,
100 size_t total_size_bytes) {
101 DCHECK_NE(0u, total_size_bytes);
102 UMA_HISTOGRAM_MEMORY_KB("Storage.Blob.PageFileSize", total_size_bytes / 1024);
103 base::ThreadRestrictions::AssertIOAllowed();
104
105 FileCreationInfo creation_info;
106 creation_info.file_deletion_runner = std::move(file_task_runner);
107 creation_info.error = CreateBlobDirectory(blob_storage_dir);
108 if (creation_info.error != File::FILE_OK)
109 return creation_info;
110
111 // Create the page file.
112 File file(file_path, File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);
113 creation_info.path = file_path;
114 creation_info.error = file.error_details();
115 if (creation_info.error != File::FILE_OK)
116 return creation_info;
117
118 // Write data.
119 file.SetLength(total_size_bytes);
120 int bytes_written = 0;
121 for (DataElement* element : items) {
122 DCHECK_EQ(DataElement::TYPE_BYTES, element->type());
123 size_t length = base::checked_cast<size_t>(element->length());
124 size_t bytes_left = length;
125 while (bytes_left > 0) {
126 bytes_written =
127 file.WriteAtCurrentPos(element->bytes() + (length - bytes_left),
128 base::saturated_cast<int>(bytes_left));
129 if (bytes_written < 0)
130 break;
131 DCHECK_LE(static_cast<size_t>(bytes_written), bytes_left);
132 bytes_left -= bytes_written;
133 }
134 if (bytes_written < 0)
135 break;
136 }
137
138 File::Info info;
139 bool success = file.GetInfo(&info);
140 creation_info.error =
141 bytes_written < 0 || !success ? File::FILE_ERROR_FAILED : File::FILE_OK;
142 creation_info.last_modified = info.last_modified;
143 return creation_info;
144 }
145
146 uint64_t GetTotalSizeAndFileSizes(
147 const std::vector<scoped_refptr<ShareableBlobDataItem>>&
148 unreserved_file_items,
149 std::vector<uint64_t>* file_sizes_output) {
150 uint64_t total_size_output = 0;
151 base::SmallMap<std::map<uint64_t, uint64_t>> file_id_to_sizes;
152 for (const auto& item : unreserved_file_items) {
153 const DataElement& element = item->item()->data_element();
154 uint64_t file_id = BlobDataBuilder::GetFutureFileID(element);
155 auto it = file_id_to_sizes.find(file_id);
156 if (it != file_id_to_sizes.end()) {
157 it->second = std::max(it->second, element.offset() + element.length());
158 } else {
159 file_id_to_sizes[file_id] = element.offset() + element.length();
160 }
161 total_size_output += element.length();
162 }
163 for (const auto& size_pair : file_id_to_sizes) {
164 file_sizes_output->push_back(size_pair.second);
165 }
166 return total_size_output;
167 }
168
169 } // namespace
170
171 BlobMemoryController::QuotaAllocationTask::~QuotaAllocationTask() {}
172
173 // The my_list_position_ iterator is stored so that we can remove ourself from
174 // the task list when it is cancelled.
175 template <typename TaskList>
176 class BlobMemoryController::BaseQuotaAllocationTask
177 : public BlobMemoryController::QuotaAllocationTask {
178 public:
179 BaseQuotaAllocationTask() : allocation_size_(0) {}
180 explicit BaseQuotaAllocationTask(uint64_t allocation_size)
181 : allocation_size_(allocation_size) {}
182
183 ~BaseQuotaAllocationTask() override {}
184
185 void set_my_list_position(typename TaskList::iterator my_list_position) {
186 my_list_position_ = my_list_position;
187 }
188 typename TaskList::iterator my_list_position() const {
189 return my_list_position_;
190 }
191
192 uint64_t allocation_size() { return allocation_size_; }
193 void set_allocation_size(uint64_t allocation_size) {
194 allocation_size_ = allocation_size;
195 }
196
197 virtual base::WeakPtr<QuotaAllocationTask> GetWeakPtr() = 0;
198
199 private:
200 uint64_t allocation_size_;
201 typename TaskList::iterator my_list_position_;
202 };
203
204 class BlobMemoryController::MemoryQuotaAllocationTask
205 : public BlobMemoryController::BaseQuotaAllocationTask<
206 PendingMemoryQuotaTaskList> {
207 public:
208 MemoryQuotaAllocationTask(
209 BlobMemoryController* controller,
210 uint64_t quota_request_size,
211 std::vector<scoped_refptr<ShareableBlobDataItem>> pending_items,
212 MemoryQuotaRequestCallback done_callback)
213 : BaseQuotaAllocationTask(quota_request_size),
214 controller_(controller),
215 pending_items_(std::move(pending_items)),
216 done_callback_(std::move(done_callback)),
217 weak_factory_(this) {}
218
219 ~MemoryQuotaAllocationTask() override = default;
220
221 void RunDoneCallback(bool success) {
222 // Make sure we clear the weak pointers we gave to the caller beforehand.
223 weak_factory_.InvalidateWeakPtrs();
224 if (success)
225 controller_->SetGrantedAndMemoryAllocation(&pending_items_);
226 done_callback_.Run(success);
227 }
228
229 base::WeakPtr<QuotaAllocationTask> GetWeakPtr() override {
230 return weak_factory_.GetWeakPtr();
231 }
232
233 void Cancel() override {
234 controller_->pending_memory_quota_total_size_ -= allocation_size();
235 // This call destroys this object.
236 controller_->pending_memory_quota_tasks_.erase(my_list_position());
237 }
238
239 private:
240 BlobMemoryController* controller_;
241 std::vector<scoped_refptr<ShareableBlobDataItem>> pending_items_;
242 MemoryQuotaRequestCallback done_callback_;
243
244 base::WeakPtrFactory<MemoryQuotaAllocationTask> weak_factory_;
245 DISALLOW_COPY_AND_ASSIGN(MemoryQuotaAllocationTask);
246 };
247
248 class BlobMemoryController::FileQuotaAllocationTask
249 : public BlobMemoryController::BaseQuotaAllocationTask<
250 PendingFileQuotaTaskList> {
251 public:
252 FileQuotaAllocationTask(
253 BlobMemoryController* memory_controller,
254 std::vector<scoped_refptr<ShareableBlobDataItem>> unreserved_file_items,
255 const FileQuotaRequestCallback& done_callback)
256 : controller_(memory_controller),
257 done_callback_(done_callback),
258 weak_factory_(this) {
259 // Get the file sizes and total size.
260 std::vector<uint64_t> file_sizes;
261 uint64_t total_size =
262 GetTotalSizeAndFileSizes(unreserved_file_items, &file_sizes);
263 DCHECK_LE(total_size, controller_->GetAvailableFileSpaceForBlobs());
264 set_allocation_size(total_size);
265
266 // Check & set our item states.
267 for (auto& shareable_item : unreserved_file_items) {
268 DCHECK_EQ(ShareableBlobDataItem::QUOTA_NEEDED, shareable_item->state());
269 DCHECK_EQ(DataElement::TYPE_FILE, shareable_item->item()->type());
270 shareable_item->set_state(ShareableBlobDataItem::QUOTA_REQUESTED);
271 }
272 pending_items_ = std::move(unreserved_file_items);
273
274 // Increment disk usage and create our file references.
275 controller_->disk_used_ += allocation_size();
276 std::vector<base::FilePath> file_paths;
277 std::vector<scoped_refptr<ShareableFileReference>> references;
278 for (size_t i = 0; i < file_sizes.size(); i++) {
279 file_paths.push_back(controller_->GenerateNextPageFileName());
280 references.push_back(ShareableFileReference::GetOrCreate(
281 file_paths.back(), ShareableFileReference::DELETE_ON_FINAL_RELEASE,
282 controller_->file_runner_.get()));
283 references.back()->AddFinalReleaseCallback(base::Bind(
284 &BlobMemoryController::OnBlobFileDelete,
285 controller_->quota_bookkeeping_factory_.GetWeakPtr(), file_sizes[i]));
286 }
287
288 // Send file creation task to file thread.
289 base::PostTaskAndReplyWithResult(
290 controller_->file_runner_.get(), FROM_HERE,
291 base::Bind(&CreateEmptyFiles, controller_->blob_storage_dir_,
292 controller_->file_runner_, base::Passed(&file_paths)),
293 base::Bind(&FileQuotaAllocationTask::OnCreateEmptyFiles,
294 weak_factory_.GetWeakPtr(), base::Passed(&references)));
295 controller_->RecordTracingCounters();
296 }
297 ~FileQuotaAllocationTask() override {}
298
299 void RunDoneCallback(bool success, std::vector<FileCreationInfo> file_info) {
300 // Make sure we clear the weak pointers we gave to the caller beforehand.
301 weak_factory_.InvalidateWeakPtrs();
302
303 // We want to destroy this object on the exit of this method if we were
304 // successful.
305 std::unique_ptr<FileQuotaAllocationTask> this_object;
306 if (success) {
307 for (auto& item : pending_items_) {
308 item->set_state(ShareableBlobDataItem::QUOTA_GRANTED);
309 }
310 this_object = std::move(*my_list_position());
311 controller_->pending_file_quota_tasks_.erase(my_list_position());
312 }
313
314 done_callback_.Run(success, std::move(file_info));
315 }
316
317 base::WeakPtr<QuotaAllocationTask> GetWeakPtr() override {
318 return weak_factory_.GetWeakPtr();
319 }
320
321 void Cancel() override {
322 // This call destroys this object. We rely on ShareableFileReference's
323 // final release callback for disk_usage_ accounting.
324 controller_->pending_file_quota_tasks_.erase(my_list_position());
325 }
326
327 void OnCreateEmptyFiles(
328 std::vector<scoped_refptr<ShareableFileReference>> references,
329 std::pair<std::vector<FileCreationInfo>, File::Error> files_and_error) {
330 auto& files = files_and_error.first;
331 if (files.empty()) {
332 controller_->disk_used_ -= allocation_size();
333 // This will call call our callback and delete the object correctly.
334 controller_->DisableFilePaging(files_and_error.second);
335 return;
336 }
337 DCHECK_EQ(files.size(), references.size());
338 for (size_t i = 0; i < files.size(); i++) {
339 files[i].file_reference = std::move(references[i]);
340 }
341 RunDoneCallback(true, std::move(files));
342 }
343
344 private:
345 BlobMemoryController* controller_;
346 std::vector<scoped_refptr<ShareableBlobDataItem>> pending_items_;
347 scoped_refptr<base::TaskRunner> file_runner_;
348 FileQuotaRequestCallback done_callback_;
349
350 base::WeakPtrFactory<FileQuotaAllocationTask> weak_factory_;
351 DISALLOW_COPY_AND_ASSIGN(FileQuotaAllocationTask);
352 };
353
354 FileCreationInfo::FileCreationInfo() {}
355 FileCreationInfo::~FileCreationInfo() {
356 if (file.IsValid()) {
357 DCHECK(file_deletion_runner);
358 file_deletion_runner->PostTask(
359 FROM_HERE, base::Bind(&DestructFile, base::Passed(&file)));
360 }
361 }
362 FileCreationInfo::FileCreationInfo(FileCreationInfo&&) = default;
363 FileCreationInfo& FileCreationInfo::operator=(FileCreationInfo&&) = default;
364
365 BlobMemoryController::BlobMemoryController(
366 const base::FilePath& storage_directory,
367 scoped_refptr<base::TaskRunner> file_runner)
368 : file_paging_enabled_(file_runner.get() != nullptr),
369 blob_storage_dir_(storage_directory),
370 file_runner_(std::move(file_runner)),
371 populated_memory_items_(
372 base::MRUCache<uint64_t, ShareableBlobDataItem*>::NO_AUTO_EVICT),
373 file_task_factory_(this),
374 quota_bookkeeping_factory_(this) {}
375
376 BlobMemoryController::~BlobMemoryController() {}
377
378 void BlobMemoryController::DisableFilePaging(base::File::Error reason) {
379 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.PagingDisabled", -reason,
380 -File::FILE_ERROR_MAX);
381 file_paging_enabled_ = false;
382 in_flight_memory_used_ = 0;
383 items_paging_to_file_.clear();
384 pending_evictions_ = 0;
385 pending_memory_quota_total_size_ = 0;
386 populated_memory_items_.Clear();
387 populated_memory_items_bytes_ = 0;
388 file_runner_ = nullptr;
389
390 PendingMemoryQuotaTaskList old_memory_tasks;
391 PendingFileQuotaTaskList old_file_tasks;
392 std::swap(old_memory_tasks, pending_memory_quota_tasks_);
393 std::swap(old_file_tasks, pending_file_quota_tasks_);
394 // Kill all disk operation callbacks.
395 file_task_factory_.InvalidateWeakPtrs();
396
397 // Don't call the callbacks until we have a consistent state.
398 for (auto& memory_request : old_memory_tasks) {
399 memory_request->RunDoneCallback(false);
400 }
401 for (auto& file_request : old_file_tasks) {
402 file_request->RunDoneCallback(false, std::vector<FileCreationInfo>());
403 }
404 }
405
406 BlobMemoryController::Strategy BlobMemoryController::DetermineStrategy(
407 size_t preemptive_transported_bytes,
408 uint64_t total_transportation_bytes) const {
409 if (total_transportation_bytes == 0) {
410 return Strategy::NONE_NEEDED;
411 }
412 if (!CanReserveQuota(total_transportation_bytes)) {
413 return Strategy::TOO_LARGE;
414 }
415 // Handle the case where we have all the bytes preemptively transported, and
416 // we can also fit them.
417 if (preemptive_transported_bytes == total_transportation_bytes &&
418 pending_memory_quota_tasks_.empty() &&
419 preemptive_transported_bytes < GetAvailableMemoryForBlobs()) {
420 return Strategy::NONE_NEEDED;
421 }
422 if (file_paging_enabled_ &&
423 (total_transportation_bytes > limits_.max_blob_in_memory_space)) {
424 return Strategy::FILE;
425 }
426 if (total_transportation_bytes > limits_.max_ipc_memory_size) {
427 return Strategy::SHARED_MEMORY;
428 }
429 return Strategy::IPC;
430 }
431
432 bool BlobMemoryController::CanReserveQuota(uint64_t size) const {
433 // We check each size independently as a blob can't be constructed in both
434 // disk and memory.
435 return size <= GetAvailableMemoryForBlobs() ||
436 size <= GetAvailableFileSpaceForBlobs();
437 }
438
439 base::WeakPtr<QuotaAllocationTask> BlobMemoryController::ReserveMemoryQuota(
440 std::vector<scoped_refptr<ShareableBlobDataItem>> unreserved_memory_items,
441 const MemoryQuotaRequestCallback& done_callback) {
442 base::CheckedNumeric<uint64_t> unsafe_total_bytes_needed = 0;
443 for (auto& item : unreserved_memory_items) {
444 DCHECK_EQ(ShareableBlobDataItem::QUOTA_NEEDED, item->state());
445 DCHECK(item->item()->type() == DataElement::TYPE_BYTES_DESCRIPTION ||
446 item->item()->type() == DataElement::TYPE_BYTES);
447 DCHECK(item->item()->length() > 0);
448 unsafe_total_bytes_needed += item->item()->length();
449 item->set_state(ShareableBlobDataItem::QUOTA_REQUESTED);
450 }
451
452 uint64_t total_bytes_needed = unsafe_total_bytes_needed.ValueOrDie();
453 if (total_bytes_needed == 0) {
454 done_callback.Run(true);
455 return base::WeakPtr<QuotaAllocationTask>();
456 }
457
458 // If we're currently waiting for blobs to page already, then we add
459 // ourselves to the end of the queue. Once paging is complete, we'll schedule
460 // more paging for any more pending blobs.
461 if (!pending_memory_quota_tasks_.empty()) {
462 return AppendMemoryTask(total_bytes_needed,
463 std::move(unreserved_memory_items), done_callback);
464 }
465
466 // Store right away if we can.
467 if (total_bytes_needed <= GetAvailableMemoryForBlobs()) {
468 blob_memory_used_ += total_bytes_needed;
469 SetGrantedAndMemoryAllocation(&unreserved_memory_items);
470 MaybeScheduleEvictionUntilSystemHealthy();
471 done_callback.Run(true);
472 return base::WeakPtr<QuotaAllocationTask>();
473 }
474
475 // Size is larger than available memory.
476 DCHECK(pending_memory_quota_tasks_.empty());
477 DCHECK_EQ(0u, pending_memory_quota_total_size_);
478
479 auto weak_ptr = AppendMemoryTask(
480 total_bytes_needed, std::move(unreserved_memory_items), done_callback);
481 MaybeScheduleEvictionUntilSystemHealthy();
482 return weak_ptr;
483 }
484
485 base::WeakPtr<QuotaAllocationTask> BlobMemoryController::ReserveFileQuota(
486 std::vector<scoped_refptr<ShareableBlobDataItem>> unreserved_file_items,
487 const FileQuotaRequestCallback& done_callback) {
488 pending_file_quota_tasks_.push_back(base::MakeUnique<FileQuotaAllocationTask>(
489 this, std::move(unreserved_file_items), done_callback));
490 pending_file_quota_tasks_.back()->set_my_list_position(
491 --pending_file_quota_tasks_.end());
492 return pending_file_quota_tasks_.back()->GetWeakPtr();
493 }
494
495 void BlobMemoryController::NotifyMemoryItemUsed(ShareableBlobDataItem* item) {
496 DCHECK_EQ(DataElement::TYPE_BYTES, item->item()->type());
497 DCHECK_EQ(ShareableBlobDataItem::POPULATED_WITH_QUOTA, item->state());
498 // We don't want to re-add the item if we're currently paging it to disk.
499 if (items_paging_to_file_.find(item->item_id()) !=
500 items_paging_to_file_.end())
501 return;
502 auto iterator = populated_memory_items_.Get(item->item_id());
503 if (iterator == populated_memory_items_.end()) {
504 populated_memory_items_bytes_ +=
505 static_cast<size_t>(item->item()->length());
506 populated_memory_items_.Put(item->item_id(), item);
507 MaybeScheduleEvictionUntilSystemHealthy();
508 }
509 }
510
511 base::WeakPtr<QuotaAllocationTask> BlobMemoryController::AppendMemoryTask(
512 uint64_t total_bytes_needed,
513 std::vector<scoped_refptr<ShareableBlobDataItem>> unreserved_memory_items,
514 const MemoryQuotaRequestCallback& done_callback) {
515 DCHECK(file_paging_enabled_)
516 << "Caller tried to reserve memory when CanReserveQuota("
517 << total_bytes_needed << ") would have returned false.";
518
519 pending_memory_quota_total_size_ += total_bytes_needed;
520 pending_memory_quota_tasks_.push_back(
521 base::MakeUnique<MemoryQuotaAllocationTask>(
522 this, total_bytes_needed, std::move(unreserved_memory_items),
523 std::move(done_callback)));
524 pending_memory_quota_tasks_.back()->set_my_list_position(
525 --pending_memory_quota_tasks_.end());
526
527 return pending_memory_quota_tasks_.back()->GetWeakPtr();
528 }
529
530 void BlobMemoryController::MaybeGrantPendingMemoryRequests() {
531 while (!pending_memory_quota_tasks_.empty() &&
532 limits_.max_blob_in_memory_space - blob_memory_used_ >=
533 pending_memory_quota_tasks_.front()->allocation_size()) {
534 std::unique_ptr<MemoryQuotaAllocationTask> memory_task =
535 std::move(pending_memory_quota_tasks_.front());
536 pending_memory_quota_tasks_.pop_front();
537 size_t request_size = memory_task->allocation_size();
538 pending_memory_quota_total_size_ -= request_size;
539 blob_memory_used_ += request_size;
540 memory_task->RunDoneCallback(true);
541 }
542 RecordTracingCounters();
543 }
544
545 size_t BlobMemoryController::CollectItemsForEviction(
546 std::vector<scoped_refptr<ShareableBlobDataItem>>* output) {
547 base::CheckedNumeric<size_t> total_items_size = 0;
548 // Process the recent item list and remove items until we have at least a
549 // minimum file size.
550 while (total_items_size.ValueOrDie() < limits_.min_page_file_size &&
551 !populated_memory_items_.empty()) {
552 auto iterator = --populated_memory_items_.end();
553 ShareableBlobDataItem* item = iterator->second;
554 DCHECK(item);
555 DCHECK_EQ(item->item()->type(), DataElement::TYPE_BYTES);
556 populated_memory_items_.Erase(iterator);
557 size_t size = base::checked_cast<size_t>(item->item()->length());
558 populated_memory_items_bytes_ -= size;
559 total_items_size += size;
560 output->push_back(make_scoped_refptr(item));
561 }
562 return total_items_size.ValueOrDie();
563 }
564
565 void BlobMemoryController::MaybeScheduleEvictionUntilSystemHealthy() {
566 // Don't do eviction when others are happening, as we don't change our
567 // pending_memory_quota_total_size_ value until after the paging files have
568 // been written.
569 if (pending_evictions_ != 0 || !file_paging_enabled_)
570 return;
571
572 // We try to page items to disk until our current system size + requested
573 // memory is below our size limit.
574 while (pending_memory_quota_total_size_ + blob_memory_used_ >
575 limits_.memory_limit_before_paging()) {
576 // We only page when we have enough items to fill a whole page file.
577 if (populated_memory_items_bytes_ < limits_.min_page_file_size)
578 break;
579 DCHECK_LE(limits_.min_page_file_size,
580 static_cast<uint64_t>(blob_memory_used_));
581
582 std::vector<scoped_refptr<ShareableBlobDataItem>> items_to_swap;
583 size_t total_items_size = CollectItemsForEviction(&items_to_swap);
584 if (total_items_size == 0)
585 break;
586
587 std::vector<DataElement*> items_for_paging;
588 for (auto& shared_blob_item : items_to_swap) {
589 items_paging_to_file_.insert(shared_blob_item->item_id());
590 items_for_paging.push_back(shared_blob_item->item()->data_element_ptr());
591 }
592
593 // Update our bookkeeping.
594 pending_evictions_++;
michaeln 2016/10/14 01:53:26 i think this value is either 0 or 1 if that's righ
dmurph 2016/10/14 23:31:48 I need this to be a number, as I can be saving mul
595 disk_used_ += total_items_size;
596 in_flight_memory_used_ += total_items_size;
597
598 // Create our file reference.
599 scoped_refptr<ShareableFileReference> file_reference =
600 ShareableFileReference::GetOrCreate(
601 GenerateNextPageFileName(),
602 ShareableFileReference::DELETE_ON_FINAL_RELEASE,
603 file_runner_.get());
604 // Add the release callback so we decrement our disk usage on file deletion.
605 file_reference->AddFinalReleaseCallback(
606 base::Bind(&BlobMemoryController::OnBlobFileDelete,
607 quota_bookkeeping_factory_.GetWeakPtr(), total_items_size));
608
609 // Post the file writing task.
610 base::PostTaskAndReplyWithResult(
611 file_runner_.get(), FROM_HERE,
612 base::Bind(&CreateFileAndWriteItems, blob_storage_dir_,
613 file_reference->path(), file_runner_,
614 base::Passed(&items_for_paging), total_items_size),
615 base::Bind(&BlobMemoryController::OnEvictionComplete,
616 file_task_factory_.GetWeakPtr(),
617 base::Passed(&file_reference), base::Passed(&items_to_swap),
618 total_items_size));
619 }
620 RecordTracingCounters();
621 }
622
623 void BlobMemoryController::OnEvictionComplete(
624 scoped_refptr<ShareableFileReference> file_reference,
625 std::vector<scoped_refptr<ShareableBlobDataItem>> items,
626 size_t total_items_size,
627 FileCreationInfo result) {
628 if (!file_paging_enabled_)
629 return;
630
631 if (result.error != File::FILE_OK) {
632 DisableFilePaging(result.error);
633 return;
634 }
635
636 DCHECK_LT(0u, pending_evictions_);
637 pending_evictions_--;
638
639 // Switch item from memory to the new file.
640 uint64_t offset = 0;
641 for (const scoped_refptr<ShareableBlobDataItem>& shareable_item : items) {
642 scoped_refptr<BlobDataItem> new_item(
643 new BlobDataItem(base::WrapUnique(new DataElement()), file_reference));
644 new_item->data_element_ptr()->SetToFilePathRange(
645 file_reference->path(), offset, shareable_item->item()->length(),
646 result.last_modified);
647 shareable_item->memory_quota_allocation()->RunAndReset();
michaeln 2016/10/14 01:53:26 i wish the ScopedClosureRunner were operator bool
dmurph 2016/10/14 23:31:46 Done.
648 shareable_item->set_item(new_item);
649 items_paging_to_file_.erase(shareable_item->item_id());
650 offset += shareable_item->item()->length();
651 }
652 in_flight_memory_used_ -= total_items_size;
653
654 // We want callback on blobs up to the amount we've freed.
655 MaybeGrantPendingMemoryRequests();
656
657 // If we still have more blobs waiting and we're not waiting on more paging
658 // operations, schedule more.
659 MaybeScheduleEvictionUntilSystemHealthy();
660 }
661
662 FilePath BlobMemoryController::GenerateNextPageFileName() {
663 std::string file_name = base::Uint64ToString(current_file_num_++);
664 return blob_storage_dir_.Append(
665 FilePath::StringType(file_name.begin(), file_name.end()));
666 }
667
668 void BlobMemoryController::RecordTracingCounters() const {
669 TRACE_COUNTER2("Blob", "MemoryUsage", "TotalStorage", blob_memory_used_,
670 "InFlightToDisk", in_flight_memory_used_);
671 TRACE_COUNTER1("Blob", "DiskUsage", disk_used_);
672 TRACE_COUNTER1("Blob", "TranfersPendingOnDisk",
673 pending_memory_quota_tasks_.size());
674 TRACE_COUNTER1("Blob", "TranfersBytesPendingOnDisk",
675 pending_memory_quota_total_size_);
676 }
677
678 size_t BlobMemoryController::GetAvailableMemoryForBlobs() const {
679 if (limits_.max_blob_in_memory_space < memory_usage())
680 return 0;
681 return limits_.max_blob_in_memory_space - memory_usage();
682 }
683
684 uint64_t BlobMemoryController::GetAvailableFileSpaceForBlobs() const {
685 if (!file_paging_enabled_)
686 return 0;
687 // Sometimes we're only paging part of what we need for the new blob, so add
688 // the rest of the size we need into our disk usage if this is the case.
689 uint64_t total_disk_used = disk_used_;
690 if (in_flight_memory_used_ < pending_memory_quota_total_size_) {
691 total_disk_used +=
692 pending_memory_quota_total_size_ - in_flight_memory_used_;
693 }
694 if (limits_.max_blob_disk_space < total_disk_used)
695 return 0;
696 return limits_.max_blob_disk_space - total_disk_used;
697 }
698
699 void BlobMemoryController::DecrementMemoryQuotaAndRemoveFromRecents(
700 uint64_t item_id,
701 uint64_t size) {
702 DCHECK_LE(size, blob_memory_used_);
703 blob_memory_used_ -= size;
704 auto iterator = populated_memory_items_.Get(item_id);
705 if (iterator != populated_memory_items_.end()) {
706 DCHECK_GE(populated_memory_items_bytes_, size);
707 populated_memory_items_bytes_ -= size;
708 populated_memory_items_.Erase(iterator);
709 }
710 MaybeGrantPendingMemoryRequests();
711 }
712
713 void BlobMemoryController::SetGrantedAndMemoryAllocation(
714 std::vector<scoped_refptr<ShareableBlobDataItem>>* items) {
715 for (auto& item : *items) {
716 item->set_state(ShareableBlobDataItem::QUOTA_GRANTED);
717 item->set_memory_quota_allocation(base::ScopedClosureRunner(base::Bind(
718 &BlobMemoryController::DecrementMemoryQuotaAndRemoveFromRecents,
719 quota_bookkeeping_factory_.GetWeakPtr(), item->item_id(),
720 item->item()->length())));
721 }
722 }
723
724 void BlobMemoryController::OnBlobFileDelete(uint64_t size,
725 const FilePath& path) {
726 DCHECK_LE(size, disk_used_);
727 disk_used_ -= size;
728 }
729
730 } // namespace storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698