123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- // Copyright 2020 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/files/important_file_writer_cleaner.h"
- #include <algorithm>
- #include <functional>
- #include <iterator>
- #include <utility>
- #include "base/bind.h"
- #include "base/files/file_enumerator.h"
- #include "base/files/file_util.h"
- #include "base/no_destructor.h"
- #include "base/process/process.h"
- #include "base/task/task_traits.h"
- #include "base/task/thread_pool.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "base/time/time.h"
- #include "build/build_config.h"
- namespace base {
- namespace {
- base::Time GetUpperBoundTime() {
- #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) || BUILDFLAG(IS_FUCHSIA)
- // If process creation time is not available then use instance creation
- // time as the upper-bound for old files. Modification times may be
- // rounded-down to coarse-grained increments, e.g. FAT has 2s granularity,
- // so it is necessary to set the upper-bound earlier than Now() by at least
- // that margin to account for modification times being rounded-down.
- return Time::Now() - Seconds(2);
- #else
- return Process::Current().CreationTime() - Seconds(2);
- #endif
- }
- } // namespace
- // static
- ImportantFileWriterCleaner& ImportantFileWriterCleaner::GetInstance() {
- static NoDestructor<ImportantFileWriterCleaner> instance;
- return *instance;
- }
- // static
- void ImportantFileWriterCleaner::AddDirectory(const FilePath& directory) {
- auto& instance = GetInstance();
- scoped_refptr<SequencedTaskRunner> task_runner;
- {
- AutoLock scoped_lock(instance.task_runner_lock_);
- task_runner = instance.task_runner_;
- }
- if (!task_runner)
- return;
- if (task_runner->RunsTasksInCurrentSequence()) {
- instance.AddDirectoryImpl(directory);
- } else {
- // Unretained is safe here since the cleaner instance is never destroyed.
- task_runner->PostTask(
- FROM_HERE, BindOnce(&ImportantFileWriterCleaner::AddDirectoryImpl,
- Unretained(&instance), directory));
- }
- }
- void ImportantFileWriterCleaner::Initialize() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- AutoLock scoped_lock(task_runner_lock_);
- DCHECK(!task_runner_ || task_runner_ == SequencedTaskRunnerHandle::Get());
- task_runner_ = SequencedTaskRunnerHandle::Get();
- }
- void ImportantFileWriterCleaner::Start() {
- #if DCHECK_IS_ON()
- {
- AutoLock scoped_lock(task_runner_lock_);
- DCHECK(task_runner_);
- }
- #endif
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (is_started())
- return;
- started_ = true;
- if (!pending_directories_.empty())
- ScheduleTask();
- }
- void ImportantFileWriterCleaner::Stop() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!is_started())
- return;
- if (is_running())
- stop_flag_.store(true, std::memory_order_relaxed);
- else
- DoStop();
- }
- void ImportantFileWriterCleaner::UninitializeForTesting() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(!is_started());
- {
- AutoLock scoped_lock(task_runner_lock_);
- task_runner_ = nullptr;
- }
- // AddDirectory may have been called after Stop. Clear the containers just in
- // case.
- important_directories_.clear();
- pending_directories_.clear();
- DETACH_FROM_SEQUENCE(sequence_checker_);
- }
- base::Time ImportantFileWriterCleaner::GetUpperBoundTimeForTest() const {
- return upper_bound_time_;
- }
- ImportantFileWriterCleaner::ImportantFileWriterCleaner()
- : upper_bound_time_(GetUpperBoundTime()) {
- DETACH_FROM_SEQUENCE(sequence_checker_);
- }
- void ImportantFileWriterCleaner::AddDirectoryImpl(const FilePath& directory) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!important_directories_.insert(directory).second)
- return; // This directory has already been seen.
- pending_directories_.push_back(directory);
- if (!is_started())
- return; // Nothing more to do if Start() has not been called.
- // Start the background task if it's not already running. If it is running, a
- // new task will be posted on completion of the current one by
- // OnBackgroundTaskFinished to handle all directories added while it was
- // running.
- if (!is_running())
- ScheduleTask();
- }
- void ImportantFileWriterCleaner::ScheduleTask() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(is_started());
- DCHECK(!is_running());
- DCHECK(!pending_directories_.empty());
- DCHECK(!stop_flag_.load(std::memory_order_relaxed));
- // Pass the set of directories to be processed.
- running_ = ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE,
- {TaskPriority::BEST_EFFORT, TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
- MayBlock()},
- BindOnce(&ImportantFileWriterCleaner::CleanInBackground,
- upper_bound_time_, std::move(pending_directories_),
- std::ref(stop_flag_)),
- // Unretained is safe here since the cleaner instance is never destroyed.
- BindOnce(&ImportantFileWriterCleaner::OnBackgroundTaskFinished,
- Unretained(this)));
- }
- // static
- bool ImportantFileWriterCleaner::CleanInBackground(
- Time upper_bound_time,
- std::vector<FilePath> directories,
- std::atomic_bool& stop_flag) {
- DCHECK(!directories.empty());
- for (auto scan = directories.begin(), end = directories.end(); scan != end;
- ++scan) {
- const auto& directory = *scan;
- FileEnumerator file_enum(
- directory, /*recursive=*/false, FileEnumerator::FILES,
- FormatTemporaryFileName(FILE_PATH_LITERAL("*")).value());
- for (FilePath path = file_enum.Next(); !path.empty();
- path = file_enum.Next()) {
- const FileEnumerator::FileInfo info = file_enum.GetInfo();
- if (info.GetLastModifiedTime() >= upper_bound_time)
- continue;
- // Cleanup is a best-effort process, so ignore any failures here and
- // continue to clean as much as possible. Metrics tell us that ~98.4% of
- // directories are cleaned with no failures.
- DeleteFile(path);
- // Break out without checking for the next file if a stop is requested.
- if (stop_flag.load(std::memory_order_relaxed))
- return false;
- }
- }
- return true;
- }
- void ImportantFileWriterCleaner::OnBackgroundTaskFinished(
- bool processing_completed) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- running_ = false;
- // There are no other accessors of |stop_flag_| at this point, so atomic
- // operations aren't needed. There is no way to read it without such, so use
- // the same (relaxed) ordering as elsewhere.
- const bool stop = stop_flag_.exchange(false, std::memory_order_relaxed);
- DCHECK(stop || processing_completed);
- if (stop) {
- DoStop();
- } else if (!pending_directories_.empty()) {
- // Run the task again with the new directories.
- ScheduleTask();
- } // else do nothing until a new directory is added.
- }
- void ImportantFileWriterCleaner::DoStop() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(is_started());
- DCHECK(!is_running());
- important_directories_.clear();
- pending_directories_.clear();
- started_ = false;
- }
- } // namespace base
|