• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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