// 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. #ifndef BASE_FILES_IMPORTANT_FILE_WRITER_CLEANER_H_ #define BASE_FILES_IMPORTANT_FILE_WRITER_CLEANER_H_ #include #include #include "base/base_export.h" #include "base/containers/flat_set.h" #include "base/files/file_path.h" #include "base/memory/scoped_refptr.h" #include "base/no_destructor.h" #include "base/numerics/clamped_math.h" #include "base/optional.h" #include "base/sequence_checker.h" #include "base/synchronization/lock.h" #include "base/thread_annotations.h" #include "base/time/time.h" namespace base { class SequencedTaskRunner; // A cleaner for forgotten .tmp files left behind by ImportantFileWriter; see // https://crbug.com/1075917. // // ImportantFileWriter has the potential to leak .tmp files in case of a crash // or power failure during processing, or in case of interference by third-party // software. This class implements a singleton that makes a single scan over // given directories to delete any *.tmp files older than the current process. // Processes that use ImportantFileWriter are expected to call the instance's // Start method at some point during startup to enable the cleaner. // ImportantFileWriter calls the AddDirectory method to provide the directory // hosting an "important" file. Hosting processes are expected to call the Stop // method at shutdown so that metrics of an in-process execution can be // recorded. // // The deletion scan takes place in a background task. Metrics are recorded when // a directory is fully processed, or shortly after the hosting process calls // the Stop method. class BASE_EXPORT ImportantFileWriterCleaner { public: // Gets the process-wide single instance of the cleaner. static ImportantFileWriterCleaner& GetInstance(); ImportantFileWriterCleaner(const ImportantFileWriterCleaner&) = delete; ImportantFileWriterCleaner& operator=(const ImportantFileWriterCleaner&) = delete; ~ImportantFileWriterCleaner() = delete; // Adds |directory| to the set to be cleaned if it has not already been // handled. If the Start method has already been called, the cleaner will // begin processing |directory| after all others that have previously been // added have been cleaned (immediately, if there are no others). Any calls to // this method prior to Initialize are ignored. static void AddDirectory(const FilePath& directory); // Initializes the instance on the hosting process's main sequence (the one on // which Start and Stop will ultimately be called). It is safe to call this // any number of times from the main sequence. void Initialize(); // Starts the instance. If any directories have already been added, the // background task is posted immediately to begin processing them. Otherwise, // the next call to AddDirectory will begin processing. void Start(); // Stops the instance. The background task, if it is active, is notified to // record metrics on the directory in progress and exit. void Stop(); // Brings the instance back to the uninitialized state. This should be used in // tests that call Initialize so that the instance forgets about the test's // main thread task runner. void UninitializeForTesting(); // Returns the upper-bound time. Files with modification times older than this // are assumed to have been orphaned by a previous instance of the process. base::Time GetUpperBoundTimeForTest() const; private: friend class NoDestructor; ImportantFileWriterCleaner(); // True once Start() has been called; false following Stop(); bool is_started() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return started_; } // True once the background task has been posted; false once it returns. bool is_running() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return running_; } // The workhorse for AddDirectory. void AddDirectoryImpl(const FilePath& directory); // Schedules the background task to run, processing all directories that have // accumulated. void ScheduleTask(); // Iterates over the contents of |directories|, deleting all *.tmp files older // than |upper_bound_time|. Checks |stop_flag| after each deletion to see if // the instance has been stopped by the host process. Returns false if // processing was interrupted by |stop_flag| having been set, or true // indicating that all directories were fully processed. static bool CleanInBackground(Time upper_bound_time, std::vector directories, std::atomic_bool& stop_flag); // Cleans up after completion of the background task. |processing_completed| // is true when all directories were fully processed, or false if the task // potentially exited early in response to Stop(). void OnBackgroundTaskFinished(bool processing_completed); // Finalizes a request to stop after the background task returns. void DoStop(); // Provides exclusive access to the instance's task runner. Lock task_runner_lock_; // The hosting process's main thread task runner. scoped_refptr task_runner_ GUARDED_BY(task_runner_lock_); // The time before which any discovered temporary file is presumed to be // unused, and therefore safe to delete. const Time upper_bound_time_; // The set of all directories hosting files written by an ImportantFileWriter. flat_set important_directories_ GUARDED_BY_CONTEXT(sequence_checker_); // Directories added to the instance waiting either for a call to Start() or // waiting for an existing background task to complete. std::vector pending_directories_ GUARDED_BY_CONTEXT(sequence_checker_); std::atomic_bool stop_flag_{false}; bool started_ GUARDED_BY_CONTEXT(sequence_checker_) = false; bool running_ GUARDED_BY_CONTEXT(sequence_checker_) = false; SEQUENCE_CHECKER(sequence_checker_); }; } // namespace base #endif // BASE_FILES_IMPORTANT_FILE_WRITER_CLEANER_H_