1 // Copyright 2021 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 "src/core/ext/filters/channel_idle/idle_filter_state.h"
16
17 #include <assert.h>
18 #include <grpc/support/port_platform.h>
19
20 namespace grpc_core {
21
IdleFilterState(bool start_timer)22 IdleFilterState::IdleFilterState(bool start_timer)
23 : state_(start_timer ? kTimerStarted : 0) {}
24
IncreaseCallCount()25 void IdleFilterState::IncreaseCallCount() {
26 uintptr_t state = state_.load(std::memory_order_relaxed);
27 uintptr_t new_state;
28 do {
29 // Increment the counter, and flag that there's been activity.
30 new_state = state;
31 new_state |= kCallsStartedSinceLastTimerCheck;
32 new_state += kCallIncrement;
33 } while (!state_.compare_exchange_weak(
34 state, new_state, std::memory_order_acq_rel, std::memory_order_relaxed));
35 }
36
DecreaseCallCount()37 bool IdleFilterState::DecreaseCallCount() {
38 uintptr_t state = state_.load(std::memory_order_relaxed);
39 uintptr_t new_state;
40 bool start_timer;
41 do {
42 start_timer = false;
43 new_state = state;
44 // Decrement call count (and assert there's at least one call outstanding!)
45 assert(new_state >= kCallIncrement);
46 new_state -= kCallIncrement;
47 // If that decrement reaches a call count of zero and we have not started a
48 // timer
49 if ((new_state >> kCallsInProgressShift) == 0 &&
50 (new_state & kTimerStarted) == 0) {
51 // Flag that we will start a timer, and mark it started so nobody else
52 // does.
53 start_timer = true;
54 new_state |= kTimerStarted;
55 new_state &= ~kCallsInProgressShift;
56 }
57 } while (!state_.compare_exchange_weak(
58 state, new_state, std::memory_order_acq_rel, std::memory_order_relaxed));
59 return start_timer;
60 }
61
CheckTimer()62 bool IdleFilterState::CheckTimer() {
63 uintptr_t state = state_.load(std::memory_order_relaxed);
64 uintptr_t new_state;
65 bool start_timer;
66 do {
67 if ((state >> kCallsInProgressShift) != 0) {
68 // Still calls in progress: nothing needs updating, just return
69 // and keep the timer going!
70 return true;
71 }
72 new_state = state;
73 bool is_active = false;
74 if (new_state & kCallsStartedSinceLastTimerCheck) {
75 // If any calls started since the last time we checked, then consider the
76 // channel still active and try again.
77 is_active = true;
78 new_state &= ~kCallsStartedSinceLastTimerCheck;
79 }
80 if (is_active) {
81 // If we are still active, we should signal that the timer should start
82 // again.
83 start_timer = true;
84 } else {
85 // Otherwise, we should not start the timer again, and we should signal
86 // that in the updated state.
87 start_timer = false;
88 new_state &= ~kTimerStarted;
89 }
90 } while (!state_.compare_exchange_weak(
91 state, new_state, std::memory_order_acq_rel, std::memory_order_relaxed));
92 return start_timer;
93 }
94
95 } // namespace grpc_core
96