1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_FILES_IMPORTANT_FILE_WRITER_H_ 6 #define BASE_FILES_IMPORTANT_FILE_WRITER_H_ 7 8 #include <string> 9 10 #include "base/base_export.h" 11 #include "base/callback.h" 12 #include "base/files/file_path.h" 13 #include "base/macros.h" 14 #include "base/memory/ref_counted.h" 15 #include "base/strings/string_piece.h" 16 #include "base/threading/non_thread_safe.h" 17 #include "base/time/time.h" 18 #include "base/timer/timer.h" 19 20 namespace base { 21 22 class SequencedTaskRunner; 23 class Thread; 24 25 // Helper to ensure that a file won't be corrupted by the write (for example on 26 // application crash). Consider a naive way to save an important file F: 27 // 28 // 1. Open F for writing, truncating it. 29 // 2. Write new data to F. 30 // 31 // It's good when it works, but it gets very bad if step 2. doesn't complete. 32 // It can be caused by a crash, a computer hang, or a weird I/O error. And you 33 // end up with a broken file. 34 // 35 // To be safe, we don't start with writing directly to F. Instead, we write to 36 // to a temporary file. Only after that write is successful, we rename the 37 // temporary file to target filename. 38 // 39 // If you want to know more about this approach and ext3/ext4 fsync issues, see 40 // http://blog.valerieaurora.org/2009/04/16/dont-panic-fsync-ext34-and-your-data/ 41 class BASE_EXPORT ImportantFileWriter : public NonThreadSafe { 42 public: 43 // Used by ScheduleSave to lazily provide the data to be saved. Allows us 44 // to also batch data serializations. 45 class BASE_EXPORT DataSerializer { 46 public: 47 // Should put serialized string in |data| and return true on successful 48 // serialization. Will be called on the same thread on which 49 // ImportantFileWriter has been created. 50 virtual bool SerializeData(std::string* data) = 0; 51 52 protected: ~DataSerializer()53 virtual ~DataSerializer() {} 54 }; 55 56 // Save |data| to |path| in an atomic manner (see the class comment above). 57 // Blocks and writes data on the current thread. 58 static bool WriteFileAtomically(const FilePath& path, StringPiece data); 59 60 // Initialize the writer. 61 // |path| is the name of file to write. 62 // |task_runner| is the SequencedTaskRunner instance where on which we will 63 // execute file I/O operations. 64 // All non-const methods, ctor and dtor must be called on the same thread. 65 ImportantFileWriter(const FilePath& path, 66 scoped_refptr<SequencedTaskRunner> task_runner); 67 68 // Same as above, but with a custom commit interval. 69 ImportantFileWriter(const FilePath& path, 70 scoped_refptr<SequencedTaskRunner> task_runner, 71 TimeDelta interval); 72 73 // You have to ensure that there are no pending writes at the moment 74 // of destruction. 75 ~ImportantFileWriter(); 76 path()77 const FilePath& path() const { return path_; } 78 79 // Returns true if there is a scheduled write pending which has not yet 80 // been started. 81 bool HasPendingWrite() const; 82 83 // Save |data| to target filename. Does not block. If there is a pending write 84 // scheduled by ScheduleWrite(), it is cancelled. 85 void WriteNow(std::unique_ptr<std::string> data); 86 87 // Schedule a save to target filename. Data will be serialized and saved 88 // to disk after the commit interval. If another ScheduleWrite is issued 89 // before that, only one serialization and write to disk will happen, and 90 // the most recent |serializer| will be used. This operation does not block. 91 // |serializer| should remain valid through the lifetime of 92 // ImportantFileWriter. 93 void ScheduleWrite(DataSerializer* serializer); 94 95 // Serialize data pending to be saved and execute write on backend thread. 96 void DoScheduledWrite(); 97 98 // Registers |on_next_successful_write| to be called once, on the next 99 // successful write event. Only one callback can be set at once. 100 void RegisterOnNextSuccessfulWriteCallback( 101 const Closure& on_next_successful_write); 102 commit_interval()103 TimeDelta commit_interval() const { 104 return commit_interval_; 105 } 106 107 private: 108 // Helper method for WriteNow(). 109 bool PostWriteTask(const Callback<bool()>& task); 110 111 // If |result| is true and |on_next_successful_write_| is set, invokes 112 // |on_successful_write_| and then resets it; no-ops otherwise. 113 void ForwardSuccessfulWrite(bool result); 114 115 // Invoked once and then reset on the next successful write event. 116 Closure on_next_successful_write_; 117 118 // Path being written to. 119 const FilePath path_; 120 121 // TaskRunner for the thread on which file I/O can be done. 122 const scoped_refptr<SequencedTaskRunner> task_runner_; 123 124 // Timer used to schedule commit after ScheduleWrite. 125 OneShotTimer timer_; 126 127 // Serializer which will provide the data to be saved. 128 DataSerializer* serializer_; 129 130 // Time delta after which scheduled data will be written to disk. 131 const TimeDelta commit_interval_; 132 133 WeakPtrFactory<ImportantFileWriter> weak_factory_; 134 135 DISALLOW_COPY_AND_ASSIGN(ImportantFileWriter); 136 }; 137 138 } // namespace base 139 140 #endif // BASE_FILES_IMPORTANT_FILE_WRITER_H_ 141