1 // Copyright 2022 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <grpc/support/port_platform.h>
16
17 #include "src/core/lib/resource_quota/periodic_update.h"
18
19 #include <atomic>
20
21 #include "src/core/lib/gpr/useful.h"
22
23 namespace grpc_core {
24
MaybeEndPeriod(absl::FunctionRef<void (Duration)> f)25 bool PeriodicUpdate::MaybeEndPeriod(absl::FunctionRef<void(Duration)> f) {
26 if (period_start_ == Timestamp::ProcessEpoch()) {
27 period_start_ = Timestamp::Now();
28 updates_remaining_.store(1, std::memory_order_release);
29 return false;
30 }
31 // updates_remaining_ just reached 0 and the thread calling this function was
32 // the decrementer that got us there.
33 // We can now safely mutate any non-atomic mutable variables (we've got a
34 // guarantee that no other thread will), and by the time this function returns
35 // we must store a postive number into updates_remaining_.
36 auto now = Timestamp::Now();
37 Duration time_so_far = now - period_start_;
38 if (time_so_far < period_) {
39 // At most double the number of updates remaining until the next period.
40 // At least try to estimate when we'll reach it.
41 int64_t better_guess;
42 if (time_so_far.millis() == 0) {
43 better_guess = expected_updates_per_period_ * 2;
44 } else {
45 // Determine a scaling factor that would have gotten us to the next
46 // period, but clamp between 1.01 (at least 1% increase in guesses)
47 // and 2.0 (at most doubling) - to avoid running completely out of
48 // control.
49 const double scale =
50 Clamp(period_.seconds() / time_so_far.seconds(), 1.01, 2.0);
51 better_guess = expected_updates_per_period_ * scale;
52 if (better_guess <= expected_updates_per_period_) {
53 better_guess = expected_updates_per_period_ + 1;
54 }
55 }
56 // Store the remainder left. Note that updates_remaining_ may have been
57 // decremented by another thread whilst we performed the above calculations:
58 // we simply discard those decrements.
59 updates_remaining_.store(better_guess - expected_updates_per_period_,
60 std::memory_order_release);
61 // Not quite done, return, try for longer.
62 return false;
63 }
64 // Finished period, start a new one and return true.
65 // We try to predict how many update periods we'd need to cover the full time
66 // span, and we increase that by 1% to attempt to tend to not enter the above
67 // stanza.
68 expected_updates_per_period_ =
69 period_.seconds() * expected_updates_per_period_ / time_so_far.seconds();
70 if (expected_updates_per_period_ < 1) expected_updates_per_period_ = 1;
71 period_start_ = now;
72 f(time_so_far);
73 updates_remaining_.store(expected_updates_per_period_,
74 std::memory_order_release);
75 return true;
76 }
77
78 } // namespace grpc_core
79