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

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

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

Powered by Google App Engine
This is Rietveld 408576698