Rocket.Chat.ReactNative/ios/Pods/Flipper-Folly/folly/executors/Codel.cpp

135 lines
4.6 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#include <folly/executors/Codel.h>
#include <folly/portability/GFlags.h>
#include <algorithm>
#include <stdexcept>
DEFINE_int32(codel_interval, 100, "Codel default interval time in ms");
DEFINE_int32(codel_target_delay, 5, "Target codel queueing delay in ms");
using namespace std::chrono;
namespace folly {
Codel::Codel()
: Codel(Codel::Options()
.setInterval(milliseconds(FLAGS_codel_interval))
.setTargetDelay(milliseconds(FLAGS_codel_target_delay))) {}
Codel::Codel(const Options& options)
: codelMinDelayNs_(0),
codelIntervalTimeNs_(
duration_cast<nanoseconds>(steady_clock::now().time_since_epoch())
.count()),
targetDelay_(options.targetDelay()),
interval_(options.interval()),
codelResetDelay_(true),
overloaded_(false) {}
bool Codel::overloaded(nanoseconds delay) {
bool ret = false;
auto now = steady_clock::now();
// Avoid another thread updating the value at the same time we are using it
// to calculate the overloaded state
auto minDelay = nanoseconds(codelMinDelayNs_);
// Get a snapshot of the parameters to determine overload condition
auto opts = getOptions();
auto sloughTimeout = getSloughTimeout(opts.targetDelay());
if (now > steady_clock::time_point(nanoseconds(codelIntervalTimeNs_)) &&
// testing before exchanging is more cacheline-friendly
(!codelResetDelay_.load(std::memory_order_acquire) &&
!codelResetDelay_.exchange(true))) {
codelIntervalTimeNs_ =
duration_cast<nanoseconds>((now + opts.interval()).time_since_epoch())
.count();
if (minDelay > opts.targetDelay()) {
overloaded_ = true;
} else {
overloaded_ = false;
}
}
// Care must be taken that only a single thread resets codelMinDelay_,
// and that it happens after the interval reset above
if (codelResetDelay_.load(std::memory_order_acquire) &&
codelResetDelay_.exchange(false)) {
codelMinDelayNs_ = delay.count();
// More than one request must come in during an interval before codel
// starts dropping requests
return false;
} else if (delay < nanoseconds(codelMinDelayNs_)) {
codelMinDelayNs_ = delay.count();
}
// Here is where we apply different logic than codel proper. Instead of
// adapting the interval until the next drop, we slough off requests with
// queueing delay > 2*target_delay while in the overloaded regime. This
// empirically works better for our services than the codel approach of
// increasingly often dropping packets.
if (overloaded_ && delay > sloughTimeout) {
ret = true;
}
return ret;
}
int Codel::getLoad() {
// it might be better to use the average delay instead of minDelay, but we'd
// have to track it. aspiring bootcamper?
auto opts = getOptions();
return std::min<int>(
100, 100 * getMinDelay() / getSloughTimeout(opts.targetDelay()));
}
void Codel::setOptions(Options const& options) {
// Carry out some basic sanity checks.
auto delay = options.targetDelay();
auto interval = options.interval();
if (interval <= delay || delay <= milliseconds::zero() ||
interval <= milliseconds::zero()) {
throw std::invalid_argument("Invalid arguments provided");
}
interval_.store(interval, std::memory_order_relaxed);
targetDelay_.store(delay, std::memory_order_relaxed);
}
const Codel::Options Codel::getOptions() const {
auto interval = interval_.load(std::memory_order_relaxed);
auto delay = targetDelay_.load(std::memory_order_relaxed);
// Enforcing the invariant that targetDelay <= interval. A violation could
// potentially occur if either parameter was updated by another concurrent
// thread via the setOptions() method.
delay = std::min(delay, interval);
return Codel::Options().setTargetDelay(delay).setInterval(interval);
}
nanoseconds Codel::getMinDelay() {
return nanoseconds(codelMinDelayNs_);
}
milliseconds Codel::getSloughTimeout(milliseconds delay) const {
return delay * 2;
}
} // namespace folly