blob: 60022c671d3a3f7340748dab3198d589fde1034d [file] [log] [blame]
// 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 <atomic>
#include <vector>
#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/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.
//
// The deletion scan takes place in a background task.
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
// halt processing and return.
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>;
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<FilePath> 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<SequencedTaskRunner> 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<FilePath> 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<FilePath> 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_