1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #include <grpc/support/port_platform.h>
20 
21 #include "src/core/ext/filters/client_channel/lb_policy.h"
22 
23 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
24 #include "src/core/lib/iomgr/combiner.h"
25 
26 namespace grpc_core {
27 
28 DebugOnlyTraceFlag grpc_trace_lb_policy_refcount(false, "lb_policy_refcount");
29 
30 //
31 // LoadBalancingPolicy
32 //
33 
LoadBalancingPolicy(Args args,intptr_t initial_refcount)34 LoadBalancingPolicy::LoadBalancingPolicy(Args args, intptr_t initial_refcount)
35     : InternallyRefCounted(
36           GRPC_TRACE_FLAG_ENABLED(grpc_trace_lb_policy_refcount)
37               ? "LoadBalancingPolicy"
38               : nullptr,
39           initial_refcount),
40       work_serializer_(std::move(args.work_serializer)),
41       interested_parties_(grpc_pollset_set_create()),
42       channel_control_helper_(std::move(args.channel_control_helper)) {}
43 
~LoadBalancingPolicy()44 LoadBalancingPolicy::~LoadBalancingPolicy() {
45   grpc_pollset_set_destroy(interested_parties_);
46 }
47 
Orphan()48 void LoadBalancingPolicy::Orphan() {
49   ShutdownLocked();
50   Unref(DEBUG_LOCATION, "Orphan");
51 }
52 
53 //
54 // LoadBalancingPolicy::UpdateArgs
55 //
56 
UpdateArgs(const UpdateArgs & other)57 LoadBalancingPolicy::UpdateArgs::UpdateArgs(const UpdateArgs& other) {
58   addresses = other.addresses;
59   config = other.config;
60   args = grpc_channel_args_copy(other.args);
61 }
62 
UpdateArgs(UpdateArgs && other)63 LoadBalancingPolicy::UpdateArgs::UpdateArgs(UpdateArgs&& other) noexcept {
64   addresses = std::move(other.addresses);
65   config = std::move(other.config);
66   // TODO(roth): Use std::move() once channel args is converted to C++.
67   args = other.args;
68   other.args = nullptr;
69 }
70 
operator =(const UpdateArgs & other)71 LoadBalancingPolicy::UpdateArgs& LoadBalancingPolicy::UpdateArgs::operator=(
72     const UpdateArgs& other) {
73   if (&other == this) {
74     return *this;
75   }
76   addresses = other.addresses;
77   config = other.config;
78   grpc_channel_args_destroy(args);
79   args = grpc_channel_args_copy(other.args);
80   return *this;
81 }
82 
operator =(UpdateArgs && other)83 LoadBalancingPolicy::UpdateArgs& LoadBalancingPolicy::UpdateArgs::operator=(
84     UpdateArgs&& other) noexcept {
85   addresses = std::move(other.addresses);
86   config = std::move(other.config);
87   // TODO(roth): Use std::move() once channel args is converted to C++.
88   grpc_channel_args_destroy(args);
89   args = other.args;
90   other.args = nullptr;
91   return *this;
92 }
93 
94 //
95 // LoadBalancingPolicy::QueuePicker
96 //
97 
Pick(PickArgs)98 LoadBalancingPolicy::PickResult LoadBalancingPolicy::QueuePicker::Pick(
99     PickArgs /*args*/) {
100   // We invoke the parent's ExitIdleLocked() via a closure instead
101   // of doing it directly here, for two reasons:
102   // 1. ExitIdleLocked() may cause the policy's state to change and
103   //    a new picker to be delivered to the channel.  If that new
104   //    picker is delivered before ExitIdleLocked() returns, then by
105   //    the time this function returns, the pick will already have
106   //    been processed, and we'll be trying to re-process the same
107   //    pick again, leading to a crash.
108   // 2. We are currently running in the data plane mutex, but we
109   //    need to bounce into the control plane work_serializer to call
110   //    ExitIdleLocked().
111   if (!exit_idle_called_ && parent_ != nullptr) {
112     exit_idle_called_ = true;
113     auto* parent = parent_->Ref().release();  // ref held by lambda.
114     ExecCtx::Run(DEBUG_LOCATION,
115                  GRPC_CLOSURE_CREATE(
116                      [](void* arg, grpc_error_handle /*error*/) {
117                        auto* parent = static_cast<LoadBalancingPolicy*>(arg);
118                        parent->work_serializer()->Run(
119                            [parent]() {
120                              parent->ExitIdleLocked();
121                              parent->Unref();
122                            },
123                            DEBUG_LOCATION);
124                      },
125                      parent, nullptr),
126                  GRPC_ERROR_NONE);
127   }
128   return PickResult::Queue();
129 }
130 
131 }  // namespace grpc_core
132