Chromium Code Reviews| Index: components/password_manager/core/browser/affiliation_fetch_throttler.cc |
| diff --git a/components/password_manager/core/browser/affiliation_fetch_throttler.cc b/components/password_manager/core/browser/affiliation_fetch_throttler.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..4d13f0b0db6cea5a2fe8dafc9a19a56c36cd0bd5 |
| --- /dev/null |
| +++ b/components/password_manager/core/browser/affiliation_fetch_throttler.cc |
| @@ -0,0 +1,162 @@ |
| +// Copyright 2014 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 "components/password_manager/core/browser/affiliation_fetch_throttler.h" |
| + |
| +#include "base/logging.h" |
| +#include "base/rand_util.h" |
| +#include "base/thread_task_runner_handle.h" |
| +#include "base/time/tick_clock.h" |
| +#include "base/time/time.h" |
| + |
| +namespace password_manager { |
| + |
| +namespace { |
| + |
| +net::BackoffEntry::Policy kAffiliationBackoffParameters = { |
|
mmenke
2015/01/14 19:25:07
const
engedy
2015/01/15 19:16:42
Done.
|
| + // Number of initial errors (in sequence) to ignore before going into |
| + // exponential backoff. |
| + 0, |
| + |
| + // Initial delay (in ms) once backoff starts. |
| + 10 * 1000, // 10 seconds |
| + |
| + // Factor by which the delay will be multiplied on each subsequent failure. |
| + 4, |
| + |
| + // Fuzzing percentage: 50% will spread delays randomly between 50%--100% of |
| + // the nominal time. |
| + .5, // 50% |
| + |
| + // Maximum delay (in ms) during exponential backoff. |
| + 6 * 3600 * 1000, // 6 hours |
| + |
| + // Time to keep an entry from being discarded even when it has no |
| + // significant state, -1 to never discard. (Not applicable.) |
| + -1, |
| + |
| + // False means that initial_delay_ms is the first delay once we start |
| + // exponential backoff, i.e., there is no delay after subsequent successful |
| + // requests. |
| + false, |
| +}; |
| + |
| +// Grace period before the first request is sent after network connectivity is |
| +// restored. The fuzzing factor from above applies. |
| +const int64 kGracePeriodAfterNetworkRestoredInMs = 10 * 1000; // 10 seconds |
|
mmenke
2015/01/14 19:25:07
Should include basictypes.h for int64
engedy
2015/01/15 19:16:42
Ahh, it looks like that is already deprecated. I h
|
| + |
| +// Implementation of net::BackoffEntry that allows mocking its tick source. |
| +class BackoffEntryImpl : public net::BackoffEntry { |
| + public: |
| + BackoffEntryImpl(base::TickClock* tick_clock) |
|
mmenke
2015/01/14 19:25:07
explicit
engedy
2015/01/15 19:16:42
Done.
|
| + : BackoffEntry(&kAffiliationBackoffParameters), tick_clock_(tick_clock) {} |
| + ~BackoffEntryImpl() override{}; |
|
mmenke
2015/01/14 19:25:07
nit: space after "override"
engedy
2015/01/15 19:16:42
Ahh, actually, there is also a superfluous semi-co
|
| + |
| + protected: |
| + // net::BackoffEntry: |
| + base::TimeTicks ImplGetTimeNow() const override { |
|
mmenke
2015/01/14 19:25:06
optional: This can be private (parent classes can
engedy
2015/01/15 19:16:42
Done.
|
| + return tick_clock_->NowTicks(); |
| + } |
| + |
| + private: |
| + // Must outlive this instance. |
| + base::TickClock* tick_clock_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BackoffEntryImpl); |
| +}; |
| + |
| +} // namespace |
| + |
| +AffiliationFetchThrottler::AffiliationFetchThrottler( |
| + AffiliationFetchThrottlerDelegate* delegate, |
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| + scoped_ptr<base::TickClock> tick_clock) |
| + : delegate_(delegate), |
| + task_runner_(task_runner), |
| + state_(IDLE), |
| + has_network_connectivity_(false), |
| + is_fetch_scheduled_(false), |
| + tick_clock_(tick_clock.Pass()), |
| + exponential_backoff_(new BackoffEntryImpl(tick_clock_.get())), |
| + weak_ptr_factory_(this) { |
| + DCHECK(delegate); |
| + net::NetworkChangeNotifier::AddConnectionTypeObserver(this); |
|
mmenke
2015/01/14 19:25:06
Putting this in a constructor seems like a bad ide
engedy
2015/01/15 19:16:42
I think that is a bit over-cautious. The contract
|
| + has_network_connectivity_ = !net::NetworkChangeNotifier::IsOffline(); |
| +} |
| + |
| +AffiliationFetchThrottler::~AffiliationFetchThrottler() { |
| + net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); |
| +} |
| + |
| +void AffiliationFetchThrottler::SignalNetworkRequestNeeded() { |
| + if (state_ != IDLE) |
| + return; |
| + |
| + state_ = FETCH_NEEDED; |
| + if (has_network_connectivity_) |
| + EnsureCallbackIsScheduled(); |
| +} |
| + |
| +void AffiliationFetchThrottler::InformOfNetworkRequestComplete(bool success) { |
| + DCHECK_EQ(state_, FETCH_IN_FLIGHT); |
| + state_ = IDLE; |
| + exponential_backoff_->InformOfRequest(success); |
|
mmenke
2015/01/14 20:43:43
Oops...Got so sidetracked thinking about the NCN s
engedy
2015/01/15 19:16:42
Please see my other comment.
|
| +} |
| + |
| +void AffiliationFetchThrottler::EnsureCallbackIsScheduled() { |
| + DCHECK_EQ(state_, FETCH_NEEDED); |
| + if (is_fetch_scheduled_) |
| + return; |
| + |
| + is_fetch_scheduled_ = true; |
| + task_runner_->PostDelayedTask( |
| + FROM_HERE, |
| + base::Bind(&AffiliationFetchThrottler::OnBackoffDelayExpiredCallback, |
| + weak_ptr_factory_.GetWeakPtr()), |
| + exponential_backoff_->GetTimeUntilRelease()); |
| +} |
| + |
| +void AffiliationFetchThrottler::OnBackoffDelayExpiredCallback() { |
| + DCHECK_EQ(state_, FETCH_NEEDED); |
| + DCHECK(is_fetch_scheduled_); |
| + is_fetch_scheduled_ = false; |
| + |
| + // Do nothing if network connectivity was lost while this callback was in the |
| + // task queue. The callback will be posted in the OnConnectionTypeChanged |
| + // handler once again. |
| + if (!has_network_connectivity_) |
| + return; |
| + |
| + // The release time might have been increased if network connectivity was lost |
| + // and restored while this callback was in the task queue. If so, reschedule. |
| + if (exponential_backoff_->ShouldRejectRequest()) |
| + EnsureCallbackIsScheduled(); |
| + else |
| + state_ = delegate_->OnCanSendNetworkRequest() ? FETCH_IN_FLIGHT : IDLE; |
| +} |
| + |
| +void AffiliationFetchThrottler::OnConnectionTypeChanged( |
| + net::NetworkChangeNotifier::ConnectionType type) { |
| + if (has_network_connectivity_ == !net::NetworkChangeNotifier::IsOffline()) |
|
mmenke
2015/01/14 19:25:06
What if we're switching from WIFI to cellular?
mmenke
2015/01/14 19:25:07
Shouldn't call NetworkChangeNotifier::IsOffline tw
engedy
2015/01/15 19:16:42
Done.
engedy
2015/01/15 19:16:42
Hmm. So far I did not consider the kind of the con
mmenke
2015/01/16 21:28:34
Now that I realize how this class actually works,
|
| + return; |
| + |
| + has_network_connectivity_ = !net::NetworkChangeNotifier::IsOffline(); |
| + if (!has_network_connectivity_) |
| + return; |
|
mmenke
2015/01/14 19:25:06
While being offline is one of the most common caus
mmenke
2015/01/14 19:25:07
No provisions to retry here?
engedy
2015/01/15 19:16:42
I am not sure I understand. Are you implying that
engedy
2015/01/15 19:16:42
Hmm. I believe this is somewhat different than net
mmenke
2015/01/16 21:28:34
Sorry, got confused about how this would plug in (
|
| + |
| + double grace_ms = |
| + kGracePeriodAfterNetworkRestoredInMs * |
| + (1 - base::RandDouble() * kAffiliationBackoffParameters.jitter_factor); |
| + base::TimeTicks release_time = std::max( |
| + exponential_backoff_->GetReleaseTime(), |
| + tick_clock_->NowTicks() + base::TimeDelta::FromMillisecondsD(grace_ms)); |
| + // Note that SetCustomReleaseTime() takes the maximum of the current release |
| + // time and the supplied |release_time|. |
| + exponential_backoff_->SetCustomReleaseTime(release_time); |
| + |
| + if (state_ == FETCH_NEEDED) |
| + EnsureCallbackIsScheduled(); |
| +} |
| + |
| +} // namespace password_manager |