#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)
return Time::Now() - Seconds(2);
#else
return Process::Current().CreationTime() - Seconds(2);
#endif
}
}
ImportantFileWriterCleaner& ImportantFileWriterCleaner::GetInstance() {
static NoDestructor<ImportantFileWriterCleaner> instance;
return *instance;
}
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 {
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;
}
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;
}
pending_directories_.push_back(directory);
if (!is_started()) {
return;
}
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));
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_)),
BindOnce(&ImportantFileWriterCleaner::OnBackgroundTaskFinished,
Unretained(this)));
}
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, false, FileEnumerator::FILES,
FormatTemporaryFileName(FILE_PATH_LITERAL("*"), true).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;
}
DeleteFile(path);
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;
const bool stop = stop_flag_.exchange(false, std::memory_order_relaxed);
DCHECK(stop || processing_completed);
if (stop) {
DoStop();
} else if (!pending_directories_.empty()) {
ScheduleTask();
}
}
void ImportantFileWriterCleaner::DoStop() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(is_started());
DCHECK(!is_running());
important_directories_.clear();
pending_directories_.clear();
started_ = false;
}
}