OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 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_memory_controller.h" | 5 #include "storage/browser/blob/blob_memory_controller.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <numeric> | |
9 | 8 |
10 #include "base/callback.h" | 9 #include "base/callback.h" |
11 #include "base/callback_helpers.h" | 10 #include "base/callback_helpers.h" |
12 #include "base/containers/small_map.h" | 11 #include "base/containers/small_map.h" |
13 #include "base/files/file_util.h" | 12 #include "base/files/file_util.h" |
14 #include "base/guid.h" | 13 #include "base/guid.h" |
15 #include "base/location.h" | 14 #include "base/location.h" |
16 #include "base/memory/ptr_util.h" | 15 #include "base/memory/ptr_util.h" |
17 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
18 #include "base/numerics/safe_conversions.h" | 17 #include "base/numerics/safe_conversions.h" |
(...skipping 20 matching lines...) Expand all Loading... |
39 namespace { | 38 namespace { |
40 using FileCreationInfo = BlobMemoryController::FileCreationInfo; | 39 using FileCreationInfo = BlobMemoryController::FileCreationInfo; |
41 using MemoryAllocation = BlobMemoryController::MemoryAllocation; | 40 using MemoryAllocation = BlobMemoryController::MemoryAllocation; |
42 using QuotaAllocationTask = BlobMemoryController::QuotaAllocationTask; | 41 using QuotaAllocationTask = BlobMemoryController::QuotaAllocationTask; |
43 | 42 |
44 File::Error CreateBlobDirectory(const FilePath& blob_storage_dir) { | 43 File::Error CreateBlobDirectory(const FilePath& blob_storage_dir) { |
45 File::Error error = File::FILE_OK; | 44 File::Error error = File::FILE_OK; |
46 base::CreateDirectoryAndGetError(blob_storage_dir, &error); | 45 base::CreateDirectoryAndGetError(blob_storage_dir, &error); |
47 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.CreateDirectoryResult", -error, | 46 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.CreateDirectoryResult", -error, |
48 -File::FILE_ERROR_MAX); | 47 -File::FILE_ERROR_MAX); |
49 DLOG_IF(ERROR, error != File::FILE_OK) | |
50 << "Error creating blob storage directory: " << error; | |
51 return error; | 48 return error; |
52 } | 49 } |
53 | 50 |
54 void DestructFile(File infos_without_references) {} | 51 void DestructFile(File infos_without_references) {} |
55 | 52 |
56 // Used for new unpopulated file items. Caller must populate file reference in | 53 // Used for new unpopulated file items. Caller must populate file reference in |
57 // returned FileCreationInfos. | 54 // returned FileCreationInfos. |
58 std::pair<std::vector<FileCreationInfo>, File::Error> CreateEmptyFiles( | 55 std::pair<std::vector<FileCreationInfo>, File::Error> CreateEmptyFiles( |
59 const FilePath& blob_storage_dir, | 56 const FilePath& blob_storage_dir, |
60 scoped_refptr<base::TaskRunner> file_task_runner, | 57 scoped_refptr<base::TaskRunner> file_task_runner, |
61 std::vector<base::FilePath> file_paths) { | 58 std::vector<base::FilePath> file_paths) { |
62 base::ThreadRestrictions::AssertIOAllowed(); | 59 base::ThreadRestrictions::AssertIOAllowed(); |
63 | 60 |
64 File::Error dir_create_status = CreateBlobDirectory(blob_storage_dir); | 61 File::Error dir_create_status = CreateBlobDirectory(blob_storage_dir); |
65 if (dir_create_status != File::FILE_OK) | 62 if (dir_create_status != File::FILE_OK) |
66 return std::make_pair(std::vector<FileCreationInfo>(), dir_create_status); | 63 return std::make_pair(std::vector<FileCreationInfo>(), dir_create_status); |
67 | 64 |
68 std::vector<FileCreationInfo> result; | 65 std::vector<FileCreationInfo> result; |
69 for (const base::FilePath& file_path : file_paths) { | 66 for (const base::FilePath& file_path : file_paths) { |
70 FileCreationInfo creation_info; | 67 FileCreationInfo creation_info; |
71 // Try to open our file. | 68 // Try to open our file. |
72 File file(file_path, File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE); | 69 File file(file_path, File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE); |
73 creation_info.path = std::move(file_path); | 70 creation_info.path = std::move(file_path); |
74 creation_info.file_deletion_runner = file_task_runner; | 71 creation_info.file_deletion_runner = file_task_runner; |
75 creation_info.error = file.error_details(); | 72 creation_info.error = file.error_details(); |
76 if (creation_info.error != File::FILE_OK) { | 73 if (creation_info.error != File::FILE_OK) { |
77 return std::make_pair(std::vector<FileCreationInfo>(), | 74 return std::make_pair(std::vector<FileCreationInfo>(), |
78 creation_info.error); | 75 creation_info.error); |
79 } | 76 } |
| 77 |
| 78 // Grab the file info to get the "last modified" time and store the file. |
| 79 File::Info file_info; |
| 80 bool success = file.GetInfo(&file_info); |
| 81 creation_info.error = success ? File::FILE_OK : File::FILE_ERROR_FAILED; |
| 82 if (!success) { |
| 83 return std::make_pair(std::vector<FileCreationInfo>(), |
| 84 creation_info.error); |
| 85 } |
80 creation_info.file = std::move(file); | 86 creation_info.file = std::move(file); |
81 | 87 |
82 result.push_back(std::move(creation_info)); | 88 result.push_back(std::move(creation_info)); |
83 } | 89 } |
84 return std::make_pair(std::move(result), File::FILE_OK); | 90 return std::make_pair(std::move(result), File::FILE_OK); |
85 } | 91 } |
86 | 92 |
87 // Used to evict multiple memory items out to a single file. Caller must | 93 // Used to evict multiple memory items out to a single file. Caller must |
88 // populate file reference in returned FileCreationInfo. | 94 // populate file reference in returned FileCreationInfo. |
89 FileCreationInfo CreateFileAndWriteItems( | 95 FileCreationInfo CreateFileAndWriteItems( |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 file.WriteAtCurrentPos(element->bytes() + (length - bytes_left), | 127 file.WriteAtCurrentPos(element->bytes() + (length - bytes_left), |
122 base::saturated_cast<int>(bytes_left)); | 128 base::saturated_cast<int>(bytes_left)); |
123 if (bytes_written < 0) | 129 if (bytes_written < 0) |
124 break; | 130 break; |
125 DCHECK_LE(static_cast<size_t>(bytes_written), bytes_left); | 131 DCHECK_LE(static_cast<size_t>(bytes_written), bytes_left); |
126 bytes_left -= bytes_written; | 132 bytes_left -= bytes_written; |
127 } | 133 } |
128 if (bytes_written < 0) | 134 if (bytes_written < 0) |
129 break; | 135 break; |
130 } | 136 } |
131 if (!file.Flush()) { | |
132 creation_info.error = File::FILE_ERROR_FAILED; | |
133 return creation_info; | |
134 } | |
135 | 137 |
136 File::Info info; | 138 File::Info info; |
137 bool success = file.GetInfo(&info); | 139 bool success = file.GetInfo(&info); |
138 creation_info.error = | 140 creation_info.error = |
139 bytes_written < 0 || !success ? File::FILE_ERROR_FAILED : File::FILE_OK; | 141 bytes_written < 0 || !success ? File::FILE_ERROR_FAILED : File::FILE_OK; |
140 creation_info.last_modified = info.last_modified; | 142 creation_info.last_modified = info.last_modified; |
141 return creation_info; | 143 return creation_info; |
142 } | 144 } |
143 | 145 |
144 uint64_t GetTotalSizeAndFileSizes( | 146 uint64_t GetTotalSizeAndFileSizes( |
145 const std::vector<scoped_refptr<ShareableBlobDataItem>>& | 147 const std::vector<scoped_refptr<ShareableBlobDataItem>>& |
146 unreserved_file_items, | 148 unreserved_file_items, |
147 std::vector<uint64_t>* file_sizes_output) { | 149 std::vector<uint64_t>* file_sizes_output) { |
148 uint64_t total_size_output = 0; | 150 uint64_t total_size_output = 0; |
149 base::SmallMap<std::map<uint64_t, uint64_t>> file_id_to_sizes; | 151 base::SmallMap<std::map<uint64_t, uint64_t>> file_id_to_sizes; |
150 for (const auto& item : unreserved_file_items) { | 152 for (const auto& item : unreserved_file_items) { |
151 const DataElement& element = item->item()->data_element(); | 153 const DataElement& element = item->item()->data_element(); |
152 uint64_t file_id = BlobDataBuilder::GetFutureFileID(element); | 154 uint64_t file_id = BlobDataBuilder::GetFutureFileID(element); |
153 auto it = file_id_to_sizes.find(file_id); | 155 auto it = file_id_to_sizes.find(file_id); |
154 if (it != file_id_to_sizes.end()) | 156 if (it != file_id_to_sizes.end()) |
155 it->second = std::max(it->second, element.offset() + element.length()); | 157 it->second = std::max(it->second, element.offset() + element.length()); |
156 else | 158 else |
157 file_id_to_sizes[file_id] = element.offset() + element.length(); | 159 file_id_to_sizes[file_id] = element.offset() + element.length(); |
158 total_size_output += element.length(); | 160 total_size_output += element.length(); |
159 } | 161 } |
160 for (const auto& size_pair : file_id_to_sizes) { | 162 for (const auto& size_pair : file_id_to_sizes) { |
161 file_sizes_output->push_back(size_pair.second); | 163 file_sizes_output->push_back(size_pair.second); |
162 } | 164 } |
163 DCHECK_EQ(std::accumulate(file_sizes_output->begin(), | |
164 file_sizes_output->end(), 0ull), | |
165 total_size_output) | |
166 << "Illegal builder configuration, temporary files must be totally used."; | |
167 return total_size_output; | 165 return total_size_output; |
168 } | 166 } |
169 | 167 |
170 } // namespace | 168 } // namespace |
171 | 169 |
172 FileCreationInfo::FileCreationInfo() {} | 170 FileCreationInfo::FileCreationInfo() {} |
173 FileCreationInfo::~FileCreationInfo() { | 171 FileCreationInfo::~FileCreationInfo() { |
174 if (file.IsValid()) { | 172 if (file.IsValid()) { |
175 DCHECK(file_deletion_runner); | 173 DCHECK(file_deletion_runner); |
176 file_deletion_runner->PostTask( | 174 file_deletion_runner->PostTask( |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
293 base::PostTaskAndReplyWithResult( | 291 base::PostTaskAndReplyWithResult( |
294 controller_->file_runner_.get(), FROM_HERE, | 292 controller_->file_runner_.get(), FROM_HERE, |
295 base::Bind(&CreateEmptyFiles, controller_->blob_storage_dir_, | 293 base::Bind(&CreateEmptyFiles, controller_->blob_storage_dir_, |
296 controller_->file_runner_, base::Passed(&file_paths)), | 294 controller_->file_runner_, base::Passed(&file_paths)), |
297 base::Bind(&FileQuotaAllocationTask::OnCreateEmptyFiles, | 295 base::Bind(&FileQuotaAllocationTask::OnCreateEmptyFiles, |
298 weak_factory_.GetWeakPtr(), base::Passed(&references))); | 296 weak_factory_.GetWeakPtr(), base::Passed(&references))); |
299 controller_->RecordTracingCounters(); | 297 controller_->RecordTracingCounters(); |
300 } | 298 } |
301 ~FileQuotaAllocationTask() override {} | 299 ~FileQuotaAllocationTask() override {} |
302 | 300 |
303 void RunDoneCallback(std::vector<FileCreationInfo> file_info, bool success) { | 301 void RunDoneCallback(bool success, std::vector<FileCreationInfo> file_info) { |
304 // Make sure we clear the weak pointers we gave to the caller beforehand. | 302 // Make sure we clear the weak pointers we gave to the caller beforehand. |
305 weak_factory_.InvalidateWeakPtrs(); | 303 weak_factory_.InvalidateWeakPtrs(); |
306 | 304 |
307 // We want to destroy this object on the exit of this method if we were | 305 // We want to destroy this object on the exit of this method if we were |
308 // successful. | 306 // successful. |
309 std::unique_ptr<FileQuotaAllocationTask> this_object; | 307 std::unique_ptr<FileQuotaAllocationTask> this_object; |
310 if (success) { | 308 if (success) { |
311 for (auto& item : pending_items_) { | 309 for (auto& item : pending_items_) { |
312 item->set_state(ShareableBlobDataItem::QUOTA_GRANTED); | 310 item->set_state(ShareableBlobDataItem::QUOTA_GRANTED); |
313 } | 311 } |
314 this_object = std::move(*my_list_position_); | 312 this_object = std::move(*my_list_position_); |
315 controller_->pending_file_quota_tasks_.erase(my_list_position_); | 313 controller_->pending_file_quota_tasks_.erase(my_list_position_); |
316 } | 314 } |
317 | 315 |
318 done_callback_.Run(std::move(file_info), success); | 316 done_callback_.Run(success, std::move(file_info)); |
319 } | 317 } |
320 | 318 |
321 base::WeakPtr<QuotaAllocationTask> GetWeakPtr() { | 319 base::WeakPtr<QuotaAllocationTask> GetWeakPtr() { |
322 return weak_factory_.GetWeakPtr(); | 320 return weak_factory_.GetWeakPtr(); |
323 } | 321 } |
324 | 322 |
325 void Cancel() override { | 323 void Cancel() override { |
326 // This call destroys this object. We rely on ShareableFileReference's | 324 // This call destroys this object. We rely on ShareableFileReference's |
327 // final release callback for disk_usage_ accounting. | 325 // final release callback for disk_usage_ accounting. |
328 controller_->pending_file_quota_tasks_.erase(my_list_position_); | 326 controller_->pending_file_quota_tasks_.erase(my_list_position_); |
329 } | 327 } |
330 | 328 |
331 void OnCreateEmptyFiles( | 329 void OnCreateEmptyFiles( |
332 std::vector<scoped_refptr<ShareableFileReference>> references, | 330 std::vector<scoped_refptr<ShareableFileReference>> references, |
333 std::pair<std::vector<FileCreationInfo>, File::Error> files_and_error) { | 331 std::pair<std::vector<FileCreationInfo>, File::Error> files_and_error) { |
334 auto& files = files_and_error.first; | 332 auto& files = files_and_error.first; |
335 if (files.empty()) { | 333 if (files.empty()) { |
336 DCHECK_GE(controller_->disk_used_, allocation_size_); | 334 DCHECK_GE(controller_->disk_used_, allocation_size_); |
337 controller_->disk_used_ -= allocation_size_; | 335 controller_->disk_used_ -= allocation_size_; |
338 // This will call our callback and delete the object correctly. | 336 // This will call our callback and delete the object correctly. |
339 controller_->DisableFilePaging(files_and_error.second); | 337 controller_->DisableFilePaging(files_and_error.second); |
340 return; | 338 return; |
341 } | 339 } |
342 DCHECK_EQ(files.size(), references.size()); | 340 DCHECK_EQ(files.size(), references.size()); |
343 for (size_t i = 0; i < files.size(); i++) { | 341 for (size_t i = 0; i < files.size(); i++) { |
344 files[i].file_reference = std::move(references[i]); | 342 files[i].file_reference = std::move(references[i]); |
345 } | 343 } |
346 RunDoneCallback(std::move(files), true); | 344 RunDoneCallback(true, std::move(files)); |
347 } | 345 } |
348 | 346 |
349 // The my_list_position_ iterator is stored so that we can remove ourself | 347 // The my_list_position_ iterator is stored so that we can remove ourself |
350 // from the task list when we are cancelled. | 348 // from the task list when we are cancelled. |
351 void set_my_list_position( | 349 void set_my_list_position( |
352 PendingFileQuotaTaskList::iterator my_list_position) { | 350 PendingFileQuotaTaskList::iterator my_list_position) { |
353 my_list_position_ = my_list_position; | 351 my_list_position_ = my_list_position; |
354 } | 352 } |
355 | 353 |
356 private: | 354 private: |
(...skipping 17 matching lines...) Expand all Loading... |
374 file_runner_(std::move(file_runner)), | 372 file_runner_(std::move(file_runner)), |
375 populated_memory_items_( | 373 populated_memory_items_( |
376 base::MRUCache<uint64_t, ShareableBlobDataItem*>::NO_AUTO_EVICT), | 374 base::MRUCache<uint64_t, ShareableBlobDataItem*>::NO_AUTO_EVICT), |
377 weak_factory_(this) {} | 375 weak_factory_(this) {} |
378 | 376 |
379 BlobMemoryController::~BlobMemoryController() {} | 377 BlobMemoryController::~BlobMemoryController() {} |
380 | 378 |
381 void BlobMemoryController::DisableFilePaging(base::File::Error reason) { | 379 void BlobMemoryController::DisableFilePaging(base::File::Error reason) { |
382 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.PagingDisabled", -reason, | 380 UMA_HISTOGRAM_ENUMERATION("Storage.Blob.PagingDisabled", -reason, |
383 -File::FILE_ERROR_MAX); | 381 -File::FILE_ERROR_MAX); |
384 DLOG(ERROR) << "Blob storage paging disabled, reason: " << reason; | |
385 file_paging_enabled_ = false; | 382 file_paging_enabled_ = false; |
386 in_flight_memory_used_ = 0; | 383 in_flight_memory_used_ = 0; |
387 items_paging_to_file_.clear(); | 384 items_paging_to_file_.clear(); |
388 pending_evictions_ = 0; | 385 pending_evictions_ = 0; |
389 pending_memory_quota_total_size_ = 0; | 386 pending_memory_quota_total_size_ = 0; |
390 populated_memory_items_.Clear(); | 387 populated_memory_items_.Clear(); |
391 populated_memory_items_bytes_ = 0; | 388 populated_memory_items_bytes_ = 0; |
392 file_runner_ = nullptr; | 389 file_runner_ = nullptr; |
393 | 390 |
394 PendingMemoryQuotaTaskList old_memory_tasks; | 391 PendingMemoryQuotaTaskList old_memory_tasks; |
395 PendingFileQuotaTaskList old_file_tasks; | 392 PendingFileQuotaTaskList old_file_tasks; |
396 std::swap(old_memory_tasks, pending_memory_quota_tasks_); | 393 std::swap(old_memory_tasks, pending_memory_quota_tasks_); |
397 std::swap(old_file_tasks, pending_file_quota_tasks_); | 394 std::swap(old_file_tasks, pending_file_quota_tasks_); |
398 | 395 |
399 // Don't call the callbacks until we have a consistent state. | 396 // Don't call the callbacks until we have a consistent state. |
400 for (auto& memory_request : old_memory_tasks) { | 397 for (auto& memory_request : old_memory_tasks) { |
401 memory_request->RunDoneCallback(false); | 398 memory_request->RunDoneCallback(false); |
402 } | 399 } |
403 for (auto& file_request : old_file_tasks) { | 400 for (auto& file_request : old_file_tasks) { |
404 file_request->RunDoneCallback(std::vector<FileCreationInfo>(), false); | 401 file_request->RunDoneCallback(false, std::vector<FileCreationInfo>()); |
405 } | 402 } |
406 } | 403 } |
407 | 404 |
408 BlobMemoryController::Strategy BlobMemoryController::DetermineStrategy( | 405 BlobMemoryController::Strategy BlobMemoryController::DetermineStrategy( |
409 size_t preemptive_transported_bytes, | 406 size_t preemptive_transported_bytes, |
410 uint64_t total_transportation_bytes) const { | 407 uint64_t total_transportation_bytes) const { |
411 if (total_transportation_bytes == 0) | 408 if (total_transportation_bytes == 0) |
412 return Strategy::NONE_NEEDED; | 409 return Strategy::NONE_NEEDED; |
413 if (!CanReserveQuota(total_transportation_bytes)) | 410 if (!CanReserveQuota(total_transportation_bytes)) |
414 return Strategy::TOO_LARGE; | 411 return Strategy::TOO_LARGE; |
(...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
741 MaybeGrantPendingMemoryRequests(); | 738 MaybeGrantPendingMemoryRequests(); |
742 } | 739 } |
743 | 740 |
744 void BlobMemoryController::OnBlobFileDelete(uint64_t size, | 741 void BlobMemoryController::OnBlobFileDelete(uint64_t size, |
745 const FilePath& path) { | 742 const FilePath& path) { |
746 DCHECK_LE(size, disk_used_); | 743 DCHECK_LE(size, disk_used_); |
747 disk_used_ -= size; | 744 disk_used_ -= size; |
748 } | 745 } |
749 | 746 |
750 } // namespace storage | 747 } // namespace storage |
OLD | NEW |