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

Unified 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: Prepare to reland after XP test fix. Created 6 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/download/download_file_impl.h ('k') | content/browser/download/download_file_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/download/download_file_impl.cc
diff --git a/content/browser/download/download_file_impl.cc b/content/browser/download/download_file_impl.cc
index e8cb761ea66ac89c642fd4b96931fd412f8e8fe1..91b25dac35bece664ef08ee0080b4ce50db166f2 100644
--- a/content/browser/download/download_file_impl.cc
+++ b/content/browser/download/download_file_impl.cc
@@ -25,6 +25,12 @@ namespace content {
const int kUpdatePeriodMs = 500;
const int kMaxTimeBlockingFileThreadMs = 1000;
+// These constants control the default retry behavior for failing renames. Each
+// retry is performed after a delay that is twice the previous delay. The
+// initial delay is specified by kInitialRenameRetryDelayMs.
+const int kMaxRenameRetries = 3;
+const int kInitialRenameRetryDelayMs = 200;
+
int DownloadFile::number_active_objects_ = 0;
DownloadFileImpl::DownloadFileImpl(
@@ -36,20 +42,20 @@ DownloadFileImpl::DownloadFileImpl(
scoped_ptr<ByteStreamReader> stream,
const net::BoundNetLog& bound_net_log,
base::WeakPtr<DownloadDestinationObserver> observer)
- : file_(save_info->file_path,
- url,
- referrer_url,
- save_info->offset,
- calculate_hash,
- save_info->hash_state,
- save_info->file.Pass(),
- bound_net_log),
- default_download_directory_(default_download_directory),
- stream_reader_(stream.Pass()),
- bytes_seen_(0),
- bound_net_log_(bound_net_log),
- observer_(observer),
- weak_factory_(this) {
+ : file_(save_info->file_path,
+ url,
+ referrer_url,
+ save_info->offset,
+ calculate_hash,
+ save_info->hash_state,
+ save_info->file.Pass(),
+ bound_net_log),
+ default_download_directory_(default_download_directory),
+ stream_reader_(stream.Pass()),
+ bytes_seen_(0),
+ bound_net_log_(bound_net_log),
+ observer_(observer),
+ weak_factory_(this) {
}
DownloadFileImpl::~DownloadFileImpl() {
@@ -103,47 +109,84 @@ DownloadInterruptReason DownloadFileImpl::AppendDataToFile(
void DownloadFileImpl::RenameAndUniquify(
const base::FilePath& full_path,
const RenameCompletionCallback& callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-
- base::FilePath new_path(full_path);
-
- int uniquifier = base::GetUniquePathNumber(
- new_path, base::FilePath::StringType());
- if (uniquifier > 0) {
- new_path = new_path.InsertBeforeExtensionASCII(
- base::StringPrintf(" (%d)", uniquifier));
- }
-
- DownloadInterruptReason reason = file_.Rename(new_path);
- if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
- // Make sure our information is updated, since we're about to
- // error out.
- SendUpdate();
+ RenameWithRetryInternal(
+ full_path, UNIQUIFY, kMaxRenameRetries, base::TimeTicks(), callback);
+}
- // Null out callback so that we don't do any more stream processing.
- stream_reader_->RegisterCallback(base::Closure());
+void DownloadFileImpl::RenameAndAnnotate(
+ const base::FilePath& full_path,
+ const RenameCompletionCallback& callback) {
+ RenameWithRetryInternal(full_path,
+ ANNOTATE_WITH_SOURCE_INFORMATION,
+ kMaxRenameRetries,
+ base::TimeTicks(),
+ callback);
+}
- new_path.clear();
- }
+base::TimeDelta DownloadFileImpl::GetRetryDelayForFailedRename(
+ int attempt_number) {
+ DCHECK_GE(attempt_number, 0);
+ // |delay| starts at kInitialRenameRetryDelayMs and increases by a factor of
+ // 2 at each subsequent retry. Assumes that |retries_left| starts at
+ // kMaxRenameRetries. Also assumes that kMaxRenameRetries is less than the
+ // number of bits in an int.
+ return base::TimeDelta::FromMilliseconds(kInitialRenameRetryDelayMs) *
+ (1 << attempt_number);
+}
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(callback, reason, new_path));
+bool DownloadFileImpl::ShouldRetryFailedRename(DownloadInterruptReason reason) {
+ return reason == DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR;
}
-void DownloadFileImpl::RenameAndAnnotate(
+void DownloadFileImpl::RenameWithRetryInternal(
const base::FilePath& full_path,
+ RenameOption option,
+ int retries_left,
+ base::TimeTicks time_of_first_failure,
const RenameCompletionCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
base::FilePath new_path(full_path);
- DownloadInterruptReason reason = DOWNLOAD_INTERRUPT_REASON_NONE;
- // Short circuit null rename.
- if (full_path != file_.full_path())
- reason = file_.Rename(new_path);
+ if ((option & UNIQUIFY) && full_path != file_.full_path()) {
+ int uniquifier =
+ base::GetUniquePathNumber(new_path, base::FilePath::StringType());
+ if (uniquifier > 0)
+ new_path = new_path.InsertBeforeExtensionASCII(
+ base::StringPrintf(" (%d)", uniquifier));
+ }
+
+ DownloadInterruptReason reason = file_.Rename(new_path);
+
+ // Attempt to retry the rename if possible. If the rename failed and the
+ // subsequent open also failed, then in_progress() would be false. We don't
+ // try to retry renames if the in_progress() was false to begin with since we
+ // have less assurance that the file at file_.full_path() was the one we were
+ // working with.
+ if (ShouldRetryFailedRename(reason) && file_.in_progress() &&
+ retries_left > 0) {
+ int attempt_number = kMaxRenameRetries - retries_left;
+ BrowserThread::PostDelayedTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&DownloadFileImpl::RenameWithRetryInternal,
+ weak_factory_.GetWeakPtr(),
+ full_path,
+ option,
+ --retries_left,
+ time_of_first_failure.is_null() ? base::TimeTicks::Now()
+ : time_of_first_failure,
+ callback),
+ GetRetryDelayForFailedRename(attempt_number));
+ return;
+ }
+
+ if (!time_of_first_failure.is_null())
+ RecordDownloadFileRenameResultAfterRetry(
+ base::TimeTicks::Now() - time_of_first_failure, reason);
- if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) {
+ if (reason == DOWNLOAD_INTERRUPT_REASON_NONE &&
+ (option & ANNOTATE_WITH_SOURCE_INFORMATION)) {
// Doing the annotation after the rename rather than before leaves
// a very small window during which the file has the final name but
// hasn't been marked with the Mark Of The Web. However, it allows
« no previous file with comments | « content/browser/download/download_file_impl.h ('k') | content/browser/download/download_file_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698