1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
|
// Copyright 2020 The Chromium Authors
// 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/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/no_destructor.h"
#include "base/process/process.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.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_ == SequencedTaskRunner::GetCurrentDefault());
task_runner_ = SequencedTaskRunner::GetCurrentDefault();
}
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& directory : directories) {
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
|