Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Side by Side Diff: content/browser/download/download_file_impl.cc

Issue 319603003: [Downloads] Retry renames after transient failures. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/download/download_file_impl.h" 5 #include "content/browser/download/download_file_impl.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/file_util.h" 10 #include "base/file_util.h"
11 #include "base/message_loop/message_loop_proxy.h" 11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/strings/stringprintf.h" 12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h" 13 #include "base/time/time.h"
14 #include "content/browser/byte_stream.h" 14 #include "content/browser/byte_stream.h"
15 #include "content/browser/download/download_create_info.h" 15 #include "content/browser/download/download_create_info.h"
16 #include "content/browser/download/download_interrupt_reasons_impl.h" 16 #include "content/browser/download/download_interrupt_reasons_impl.h"
17 #include "content/browser/download/download_net_log_parameters.h" 17 #include "content/browser/download/download_net_log_parameters.h"
18 #include "content/browser/download/download_stats.h" 18 #include "content/browser/download/download_stats.h"
19 #include "content/public/browser/browser_thread.h" 19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/download_destination_observer.h" 20 #include "content/public/browser/download_destination_observer.h"
21 #include "net/base/io_buffer.h" 21 #include "net/base/io_buffer.h"
22 22
23 namespace content { 23 namespace content {
24 24
25 const int kUpdatePeriodMs = 500; 25 const int kUpdatePeriodMs = 500;
26 const int kMaxTimeBlockingFileThreadMs = 1000; 26 const int kMaxTimeBlockingFileThreadMs = 1000;
27 27
28 // These constants control the default retry behavior for failing renames. Each
29 // retry is performed after a delay that is twice the previous delay. The
30 // initial delay is specified by kInitialRenameRetryDelayMs.
31 const int kMaxRenameRetries = 3;
32 const int kInitialRenameRetryDelayMs = 200;
33
28 int DownloadFile::number_active_objects_ = 0; 34 int DownloadFile::number_active_objects_ = 0;
29 35
30 DownloadFileImpl::DownloadFileImpl( 36 DownloadFileImpl::DownloadFileImpl(
31 scoped_ptr<DownloadSaveInfo> save_info, 37 scoped_ptr<DownloadSaveInfo> save_info,
32 const base::FilePath& default_download_directory, 38 const base::FilePath& default_download_directory,
33 const GURL& url, 39 const GURL& url,
34 const GURL& referrer_url, 40 const GURL& referrer_url,
35 bool calculate_hash, 41 bool calculate_hash,
36 scoped_ptr<ByteStreamReader> stream, 42 scoped_ptr<ByteStreamReader> stream,
37 const net::BoundNetLog& bound_net_log, 43 const net::BoundNetLog& bound_net_log,
38 base::WeakPtr<DownloadDestinationObserver> observer) 44 base::WeakPtr<DownloadDestinationObserver> observer)
39 : file_(save_info->file_path, 45 : file_(save_info->file_path,
40 url, 46 url,
41 referrer_url, 47 referrer_url,
42 save_info->offset, 48 save_info->offset,
43 calculate_hash, 49 calculate_hash,
44 save_info->hash_state, 50 save_info->hash_state,
45 save_info->file.Pass(), 51 save_info->file.Pass(),
46 bound_net_log), 52 bound_net_log),
47 default_download_directory_(default_download_directory), 53 default_download_directory_(default_download_directory),
48 stream_reader_(stream.Pass()), 54 stream_reader_(stream.Pass()),
49 bytes_seen_(0), 55 bytes_seen_(0),
50 bound_net_log_(bound_net_log), 56 bound_net_log_(bound_net_log),
51 observer_(observer), 57 observer_(observer),
52 weak_factory_(this) { 58 weak_factory_(this) {
53 } 59 }
54 60
55 DownloadFileImpl::~DownloadFileImpl() { 61 DownloadFileImpl::~DownloadFileImpl() {
56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
57 --number_active_objects_; 63 --number_active_objects_;
58 } 64 }
59 65
60 void DownloadFileImpl::Initialize(const InitializeCallback& callback) { 66 void DownloadFileImpl::Initialize(const InitializeCallback& callback) {
61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
62 68
(...skipping 30 matching lines...) Expand all
93 99
94 if (!update_timer_->IsRunning()) { 100 if (!update_timer_->IsRunning()) {
95 update_timer_->Start(FROM_HERE, 101 update_timer_->Start(FROM_HERE,
96 base::TimeDelta::FromMilliseconds(kUpdatePeriodMs), 102 base::TimeDelta::FromMilliseconds(kUpdatePeriodMs),
97 this, &DownloadFileImpl::SendUpdate); 103 this, &DownloadFileImpl::SendUpdate);
98 } 104 }
99 rate_estimator_.Increment(data_len); 105 rate_estimator_.Increment(data_len);
100 return file_.AppendDataToFile(data, data_len); 106 return file_.AppendDataToFile(data, data_len);
101 } 107 }
102 108
103 void DownloadFileImpl::RenameAndUniquify( 109 void DownloadFileImpl::RenameAndUniquify(
Randy Smith (Not in Mondays) 2014/06/11 18:07:29 [Not for this CL] Why are we doing a RenameAndU
asanka 2014/06/12 20:02:37 RenameAndUniquify is invoked for the intermediate
104 const base::FilePath& full_path, 110 const base::FilePath& full_path,
105 const RenameCompletionCallback& callback) { 111 const RenameCompletionCallback& callback) {
112 RenameWithRetryInternal(
113 full_path, UNIQUIFY, kMaxRenameRetries, base::TimeTicks(), callback);
114 }
115
116 void DownloadFileImpl::RenameAndAnnotate(
117 const base::FilePath& full_path,
118 const RenameCompletionCallback& callback) {
119 RenameWithRetryInternal(full_path,
120 ANNOTATE_WITH_SOURCE_INFORMATION,
121 kMaxRenameRetries,
122 base::TimeTicks(),
123 callback);
124 }
125
126 base::TimeDelta DownloadFileImpl::GetRetryDelayForFailedRename(
127 int attempt_number) {
128 DCHECK_GE(attempt_number, 0);
129 // |delay| starts at kInitialRenameRetryDelayMs and increases by a factor of
130 // 2 at each subsequent retry. Assumes that |retries_left| starts at
131 // kMaxRenameRetries. Also assumes that kMaxRenameRetries is less than the
132 // number of bits in an int.
133 return base::TimeDelta::FromMilliseconds(kInitialRenameRetryDelayMs) *
134 (1 << attempt_number);
135 }
136
137 bool DownloadFileImpl::ShouldRetryFailedRename(DownloadInterruptReason reason) {
138 return reason == DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR;
139 }
140
141 void DownloadFileImpl::RenameWithRetryInternal(
142 const base::FilePath& full_path,
143 RenameOption option,
144 int retries_left,
145 base::TimeTicks time_of_first_failure,
146 const RenameCompletionCallback& callback) {
106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
107 148
108 base::FilePath new_path(full_path); 149 base::FilePath new_path(full_path);
109 150
110 int uniquifier = base::GetUniquePathNumber( 151 if ((option & UNIQUIFY) && full_path != file_.full_path()) {
111 new_path, base::FilePath::StringType()); 152 int uniquifier =
112 if (uniquifier > 0) { 153 base::GetUniquePathNumber(new_path, base::FilePath::StringType());
113 new_path = new_path.InsertBeforeExtensionASCII( 154 if (uniquifier > 0)
114 base::StringPrintf(" (%d)", uniquifier)); 155 new_path = new_path.InsertBeforeExtensionASCII(
156 base::StringPrintf(" (%d)", uniquifier));
115 } 157 }
116 158
117 DownloadInterruptReason reason = file_.Rename(new_path); 159 DownloadInterruptReason reason = file_.Rename(new_path);
118 if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
119 // Make sure our information is updated, since we're about to
120 // error out.
121 SendUpdate();
122 160
123 // Null out callback so that we don't do any more stream processing. 161 // Attempt to retry the rename if possible. If the rename failed and the
124 stream_reader_->RegisterCallback(base::Closure()); 162 // subsequent open also failed, then in_progress() would be false. We don't
125 163 // try to retry renames if the in_progress() was false to begin with since we
126 new_path.clear(); 164 // have less assurance that the file at file_.full_path() was the one we were
165 // working with.
166 if (ShouldRetryFailedRename(reason) && file_.in_progress() &&
167 retries_left > 0) {
168 int attempt_number = kMaxRenameRetries - retries_left;
169 BrowserThread::PostDelayedTask(
170 BrowserThread::FILE,
171 FROM_HERE,
172 base::Bind(&DownloadFileImpl::RenameWithRetryInternal,
173 weak_factory_.GetWeakPtr(),
174 full_path,
175 option,
176 --retries_left,
177 time_of_first_failure.is_null() ? base::TimeTicks::Now()
178 : time_of_first_failure,
179 callback),
180 GetRetryDelayForFailedRename(attempt_number));
181 return;
127 } 182 }
128 183
129 BrowserThread::PostTask( 184 if (!time_of_first_failure.is_null())
130 BrowserThread::UI, FROM_HERE, 185 RecordDownloadFileRenameResultAfterRetry(
131 base::Bind(callback, reason, new_path)); 186 base::TimeTicks::Now() - time_of_first_failure, reason);
132 }
133 187
134 void DownloadFileImpl::RenameAndAnnotate( 188 if (reason == DOWNLOAD_INTERRUPT_REASON_NONE &&
135 const base::FilePath& full_path, 189 (option & ANNOTATE_WITH_SOURCE_INFORMATION)) {
136 const RenameCompletionCallback& callback) {
137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
138
139 base::FilePath new_path(full_path);
140
141 DownloadInterruptReason reason = DOWNLOAD_INTERRUPT_REASON_NONE;
142 // Short circuit null rename.
143 if (full_path != file_.full_path())
144 reason = file_.Rename(new_path);
145
146 if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) {
147 // Doing the annotation after the rename rather than before leaves 190 // Doing the annotation after the rename rather than before leaves
148 // a very small window during which the file has the final name but 191 // a very small window during which the file has the final name but
149 // hasn't been marked with the Mark Of The Web. However, it allows 192 // hasn't been marked with the Mark Of The Web. However, it allows
150 // anti-virus scanners on Windows to actually see the data 193 // anti-virus scanners on Windows to actually see the data
151 // (http://crbug.com/127999) under the correct name (which is information 194 // (http://crbug.com/127999) under the correct name (which is information
152 // it uses). 195 // it uses).
153 reason = file_.AnnotateWithSourceInformation(); 196 reason = file_.AnnotateWithSourceInformation();
154 } 197 }
155 198
156 if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) { 199 if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
309 observer_, file_.bytes_so_far(), CurrentSpeed(), 352 observer_, file_.bytes_so_far(), CurrentSpeed(),
310 GetHashState())); 353 GetHashState()));
311 } 354 }
312 355
313 // static 356 // static
314 int DownloadFile::GetNumberOfDownloadFiles() { 357 int DownloadFile::GetNumberOfDownloadFiles() {
315 return number_active_objects_; 358 return number_active_objects_;
316 } 359 }
317 360
318 } // namespace content 361 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698