Added unit test for DevTools' ephemeral port support.
[chromium-blink-merge.git] / base / files / important_file_writer.cc
blobde42c0ec0034fb6d6a4974f73314aaea7bd7fd41
1 // Copyright (c) 2011 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.
5 #if defined _MSC_VER && _MSC_VER == 1800
6 // TODO(scottmg): Internal errors on VS2013 RC in LTCG. This should be removed
7 // after RTM. http://crbug.com/288948
8 #pragma optimize("", off)
9 #endif
11 #include "base/files/important_file_writer.h"
13 #include <stdio.h>
15 #include <string>
17 #include "base/bind.h"
18 #include "base/critical_closure.h"
19 #include "base/file_util.h"
20 #include "base/files/file.h"
21 #include "base/files/file_path.h"
22 #include "base/logging.h"
23 #include "base/metrics/histogram.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/task_runner.h"
26 #include "base/task_runner_util.h"
27 #include "base/threading/thread.h"
28 #include "base/time/time.h"
30 namespace base {
32 namespace {
34 const int kDefaultCommitIntervalMs = 10000;
36 enum TempFileFailure {
37 FAILED_CREATING,
38 FAILED_OPENING,
39 FAILED_CLOSING,
40 FAILED_WRITING,
41 FAILED_RENAMING,
42 TEMP_FILE_FAILURE_MAX
45 void LogFailure(const FilePath& path, TempFileFailure failure_code,
46 const std::string& message) {
47 UMA_HISTOGRAM_ENUMERATION("ImportantFile.TempFileFailures", failure_code,
48 TEMP_FILE_FAILURE_MAX);
49 DPLOG(WARNING) << "temp file failure: " << path.value().c_str()
50 << " : " << message;
53 } // namespace
55 // static
56 bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
57 const std::string& data) {
58 // Write the data to a temp file then rename to avoid data loss if we crash
59 // while writing the file. Ensure that the temp file is on the same volume
60 // as target file, so it can be moved in one step, and that the temp file
61 // is securely created.
62 FilePath tmp_file_path;
63 if (!base::CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
64 LogFailure(path, FAILED_CREATING, "could not create temporary file");
65 return false;
68 File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE);
69 if (!tmp_file.IsValid()) {
70 LogFailure(path, FAILED_OPENING, "could not open temporary file");
71 return false;
74 // If this happens in the wild something really bad is going on.
75 CHECK_LE(data.length(), static_cast<size_t>(kint32max));
76 int bytes_written = tmp_file.Write(0, data.data(),
77 static_cast<int>(data.length()));
78 tmp_file.Flush(); // Ignore return value.
79 tmp_file.Close();
81 if (bytes_written < static_cast<int>(data.length())) {
82 LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" +
83 IntToString(bytes_written));
84 base::DeleteFile(tmp_file_path, false);
85 return false;
88 if (!base::ReplaceFile(tmp_file_path, path, NULL)) {
89 LogFailure(path, FAILED_RENAMING, "could not rename temporary file");
90 base::DeleteFile(tmp_file_path, false);
91 return false;
94 return true;
97 ImportantFileWriter::ImportantFileWriter(const FilePath& path,
98 base::SequencedTaskRunner* task_runner)
99 : path_(path),
100 task_runner_(task_runner),
101 serializer_(NULL),
102 commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)),
103 weak_factory_(this) {
104 DCHECK(CalledOnValidThread());
105 DCHECK(task_runner_.get());
108 ImportantFileWriter::~ImportantFileWriter() {
109 // We're usually a member variable of some other object, which also tends
110 // to be our serializer. It may not be safe to call back to the parent object
111 // being destructed.
112 DCHECK(!HasPendingWrite());
115 bool ImportantFileWriter::HasPendingWrite() const {
116 DCHECK(CalledOnValidThread());
117 return timer_.IsRunning();
120 void ImportantFileWriter::WriteNow(const std::string& data) {
121 DCHECK(CalledOnValidThread());
122 if (data.length() > static_cast<size_t>(kint32max)) {
123 NOTREACHED();
124 return;
127 if (HasPendingWrite())
128 timer_.Stop();
130 if (!PostWriteTask(data)) {
131 // Posting the task to background message loop is not expected
132 // to fail, but if it does, avoid losing data and just hit the disk
133 // on the current thread.
134 NOTREACHED();
136 WriteFileAtomically(path_, data);
140 void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
141 DCHECK(CalledOnValidThread());
143 DCHECK(serializer);
144 serializer_ = serializer;
146 if (!timer_.IsRunning()) {
147 timer_.Start(FROM_HERE, commit_interval_, this,
148 &ImportantFileWriter::DoScheduledWrite);
152 void ImportantFileWriter::DoScheduledWrite() {
153 DCHECK(serializer_);
154 std::string data;
155 if (serializer_->SerializeData(&data)) {
156 WriteNow(data);
157 } else {
158 DLOG(WARNING) << "failed to serialize data to be saved in "
159 << path_.value().c_str();
161 serializer_ = NULL;
164 void ImportantFileWriter::RegisterOnNextSuccessfulWriteCallback(
165 const base::Closure& on_next_successful_write) {
166 DCHECK(on_next_successful_write_.is_null());
167 on_next_successful_write_ = on_next_successful_write;
170 bool ImportantFileWriter::PostWriteTask(const std::string& data) {
171 // TODO(gab): This code could always use PostTaskAndReplyWithResult and let
172 // ForwardSuccessfulWrite() no-op if |on_next_successful_write_| is null, but
173 // PostTaskAndReply causes memory leaks in tests (crbug.com/371974) and
174 // suppressing all of those is unrealistic hence we avoid most of them by
175 // using PostTask() in the typical scenario below.
176 if (!on_next_successful_write_.is_null()) {
177 return base::PostTaskAndReplyWithResult(
178 task_runner_,
179 FROM_HERE,
180 MakeCriticalClosure(
181 Bind(&ImportantFileWriter::WriteFileAtomically, path_, data)),
182 Bind(&ImportantFileWriter::ForwardSuccessfulWrite,
183 weak_factory_.GetWeakPtr()));
185 return task_runner_->PostTask(
186 FROM_HERE,
187 MakeCriticalClosure(
188 Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically),
189 path_, data)));
192 void ImportantFileWriter::ForwardSuccessfulWrite(bool result) {
193 DCHECK(CalledOnValidThread());
194 if (result && !on_next_successful_write_.is_null()) {
195 on_next_successful_write_.Run();
196 on_next_successful_write_.Reset();
200 } // namespace base