diff options
author | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-20 18:20:29 +0000 |
---|---|---|
committer | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-20 18:20:29 +0000 |
commit | 6658ca866d0e28950179a65179c1a4d6e8e3f8df (patch) | |
tree | 5e7c4767096a10bc71aa0bd4bf2228f1476a564e /chrome/common/important_file_writer.cc | |
parent | e45b40f5bfe4f20aece5a8fa25d9a4c6c89f22e1 (diff) | |
download | chromium_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.cc | 150 |
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; +} |