diff options
author | Alex Vakulenko <avakulenko@google.com> | 2015-10-12 15:21:28 -0700 |
---|---|---|
committer | Alex Vakulenko <avakulenko@google.com> | 2015-10-13 16:10:03 -0700 |
commit | 9ed0cab99f18acb3570a35e9408f24355f6b8324 (patch) | |
tree | 60e3b4c2822b812b3218489a9a6d835df1e8ca6e /brillo/backoff_entry.cc | |
parent | eabfe23a51c91a103042793ac2d5c28170994e1f (diff) | |
download | platform_external_libbrillo-9ed0cab99f18acb3570a35e9408f24355f6b8324.tar.gz platform_external_libbrillo-9ed0cab99f18acb3570a35e9408f24355f6b8324.tar.bz2 platform_external_libbrillo-9ed0cab99f18acb3570a35e9408f24355f6b8324.zip |
Move chromeos symbols into brillo namespace
And move the include files into "brillo" directory instead of "chromeos"
BUG: 24872993
TEST=built aosp and brillo and unit tests pass on dragonoboard
Change-Id: Ieb979d1ebd3152921d36cd15acbd6247f02aae69
Diffstat (limited to 'brillo/backoff_entry.cc')
-rw-r--r-- | brillo/backoff_entry.cc | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/brillo/backoff_entry.cc b/brillo/backoff_entry.cc new file mode 100644 index 0000000..acef714 --- /dev/null +++ b/brillo/backoff_entry.cc @@ -0,0 +1,167 @@ +// Copyright 2015 The Chromium OS 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 <brillo/backoff_entry.h> + +#include <algorithm> +#include <cmath> +#include <limits> + +#include <base/logging.h> +#include <base/numerics/safe_math.h> +#include <base/rand_util.h> + +namespace brillo { + +BackoffEntry::BackoffEntry(const BackoffEntry::Policy* const policy) + : policy_(policy) { + DCHECK(policy_); + Reset(); +} + +void BackoffEntry::InformOfRequest(bool succeeded) { + if (!succeeded) { + ++failure_count_; + exponential_backoff_release_time_ = CalculateReleaseTime(); + } else { + // We slowly decay the number of times delayed instead of + // resetting it to 0 in order to stay stable if we receive + // successes interleaved between lots of failures. Note that in + // the normal case, the calculated release time (in the next + // statement) will be in the past once the method returns. + if (failure_count_ > 0) + --failure_count_; + + // The reason why we are not just cutting the release time to + // ImplGetTimeNow() is on the one hand, it would unset a release + // time set by SetCustomReleaseTime and on the other we would like + // to push every request up to our "horizon" when dealing with + // multiple in-flight requests. Ex: If we send three requests and + // we receive 2 failures and 1 success. The success that follows + // those failures will not reset the release time, further + // requests will then need to wait the delay caused by the 2 + // failures. + base::TimeDelta delay; + if (policy_->always_use_initial_delay) + delay = base::TimeDelta::FromMilliseconds(policy_->initial_delay_ms); + exponential_backoff_release_time_ = std::max( + ImplGetTimeNow() + delay, exponential_backoff_release_time_); + } +} + +bool BackoffEntry::ShouldRejectRequest() const { + return exponential_backoff_release_time_ > ImplGetTimeNow(); +} + +base::TimeDelta BackoffEntry::GetTimeUntilRelease() const { + base::TimeTicks now = ImplGetTimeNow(); + if (exponential_backoff_release_time_ <= now) + return base::TimeDelta(); + return exponential_backoff_release_time_ - now; +} + +base::TimeTicks BackoffEntry::GetReleaseTime() const { + return exponential_backoff_release_time_; +} + +void BackoffEntry::SetCustomReleaseTime(const base::TimeTicks& release_time) { + exponential_backoff_release_time_ = release_time; +} + +bool BackoffEntry::CanDiscard() const { + if (policy_->entry_lifetime_ms == -1) + return false; + + base::TimeTicks now = ImplGetTimeNow(); + + int64 unused_since_ms = + (now - exponential_backoff_release_time_).InMilliseconds(); + + // Release time is further than now, we are managing it. + if (unused_since_ms < 0) + return false; + + if (failure_count_ > 0) { + // Need to keep track of failures until maximum back-off period + // has passed (since further failures can add to back-off). + return unused_since_ms >= std::max(policy_->maximum_backoff_ms, + policy_->entry_lifetime_ms); + } + + // Otherwise, consider the entry is outdated if it hasn't been used for the + // specified lifetime period. + return unused_since_ms >= policy_->entry_lifetime_ms; +} + +void BackoffEntry::Reset() { + failure_count_ = 0; + + // We leave exponential_backoff_release_time_ unset, meaning 0. We could + // initialize to ImplGetTimeNow() but because it's a virtual method it's + // not safe to call in the constructor (and the constructor calls Reset()). + // The effects are the same, i.e. ShouldRejectRequest() will return false + // right after Reset(). + exponential_backoff_release_time_ = base::TimeTicks(); +} + +base::TimeTicks BackoffEntry::ImplGetTimeNow() const { + return base::TimeTicks::Now(); +} + +base::TimeTicks BackoffEntry::CalculateReleaseTime() const { + int effective_failure_count = + std::max(0, failure_count_ - policy_->num_errors_to_ignore); + + // If always_use_initial_delay is true, it's equivalent to + // the effective_failure_count always being one greater than when it's false. + if (policy_->always_use_initial_delay) + ++effective_failure_count; + + if (effective_failure_count == 0) { + // Never reduce previously set release horizon, e.g. due to Retry-After + // header. + return std::max(ImplGetTimeNow(), exponential_backoff_release_time_); + } + + // The delay is calculated with this formula: + // delay = initial_backoff * multiply_factor^( + // effective_failure_count - 1) * Uniform(1 - jitter_factor, 1] + // Note: if the failure count is too high, |delay_ms| will become infinity + // after the exponential calculation, and then NaN after the jitter is + // accounted for. Both cases are handled by using CheckedNumeric<int64_t> to + // perform the conversion to integers. + double delay_ms = policy_->initial_delay_ms; + delay_ms *= pow(policy_->multiply_factor, effective_failure_count - 1); + delay_ms -= base::RandDouble() * policy_->jitter_factor * delay_ms; + + // Do overflow checking in microseconds, the internal unit of TimeTicks. + const int64_t kTimeTicksNowUs = + (ImplGetTimeNow() - base::TimeTicks()).InMicroseconds(); + base::internal::CheckedNumeric<int64_t> calculated_release_time_us = + delay_ms + 0.5; + calculated_release_time_us *= base::Time::kMicrosecondsPerMillisecond; + calculated_release_time_us += kTimeTicksNowUs; + + const int64_t kMaxTime = std::numeric_limits<int64_t>::max(); + base::internal::CheckedNumeric<int64_t> maximum_release_time_us = kMaxTime; + if (policy_->maximum_backoff_ms >= 0) { + maximum_release_time_us = policy_->maximum_backoff_ms; + maximum_release_time_us *= base::Time::kMicrosecondsPerMillisecond; + maximum_release_time_us += kTimeTicksNowUs; + } + + // Decide between maximum release time and calculated release time, accounting + // for overflow with both. + int64 release_time_us = std::min( + calculated_release_time_us.ValueOrDefault(kMaxTime), + maximum_release_time_us.ValueOrDefault(kMaxTime)); + + // Never reduce previously set release horizon, e.g. due to Retry-After + // header. + return std::max( + base::TimeTicks() + base::TimeDelta::FromMicroseconds(release_time_us), + exponential_backoff_release_time_); +} + +} // namespace brillo |