// Copyright 2022 gRPC authors. // // 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 "src/core/lib/resource_quota/periodic_update.h" #include #include #include "src/core/util/useful.h" namespace grpc_core { bool PeriodicUpdate::MaybeEndPeriod(absl::FunctionRef f) { if (period_start_ == Timestamp::ProcessEpoch()) { period_start_ = Timestamp::Now(); updates_remaining_.store(1, std::memory_order_release); return false; } // updates_remaining_ just reached 0 and the thread calling this function was // the decrementer that got us there. // We can now safely mutate any non-atomic mutable variables (we've got a // guarantee that no other thread will), and by the time this function returns // we must store a postive number into updates_remaining_. auto now = Timestamp::Now(); Duration time_so_far = now - period_start_; if (time_so_far < period_) { // At most double the number of updates remaining until the next period. // At least try to estimate when we'll reach it. int64_t better_guess; if (time_so_far.millis() == 0) { better_guess = expected_updates_per_period_ * 2; } else { // Determine a scaling factor that would have gotten us to the next // period, but clamp between 1.01 (at least 1% increase in guesses) // and 2.0 (at most doubling) - to avoid running completely out of // control. const double scale = Clamp(period_.seconds() / time_so_far.seconds(), 1.01, 2.0); better_guess = expected_updates_per_period_ * scale; if (better_guess <= expected_updates_per_period_) { better_guess = expected_updates_per_period_ + 1; } } // Store the remainder left. Note that updates_remaining_ may have been // decremented by another thread whilst we performed the above calculations: // we simply discard those decrements. auto remaining = better_guess - expected_updates_per_period_; expected_updates_per_period_ = better_guess; updates_remaining_.store(remaining, std::memory_order_release); // Not quite done, return, try for longer. return false; } // Finished period, start a new one and return true. // We try to predict how many update periods we'd need to cover the full time // span, and we increase that by 1% to attempt to tend to not enter the above // stanza. expected_updates_per_period_ = period_.seconds() * expected_updates_per_period_ / time_so_far.seconds(); if (expected_updates_per_period_ < 1) expected_updates_per_period_ = 1; period_start_ = now; f(time_so_far); updates_remaining_.store(expected_updates_per_period_, std::memory_order_release); return true; } } // namespace grpc_core