summaryrefslogtreecommitdiffstats
path: root/chrome/common/important_file_writer.cc
diff options
context:
space:
mode:
authorsanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-20 18:20:29 +0000
committersanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-20 18:20:29 +0000
commit6658ca866d0e28950179a65179c1a4d6e8e3f8df (patch)
tree5e7c4767096a10bc71aa0bd4bf2228f1476a564e /chrome/common/important_file_writer.cc
parente45b40f5bfe4f20aece5a8fa25d9a4c6c89f22e1 (diff)
downloadchromium_src-6658ca866d0e28950179a65179c1a4d6e8e3f8df.zip
chromium_src-6658ca866d0e28950179a65179c1a4d6e8e3f8df.tar.gz
chromium_src-6658ca866d0e28950179a65179c1a4d6e8e3f8df.tar.bz2
Moved ImportantFileWriter to chrome/common in preparation of moving JsonPrefStore to chrome/common.
BUG=None. TEST=Unit-tests moved and modified. Review URL: http://codereview.chromium.org/2128014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47818 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/important_file_writer.cc')
-rw-r--r--chrome/common/important_file_writer.cc150
1 files changed, 150 insertions, 0 deletions
diff --git a/chrome/common/important_file_writer.cc b/chrome/common/important_file_writer.cc
new file mode 100644
index 0000000..3466958
--- /dev/null
+++ b/chrome/common/important_file_writer.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2010 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.
+
+#include "chrome/common/important_file_writer.h"
+
+#include <stdio.h>
+
+#include <ostream>
+#include <string>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/message_loop_proxy.h"
+#include "base/string_util.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "base/time.h"
+
+using base::TimeDelta;
+
+namespace {
+
+const int kDefaultCommitIntervalMs = 10000;
+
+class WriteToDiskTask : public Task {
+ public:
+ WriteToDiskTask(const FilePath& path, const std::string& data)
+ : path_(path),
+ data_(data) {
+ }
+
+ virtual void Run() {
+ // Write the data to a temp file then rename to avoid data loss if we crash
+ // while writing the file. Ensure that the temp file is on the same volume
+ // as target file, so it can be moved in one step, and that the temp file
+ // is securely created.
+ FilePath tmp_file_path;
+ FILE* tmp_file = file_util::CreateAndOpenTemporaryFileInDir(
+ path_.DirName(), &tmp_file_path);
+ if (!tmp_file) {
+ LogFailure("could not create temporary file");
+ return;
+ }
+
+ size_t bytes_written = fwrite(data_.data(), 1, data_.length(), tmp_file);
+ if (!file_util::CloseFile(tmp_file)) {
+ file_util::Delete(tmp_file_path, false);
+ LogFailure("failed to close temporary file");
+ return;
+ }
+ if (bytes_written < data_.length()) {
+ file_util::Delete(tmp_file_path, false);
+ LogFailure("error writing, bytes_written=" + UintToString(
+ static_cast<unsigned int>(bytes_written)));
+ return;
+ }
+
+ if (file_util::ReplaceFile(tmp_file_path, path_)) {
+ LogSuccess();
+ return;
+ }
+
+ file_util::Delete(tmp_file_path, false);
+ LogFailure("could not rename temporary file");
+ }
+
+ private:
+ void LogSuccess() {
+ LOG(INFO) << "successfully saved " << path_.value();
+ }
+
+ void LogFailure(const std::string& message) {
+ LOG(WARNING) << "failed to write " << path_.value()
+ << ": " << message;
+ }
+
+ const FilePath path_;
+ const std::string data_;
+
+ DISALLOW_COPY_AND_ASSIGN(WriteToDiskTask);
+};
+
+} // namespace
+
+ImportantFileWriter::ImportantFileWriter(
+ const FilePath& path, base::MessageLoopProxy* file_message_loop_proxy)
+ : path_(path),
+ file_message_loop_proxy_(file_message_loop_proxy),
+ serializer_(NULL),
+ commit_interval_(TimeDelta::FromMilliseconds(
+ kDefaultCommitIntervalMs)) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(file_message_loop_proxy_.get());
+}
+
+ImportantFileWriter::~ImportantFileWriter() {
+ // We're usually a member variable of some other object, which also tends
+ // to be our serializer. It may not be safe to call back to the parent object
+ // being destructed.
+ DCHECK(!HasPendingWrite());
+}
+
+bool ImportantFileWriter::HasPendingWrite() const {
+ DCHECK(CalledOnValidThread());
+ return timer_.IsRunning();
+}
+
+void ImportantFileWriter::WriteNow(const std::string& data) {
+ DCHECK(CalledOnValidThread());
+
+ if (HasPendingWrite())
+ timer_.Stop();
+
+ // TODO(sanjeevr): Add a DCHECK for the return value of PostTask.
+ // (Some tests fail if we add the DCHECK and they need to be fixed first).
+ file_message_loop_proxy_->PostTask(FROM_HERE,
+ new WriteToDiskTask(path_, data));
+}
+
+void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
+ DCHECK(CalledOnValidThread());
+
+ DCHECK(serializer);
+ serializer_ = serializer;
+
+ if (!MessageLoop::current()) {
+ // Happens in unit tests.
+ DoScheduledWrite();
+ return;
+ }
+
+ if (!timer_.IsRunning()) {
+ timer_.Start(commit_interval_, this,
+ &ImportantFileWriter::DoScheduledWrite);
+ }
+}
+
+void ImportantFileWriter::DoScheduledWrite() {
+ DCHECK(serializer_);
+ std::string data;
+ if (serializer_->SerializeData(&data)) {
+ WriteNow(data);
+ } else {
+ LOG(WARNING) << "failed to serialize data to be saved in "
+ << path_.value();
+ }
+ serializer_ = NULL;
+}