You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
5.0 KiB
154 lines
5.0 KiB
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef NETDUTILS_BACKOFFSEQUENCE_H
|
|
#define NETDUTILS_BACKOFFSEQUENCE_H
|
|
|
|
#include <stdint.h>
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <limits>
|
|
|
|
namespace android {
|
|
namespace netdutils {
|
|
|
|
// Encapsulate some RFC 3315 section 14 -style backoff mechanics.
|
|
//
|
|
// https://tools.ietf.org/html/rfc3315#section-14
|
|
template<typename time_type = std::chrono::seconds, typename counter_type = uint32_t>
|
|
class BackoffSequence {
|
|
public:
|
|
struct Parameters {
|
|
time_type initialRetransTime{TIME_UNITY};
|
|
counter_type maxRetransCount{0U};
|
|
time_type maxRetransTime{TIME_ZERO};
|
|
time_type maxRetransDuration{TIME_ZERO};
|
|
time_type endOfSequenceIndicator{TIME_ZERO};
|
|
};
|
|
|
|
BackoffSequence() : BackoffSequence(Parameters{}) {}
|
|
BackoffSequence(const BackoffSequence &) = default;
|
|
BackoffSequence(BackoffSequence &&) = default;
|
|
BackoffSequence& operator=(const BackoffSequence &) = default;
|
|
BackoffSequence& operator=(BackoffSequence &&) = default;
|
|
|
|
bool hasNextTimeout() const noexcept {
|
|
return !maxRetransCountExceed() && !maxRetransDurationExceeded();
|
|
}
|
|
|
|
// Returns 0 when the sequence is exhausted.
|
|
time_type getNextTimeout() {
|
|
if (!hasNextTimeout()) return getEndOfSequenceIndicator();
|
|
|
|
mRetransTime = getNextTimeoutAfter(mRetransTime);
|
|
|
|
mRetransCount++;
|
|
mTotalRetransDuration += mRetransTime;
|
|
return mRetransTime;
|
|
}
|
|
|
|
time_type getEndOfSequenceIndicator() const noexcept {
|
|
return mParams.endOfSequenceIndicator;
|
|
}
|
|
|
|
class Builder {
|
|
public:
|
|
Builder() {}
|
|
|
|
constexpr Builder& withInitialRetransmissionTime(time_type irt) {
|
|
mParams.initialRetransTime = irt;
|
|
return *this;
|
|
}
|
|
constexpr Builder& withMaximumRetransmissionCount(counter_type mrc) {
|
|
mParams.maxRetransCount = mrc;
|
|
return *this;
|
|
}
|
|
constexpr Builder& withMaximumRetransmissionTime(time_type mrt) {
|
|
mParams.maxRetransTime = mrt;
|
|
return *this;
|
|
}
|
|
constexpr Builder& withMaximumRetransmissionDuration(time_type mrd) {
|
|
mParams.maxRetransDuration = mrd;
|
|
return *this;
|
|
}
|
|
constexpr Builder& withEndOfSequenceIndicator(time_type eos) {
|
|
mParams.endOfSequenceIndicator = eos;
|
|
return *this;
|
|
}
|
|
|
|
constexpr BackoffSequence build() const {
|
|
return BackoffSequence(mParams);
|
|
}
|
|
|
|
private:
|
|
Parameters mParams;
|
|
};
|
|
|
|
private:
|
|
static constexpr int PER_ITERATION_SCALING_FACTOR = 2;
|
|
static constexpr time_type TIME_ZERO = time_type();
|
|
static constexpr time_type TIME_UNITY = time_type(1);
|
|
|
|
constexpr BackoffSequence(const struct Parameters ¶ms)
|
|
: mParams(params),
|
|
mRetransCount(0),
|
|
mRetransTime(TIME_ZERO),
|
|
mTotalRetransDuration(TIME_ZERO) {}
|
|
|
|
constexpr bool maxRetransCountExceed() const {
|
|
return (mParams.maxRetransCount > 0) && (mRetransCount >= mParams.maxRetransCount);
|
|
}
|
|
|
|
constexpr bool maxRetransDurationExceeded() const {
|
|
return (mParams.maxRetransDuration > TIME_ZERO) &&
|
|
(mTotalRetransDuration >= mParams.maxRetransDuration);
|
|
}
|
|
|
|
time_type getNextTimeoutAfter(time_type lastTimeout) const {
|
|
// TODO: Support proper random jitter. Also, consider supporting some
|
|
// per-iteration scaling factor other than doubling.
|
|
time_type nextTimeout = (lastTimeout > TIME_ZERO)
|
|
? PER_ITERATION_SCALING_FACTOR * lastTimeout
|
|
: mParams.initialRetransTime;
|
|
|
|
// Check if overflow occurred.
|
|
if (nextTimeout < lastTimeout) {
|
|
nextTimeout = std::numeric_limits<time_type>::max();
|
|
}
|
|
|
|
// Cap to maximum allowed, if necessary.
|
|
if (mParams.maxRetransTime > TIME_ZERO) {
|
|
nextTimeout = std::min(nextTimeout, mParams.maxRetransTime);
|
|
}
|
|
|
|
// Don't overflow the maximum total duration.
|
|
if (mParams.maxRetransDuration > TIME_ZERO) {
|
|
nextTimeout = std::min(nextTimeout, mParams.maxRetransDuration - lastTimeout);
|
|
}
|
|
return nextTimeout;
|
|
}
|
|
|
|
const Parameters mParams;
|
|
counter_type mRetransCount;
|
|
time_type mRetransTime;
|
|
time_type mTotalRetransDuration;
|
|
};
|
|
|
|
} // namespace netdutils
|
|
} // namespace android
|
|
|
|
#endif /* NETDUTILS_BACKOFFSEQUENCE_H */
|