• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium Authors
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 "base/files/important_file_writer_cleaner.h"
6 
7 #include <algorithm>
8 #include <functional>
9 #include <iterator>
10 #include <utility>
11 
12 #include "base/files/file_enumerator.h"
13 #include "base/files/file_util.h"
14 #include "base/functional/bind.h"
15 #include "base/no_destructor.h"
16 #include "base/process/process.h"
17 #include "base/task/sequenced_task_runner.h"
18 #include "base/task/task_traits.h"
19 #include "base/task/thread_pool.h"
20 #include "base/time/time.h"
21 #include "build/build_config.h"
22 
23 namespace base {
24 
25 namespace {
26 
GetUpperBoundTime()27 base::Time GetUpperBoundTime() {
28 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) || BUILDFLAG(IS_FUCHSIA)
29   // If process creation time is not available then use instance creation
30   // time as the upper-bound for old files. Modification times may be
31   // rounded-down to coarse-grained increments, e.g. FAT has 2s granularity,
32   // so it is necessary to set the upper-bound earlier than Now() by at least
33   // that margin to account for modification times being rounded-down.
34   return Time::Now() - Seconds(2);
35 #else
36   return Process::Current().CreationTime() - Seconds(2);
37 #endif
38 }
39 
40 }  // namespace
41 
42 // static
GetInstance()43 ImportantFileWriterCleaner& ImportantFileWriterCleaner::GetInstance() {
44   static NoDestructor<ImportantFileWriterCleaner> instance;
45   return *instance;
46 }
47 
48 // static
AddDirectory(const FilePath & directory)49 void ImportantFileWriterCleaner::AddDirectory(const FilePath& directory) {
50   auto& instance = GetInstance();
51   scoped_refptr<SequencedTaskRunner> task_runner;
52   {
53     AutoLock scoped_lock(instance.task_runner_lock_);
54     task_runner = instance.task_runner_;
55   }
56   if (!task_runner)
57     return;
58   if (task_runner->RunsTasksInCurrentSequence()) {
59     instance.AddDirectoryImpl(directory);
60   } else {
61     // Unretained is safe here since the cleaner instance is never destroyed.
62     task_runner->PostTask(
63         FROM_HERE, BindOnce(&ImportantFileWriterCleaner::AddDirectoryImpl,
64                             Unretained(&instance), directory));
65   }
66 }
67 
Initialize()68 void ImportantFileWriterCleaner::Initialize() {
69   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
70   AutoLock scoped_lock(task_runner_lock_);
71   DCHECK(!task_runner_ ||
72          task_runner_ == SequencedTaskRunner::GetCurrentDefault());
73   task_runner_ = SequencedTaskRunner::GetCurrentDefault();
74 }
75 
Start()76 void ImportantFileWriterCleaner::Start() {
77 #if DCHECK_IS_ON()
78   {
79     AutoLock scoped_lock(task_runner_lock_);
80     DCHECK(task_runner_);
81   }
82 #endif
83   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
84 
85   if (is_started())
86     return;
87 
88   started_ = true;
89 
90   if (!pending_directories_.empty())
91     ScheduleTask();
92 }
93 
Stop()94 void ImportantFileWriterCleaner::Stop() {
95   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
96 
97   if (!is_started())
98     return;
99 
100   if (is_running())
101     stop_flag_.store(true, std::memory_order_relaxed);
102   else
103     DoStop();
104 }
105 
UninitializeForTesting()106 void ImportantFileWriterCleaner::UninitializeForTesting() {
107   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
108   DCHECK(!is_started());
109   {
110     AutoLock scoped_lock(task_runner_lock_);
111     task_runner_ = nullptr;
112   }
113   // AddDirectory may have been called after Stop. Clear the containers just in
114   // case.
115   important_directories_.clear();
116   pending_directories_.clear();
117   DETACH_FROM_SEQUENCE(sequence_checker_);
118 }
119 
GetUpperBoundTimeForTest() const120 base::Time ImportantFileWriterCleaner::GetUpperBoundTimeForTest() const {
121   return upper_bound_time_;
122 }
123 
ImportantFileWriterCleaner()124 ImportantFileWriterCleaner::ImportantFileWriterCleaner()
125     : upper_bound_time_(GetUpperBoundTime()) {
126   DETACH_FROM_SEQUENCE(sequence_checker_);
127 }
128 
AddDirectoryImpl(const FilePath & directory)129 void ImportantFileWriterCleaner::AddDirectoryImpl(const FilePath& directory) {
130   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
131 
132   if (!important_directories_.insert(directory).second)
133     return;  // This directory has already been seen.
134 
135   pending_directories_.push_back(directory);
136 
137   if (!is_started())
138     return;  // Nothing more to do if Start() has not been called.
139 
140   // Start the background task if it's not already running. If it is running, a
141   // new task will be posted on completion of the current one by
142   // OnBackgroundTaskFinished to handle all directories added while it was
143   // running.
144   if (!is_running())
145     ScheduleTask();
146 }
147 
ScheduleTask()148 void ImportantFileWriterCleaner::ScheduleTask() {
149   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
150   DCHECK(is_started());
151   DCHECK(!is_running());
152   DCHECK(!pending_directories_.empty());
153   DCHECK(!stop_flag_.load(std::memory_order_relaxed));
154 
155   // Pass the set of directories to be processed.
156   running_ = ThreadPool::PostTaskAndReplyWithResult(
157       FROM_HERE,
158       {TaskPriority::BEST_EFFORT, TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
159        MayBlock()},
160       BindOnce(&ImportantFileWriterCleaner::CleanInBackground,
161                upper_bound_time_, std::move(pending_directories_),
162                std::ref(stop_flag_)),
163       // Unretained is safe here since the cleaner instance is never destroyed.
164       BindOnce(&ImportantFileWriterCleaner::OnBackgroundTaskFinished,
165                Unretained(this)));
166 }
167 
168 // static
CleanInBackground(Time upper_bound_time,std::vector<FilePath> directories,std::atomic_bool & stop_flag)169 bool ImportantFileWriterCleaner::CleanInBackground(
170     Time upper_bound_time,
171     std::vector<FilePath> directories,
172     std::atomic_bool& stop_flag) {
173   DCHECK(!directories.empty());
174   for (auto scan = directories.begin(), end = directories.end(); scan != end;
175        ++scan) {
176     const auto& directory = *scan;
177     FileEnumerator file_enum(
178         directory, /*recursive=*/false, FileEnumerator::FILES,
179         FormatTemporaryFileName(FILE_PATH_LITERAL("*")).value());
180     for (FilePath path = file_enum.Next(); !path.empty();
181          path = file_enum.Next()) {
182       const FileEnumerator::FileInfo info = file_enum.GetInfo();
183       if (info.GetLastModifiedTime() >= upper_bound_time)
184         continue;
185       // Cleanup is a best-effort process, so ignore any failures here and
186       // continue to clean as much as possible. Metrics tell us that ~98.4% of
187       // directories are cleaned with no failures.
188       DeleteFile(path);
189       // Break out without checking for the next file if a stop is requested.
190       if (stop_flag.load(std::memory_order_relaxed))
191         return false;
192     }
193   }
194   return true;
195 }
196 
OnBackgroundTaskFinished(bool processing_completed)197 void ImportantFileWriterCleaner::OnBackgroundTaskFinished(
198     bool processing_completed) {
199   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
200 
201   running_ = false;
202 
203   // There are no other accessors of |stop_flag_| at this point, so atomic
204   // operations aren't needed. There is no way to read it without such, so use
205   // the same (relaxed) ordering as elsewhere.
206   const bool stop = stop_flag_.exchange(false, std::memory_order_relaxed);
207   DCHECK(stop || processing_completed);
208 
209   if (stop) {
210     DoStop();
211   } else if (!pending_directories_.empty()) {
212     // Run the task again with the new directories.
213     ScheduleTask();
214   }  // else do nothing until a new directory is added.
215 }
216 
DoStop()217 void ImportantFileWriterCleaner::DoStop() {
218   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
219   DCHECK(is_started());
220   DCHECK(!is_running());
221 
222   important_directories_.clear();
223   pending_directories_.clear();
224   started_ = false;
225 }
226 
227 }  // namespace base
228