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 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H
20 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H
21
22 #include <grpc/support/port_platform.h>
23
24 #include <string.h>
25
26 #include <grpc/support/alloc.h>
27
28 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
29 #include "src/core/ext/filters/client_channel/subchannel.h"
30 #include "src/core/lib/channel/channel_args.h"
31 #include "src/core/lib/debug/trace.h"
32 #include "src/core/lib/gprpp/abstract.h"
33 #include "src/core/lib/gprpp/inlined_vector.h"
34 #include "src/core/lib/gprpp/orphanable.h"
35 #include "src/core/lib/gprpp/ref_counted.h"
36 #include "src/core/lib/gprpp/ref_counted_ptr.h"
37 #include "src/core/lib/iomgr/closure.h"
38 #include "src/core/lib/iomgr/combiner.h"
39 #include "src/core/lib/iomgr/sockaddr_utils.h"
40 #include "src/core/lib/transport/connectivity_state.h"
41
42 // Code for maintaining a list of subchannels within an LB policy.
43 //
44 // To use this, callers must create their own subclasses, like so:
45 /*
46
47 class MySubchannelList; // Forward declaration.
48
49 class MySubchannelData
50 : public SubchannelData<MySubchannelList, MySubchannelData> {
51 public:
52 void ProcessConnectivityChangeLocked(
53 grpc_connectivity_state connectivity_state, grpc_error* error) override {
54 // ...code to handle connectivity changes...
55 }
56 };
57
58 class MySubchannelList
59 : public SubchannelList<MySubchannelList, MySubchannelData> {
60 };
61
62 */
63 // All methods with a Locked() suffix must be called from within the
64 // client_channel combiner.
65
66 namespace grpc_core {
67
68 // Forward declaration.
69 template <typename SubchannelListType, typename SubchannelDataType>
70 class SubchannelList;
71
72 // Stores data for a particular subchannel in a subchannel list.
73 // Callers must create a subclass that implements the
74 // ProcessConnectivityChangeLocked() method.
75 template <typename SubchannelListType, typename SubchannelDataType>
76 class SubchannelData {
77 public:
78 // Returns a pointer to the subchannel list containing this object.
subchannel_list()79 SubchannelListType* subchannel_list() const {
80 return static_cast<SubchannelListType*>(subchannel_list_);
81 }
82
83 // Returns the index into the subchannel list of this object.
Index()84 size_t Index() const {
85 return static_cast<size_t>(static_cast<const SubchannelDataType*>(this) -
86 subchannel_list_->subchannel(0));
87 }
88
89 // Returns a pointer to the subchannel.
subchannel()90 grpc_subchannel* subchannel() const { return subchannel_; }
91
92 // Returns the connected subchannel. Will be null if the subchannel
93 // is not connected.
connected_subchannel()94 ConnectedSubchannel* connected_subchannel() const {
95 return connected_subchannel_.get();
96 }
97
98 // Synchronously checks the subchannel's connectivity state.
99 // Must not be called while there is a connectivity notification
100 // pending (i.e., between calling StartConnectivityWatchLocked() or
101 // RenewConnectivityWatchLocked() and the resulting invocation of
102 // ProcessConnectivityChangeLocked()).
CheckConnectivityStateLocked(grpc_error ** error)103 grpc_connectivity_state CheckConnectivityStateLocked(grpc_error** error) {
104 GPR_ASSERT(!connectivity_notification_pending_);
105 pending_connectivity_state_unsafe_ =
106 grpc_subchannel_check_connectivity(subchannel(), error);
107 UpdateConnectedSubchannelLocked();
108 return pending_connectivity_state_unsafe_;
109 }
110
111 // Resets the connection backoff.
112 // TODO(roth): This method should go away when we move the backoff
113 // code out of the subchannel and into the LB policies.
114 void ResetBackoffLocked();
115
116 // Starts watching the connectivity state of the subchannel.
117 // ProcessConnectivityChangeLocked() will be called when the
118 // connectivity state changes.
119 void StartConnectivityWatchLocked();
120
121 // Renews watching the connectivity state of the subchannel.
122 void RenewConnectivityWatchLocked();
123
124 // Stops watching the connectivity state of the subchannel.
125 void StopConnectivityWatchLocked();
126
127 // Cancels watching the connectivity state of the subchannel.
128 // Must be called only while there is a connectivity notification
129 // pending (i.e., between calling StartConnectivityWatchLocked() or
130 // RenewConnectivityWatchLocked() and the resulting invocation of
131 // ProcessConnectivityChangeLocked()).
132 // From within ProcessConnectivityChangeLocked(), use
133 // StopConnectivityWatchLocked() instead.
134 void CancelConnectivityWatchLocked(const char* reason);
135
136 // Cancels any pending connectivity watch and unrefs the subchannel.
137 void ShutdownLocked();
138
139 GRPC_ABSTRACT_BASE_CLASS
140
141 protected:
142 SubchannelData(
143 SubchannelList<SubchannelListType, SubchannelDataType>* subchannel_list,
144 const grpc_lb_user_data_vtable* user_data_vtable,
145 const grpc_lb_address& address, grpc_subchannel* subchannel,
146 grpc_combiner* combiner);
147
148 virtual ~SubchannelData();
149
150 // After StartConnectivityWatchLocked() or RenewConnectivityWatchLocked()
151 // is called, this method will be invoked when the subchannel's connectivity
152 // state changes.
153 // Implementations must invoke either RenewConnectivityWatchLocked() or
154 // StopConnectivityWatchLocked() before returning.
155 virtual void ProcessConnectivityChangeLocked(
156 grpc_connectivity_state connectivity_state,
157 grpc_error* error) GRPC_ABSTRACT;
158
159 // Unrefs the subchannel. May be overridden by subclasses that need
160 // to perform extra cleanup when unreffing the subchannel.
161 virtual void UnrefSubchannelLocked(const char* reason);
162
163 private:
164 // Updates connected_subchannel_ based on pending_connectivity_state_unsafe_.
165 // Returns true if the connectivity state should be reported.
166 bool UpdateConnectedSubchannelLocked();
167
168 static void OnConnectivityChangedLocked(void* arg, grpc_error* error);
169
170 // Backpointer to owning subchannel list. Not owned.
171 SubchannelList<SubchannelListType, SubchannelDataType>* subchannel_list_;
172
173 // The subchannel and connected subchannel.
174 grpc_subchannel* subchannel_;
175 RefCountedPtr<ConnectedSubchannel> connected_subchannel_;
176
177 // Notification that connectivity has changed on subchannel.
178 grpc_closure connectivity_changed_closure_;
179 // Is a connectivity notification pending?
180 bool connectivity_notification_pending_ = false;
181 // Connectivity state to be updated by
182 // grpc_subchannel_notify_on_state_change(), not guarded by
183 // the combiner.
184 grpc_connectivity_state pending_connectivity_state_unsafe_;
185 };
186
187 // A list of subchannels.
188 template <typename SubchannelListType, typename SubchannelDataType>
189 class SubchannelList
190 : public InternallyRefCountedWithTracing<SubchannelListType> {
191 public:
192 typedef InlinedVector<SubchannelDataType, 10> SubchannelVector;
193
194 // The number of subchannels in the list.
num_subchannels()195 size_t num_subchannels() const { return subchannels_.size(); }
196
197 // The data for the subchannel at a particular index.
subchannel(size_t index)198 SubchannelDataType* subchannel(size_t index) { return &subchannels_[index]; }
199
200 // Returns true if the subchannel list is shutting down.
shutting_down()201 bool shutting_down() const { return shutting_down_; }
202
203 // Populates refs_list with the uuids of this SubchannelLists's subchannels.
PopulateChildRefsList(ChildRefsList * refs_list)204 void PopulateChildRefsList(ChildRefsList* refs_list) {
205 for (size_t i = 0; i < subchannels_.size(); ++i) {
206 if (subchannels_[i].subchannel() != nullptr) {
207 grpc_core::channelz::SubchannelNode* subchannel_node =
208 grpc_subchannel_get_channelz_node(subchannels_[i].subchannel());
209 if (subchannel_node != nullptr) {
210 refs_list->push_back(subchannel_node->uuid());
211 }
212 }
213 }
214 }
215
216 // Accessors.
policy()217 LoadBalancingPolicy* policy() const { return policy_; }
tracer()218 TraceFlag* tracer() const { return tracer_; }
219
220 // Resets connection backoff of all subchannels.
221 // TODO(roth): We will probably need to rethink this as part of moving
222 // the backoff code out of subchannels and into LB policies.
223 void ResetBackoffLocked();
224
225 // Note: Caller must ensure that this is invoked inside of the combiner.
Orphan()226 void Orphan() override {
227 ShutdownLocked();
228 InternallyRefCountedWithTracing<SubchannelListType>::Unref(DEBUG_LOCATION,
229 "shutdown");
230 }
231
232 GRPC_ABSTRACT_BASE_CLASS
233
234 protected:
235 SubchannelList(LoadBalancingPolicy* policy, TraceFlag* tracer,
236 const grpc_lb_addresses* addresses, grpc_combiner* combiner,
237 grpc_client_channel_factory* client_channel_factory,
238 const grpc_channel_args& args);
239
240 virtual ~SubchannelList();
241
242 private:
243 // So New() can call our private ctor.
244 template <typename T, typename... Args>
245 friend T* New(Args&&... args);
246
247 // For accessing Ref() and Unref().
248 friend class SubchannelData<SubchannelListType, SubchannelDataType>;
249
250 void ShutdownLocked();
251
252 // Backpointer to owning policy.
253 LoadBalancingPolicy* policy_;
254
255 TraceFlag* tracer_;
256
257 grpc_combiner* combiner_;
258
259 // The list of subchannels.
260 SubchannelVector subchannels_;
261
262 // Is this list shutting down? This may be true due to the shutdown of the
263 // policy itself or because a newer update has arrived while this one hadn't
264 // finished processing.
265 bool shutting_down_ = false;
266 };
267
268 //
269 // implementation -- no user-servicable parts below
270 //
271
272 //
273 // SubchannelData
274 //
275
276 template <typename SubchannelListType, typename SubchannelDataType>
SubchannelData(SubchannelList<SubchannelListType,SubchannelDataType> * subchannel_list,const grpc_lb_user_data_vtable * user_data_vtable,const grpc_lb_address & address,grpc_subchannel * subchannel,grpc_combiner * combiner)277 SubchannelData<SubchannelListType, SubchannelDataType>::SubchannelData(
278 SubchannelList<SubchannelListType, SubchannelDataType>* subchannel_list,
279 const grpc_lb_user_data_vtable* user_data_vtable,
280 const grpc_lb_address& address, grpc_subchannel* subchannel,
281 grpc_combiner* combiner)
282 : subchannel_list_(subchannel_list),
283 subchannel_(subchannel),
284 // We assume that the current state is IDLE. If not, we'll get a
285 // callback telling us that.
286 pending_connectivity_state_unsafe_(GRPC_CHANNEL_IDLE) {
287 GRPC_CLOSURE_INIT(
288 &connectivity_changed_closure_,
289 (&SubchannelData<SubchannelListType,
290 SubchannelDataType>::OnConnectivityChangedLocked),
291 this, grpc_combiner_scheduler(combiner));
292 }
293
294 template <typename SubchannelListType, typename SubchannelDataType>
~SubchannelData()295 SubchannelData<SubchannelListType, SubchannelDataType>::~SubchannelData() {
296 UnrefSubchannelLocked("subchannel_data_destroy");
297 }
298
299 template <typename SubchannelListType, typename SubchannelDataType>
300 void SubchannelData<SubchannelListType, SubchannelDataType>::
UnrefSubchannelLocked(const char * reason)301 UnrefSubchannelLocked(const char* reason) {
302 if (subchannel_ != nullptr) {
303 if (subchannel_list_->tracer()->enabled()) {
304 gpr_log(GPR_INFO,
305 "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
306 " (subchannel %p): unreffing subchannel",
307 subchannel_list_->tracer()->name(), subchannel_list_->policy(),
308 subchannel_list_, Index(), subchannel_list_->num_subchannels(),
309 subchannel_);
310 }
311 GRPC_SUBCHANNEL_UNREF(subchannel_, reason);
312 subchannel_ = nullptr;
313 connected_subchannel_.reset();
314 }
315 }
316
317 template <typename SubchannelListType, typename SubchannelDataType>
318 void SubchannelData<SubchannelListType,
ResetBackoffLocked()319 SubchannelDataType>::ResetBackoffLocked() {
320 if (subchannel_ != nullptr) {
321 grpc_subchannel_reset_backoff(subchannel_);
322 }
323 }
324
325 template <typename SubchannelListType, typename SubchannelDataType>
326 void SubchannelData<SubchannelListType,
StartConnectivityWatchLocked()327 SubchannelDataType>::StartConnectivityWatchLocked() {
328 if (subchannel_list_->tracer()->enabled()) {
329 gpr_log(GPR_INFO,
330 "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
331 " (subchannel %p): starting watch: requesting connectivity change "
332 "notification (from %s)",
333 subchannel_list_->tracer()->name(), subchannel_list_->policy(),
334 subchannel_list_, Index(), subchannel_list_->num_subchannels(),
335 subchannel_,
336 grpc_connectivity_state_name(pending_connectivity_state_unsafe_));
337 }
338 GPR_ASSERT(!connectivity_notification_pending_);
339 connectivity_notification_pending_ = true;
340 subchannel_list()->Ref(DEBUG_LOCATION, "connectivity_watch").release();
341 grpc_subchannel_notify_on_state_change(
342 subchannel_, subchannel_list_->policy()->interested_parties(),
343 &pending_connectivity_state_unsafe_, &connectivity_changed_closure_);
344 }
345
346 template <typename SubchannelListType, typename SubchannelDataType>
347 void SubchannelData<SubchannelListType,
RenewConnectivityWatchLocked()348 SubchannelDataType>::RenewConnectivityWatchLocked() {
349 if (subchannel_list_->tracer()->enabled()) {
350 gpr_log(GPR_INFO,
351 "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
352 " (subchannel %p): renewing watch: requesting connectivity change "
353 "notification (from %s)",
354 subchannel_list_->tracer()->name(), subchannel_list_->policy(),
355 subchannel_list_, Index(), subchannel_list_->num_subchannels(),
356 subchannel_,
357 grpc_connectivity_state_name(pending_connectivity_state_unsafe_));
358 }
359 GPR_ASSERT(connectivity_notification_pending_);
360 grpc_subchannel_notify_on_state_change(
361 subchannel_, subchannel_list_->policy()->interested_parties(),
362 &pending_connectivity_state_unsafe_, &connectivity_changed_closure_);
363 }
364
365 template <typename SubchannelListType, typename SubchannelDataType>
366 void SubchannelData<SubchannelListType,
StopConnectivityWatchLocked()367 SubchannelDataType>::StopConnectivityWatchLocked() {
368 if (subchannel_list_->tracer()->enabled()) {
369 gpr_log(GPR_INFO,
370 "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
371 " (subchannel %p): stopping connectivity watch",
372 subchannel_list_->tracer()->name(), subchannel_list_->policy(),
373 subchannel_list_, Index(), subchannel_list_->num_subchannels(),
374 subchannel_);
375 }
376 GPR_ASSERT(connectivity_notification_pending_);
377 connectivity_notification_pending_ = false;
378 subchannel_list()->Unref(DEBUG_LOCATION, "connectivity_watch");
379 }
380
381 template <typename SubchannelListType, typename SubchannelDataType>
382 void SubchannelData<SubchannelListType, SubchannelDataType>::
CancelConnectivityWatchLocked(const char * reason)383 CancelConnectivityWatchLocked(const char* reason) {
384 if (subchannel_list_->tracer()->enabled()) {
385 gpr_log(GPR_INFO,
386 "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
387 " (subchannel %p): canceling connectivity watch (%s)",
388 subchannel_list_->tracer()->name(), subchannel_list_->policy(),
389 subchannel_list_, Index(), subchannel_list_->num_subchannels(),
390 subchannel_, reason);
391 }
392 GPR_ASSERT(connectivity_notification_pending_);
393 grpc_subchannel_notify_on_state_change(subchannel_, nullptr, nullptr,
394 &connectivity_changed_closure_);
395 }
396
397 template <typename SubchannelListType, typename SubchannelDataType>
398 bool SubchannelData<SubchannelListType,
UpdateConnectedSubchannelLocked()399 SubchannelDataType>::UpdateConnectedSubchannelLocked() {
400 // If the subchannel is READY, take a ref to the connected subchannel.
401 if (pending_connectivity_state_unsafe_ == GRPC_CHANNEL_READY) {
402 connected_subchannel_ =
403 grpc_subchannel_get_connected_subchannel(subchannel_);
404 // If the subchannel became disconnected between the time that READY
405 // was reported and the time we got here (e.g., between when a
406 // notification callback is scheduled and when it was actually run in
407 // the combiner), then the connected subchannel may have disappeared out
408 // from under us. In that case, we don't actually want to consider the
409 // subchannel to be in state READY. Instead, we use IDLE as the
410 // basis for any future connectivity watch; this is the one state that
411 // the subchannel will never transition back into, so this ensures
412 // that we will get a notification for the next state, even if that state
413 // is READY again (e.g., if the subchannel has transitioned back to
414 // READY before the next watch gets requested).
415 if (connected_subchannel_ == nullptr) {
416 if (subchannel_list_->tracer()->enabled()) {
417 gpr_log(GPR_INFO,
418 "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
419 " (subchannel %p): state is READY but connected subchannel is "
420 "null; moving to state IDLE",
421 subchannel_list_->tracer()->name(), subchannel_list_->policy(),
422 subchannel_list_, Index(), subchannel_list_->num_subchannels(),
423 subchannel_);
424 }
425 pending_connectivity_state_unsafe_ = GRPC_CHANNEL_IDLE;
426 return false;
427 }
428 } else {
429 // For any state other than READY, unref the connected subchannel.
430 connected_subchannel_.reset();
431 }
432 return true;
433 }
434
435 template <typename SubchannelListType, typename SubchannelDataType>
436 void SubchannelData<SubchannelListType, SubchannelDataType>::
OnConnectivityChangedLocked(void * arg,grpc_error * error)437 OnConnectivityChangedLocked(void* arg, grpc_error* error) {
438 SubchannelData* sd = static_cast<SubchannelData*>(arg);
439 if (sd->subchannel_list_->tracer()->enabled()) {
440 gpr_log(
441 GPR_INFO,
442 "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
443 " (subchannel %p): connectivity changed: state=%s, error=%s, "
444 "shutting_down=%d",
445 sd->subchannel_list_->tracer()->name(), sd->subchannel_list_->policy(),
446 sd->subchannel_list_, sd->Index(),
447 sd->subchannel_list_->num_subchannels(), sd->subchannel_,
448 grpc_connectivity_state_name(sd->pending_connectivity_state_unsafe_),
449 grpc_error_string(error), sd->subchannel_list_->shutting_down());
450 }
451 // If shutting down, unref subchannel and stop watching.
452 if (sd->subchannel_list_->shutting_down() || error == GRPC_ERROR_CANCELLED) {
453 sd->UnrefSubchannelLocked("connectivity_shutdown");
454 sd->StopConnectivityWatchLocked();
455 return;
456 }
457 // Get or release ref to connected subchannel.
458 if (!sd->UpdateConnectedSubchannelLocked()) {
459 // We don't want to report this connectivity state, so renew the watch.
460 sd->RenewConnectivityWatchLocked();
461 return;
462 }
463 // Call the subclass's ProcessConnectivityChangeLocked() method.
464 sd->ProcessConnectivityChangeLocked(sd->pending_connectivity_state_unsafe_,
465 GRPC_ERROR_REF(error));
466 }
467
468 template <typename SubchannelListType, typename SubchannelDataType>
ShutdownLocked()469 void SubchannelData<SubchannelListType, SubchannelDataType>::ShutdownLocked() {
470 // If there's a pending notification for this subchannel, cancel it;
471 // the callback is responsible for unreffing the subchannel.
472 // Otherwise, unref the subchannel directly.
473 if (connectivity_notification_pending_) {
474 CancelConnectivityWatchLocked("shutdown");
475 } else if (subchannel_ != nullptr) {
476 UnrefSubchannelLocked("shutdown");
477 }
478 }
479
480 //
481 // SubchannelList
482 //
483
484 template <typename SubchannelListType, typename SubchannelDataType>
SubchannelList(LoadBalancingPolicy * policy,TraceFlag * tracer,const grpc_lb_addresses * addresses,grpc_combiner * combiner,grpc_client_channel_factory * client_channel_factory,const grpc_channel_args & args)485 SubchannelList<SubchannelListType, SubchannelDataType>::SubchannelList(
486 LoadBalancingPolicy* policy, TraceFlag* tracer,
487 const grpc_lb_addresses* addresses, grpc_combiner* combiner,
488 grpc_client_channel_factory* client_channel_factory,
489 const grpc_channel_args& args)
490 : InternallyRefCountedWithTracing<SubchannelListType>(tracer),
491 policy_(policy),
492 tracer_(tracer),
493 combiner_(GRPC_COMBINER_REF(combiner, "subchannel_list")) {
494 if (tracer_->enabled()) {
495 gpr_log(GPR_INFO,
496 "[%s %p] Creating subchannel list %p for %" PRIuPTR " subchannels",
497 tracer_->name(), policy, this, addresses->num_addresses);
498 }
499 subchannels_.reserve(addresses->num_addresses);
500 // We need to remove the LB addresses in order to be able to compare the
501 // subchannel keys of subchannels from a different batch of addresses.
502 static const char* keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS,
503 GRPC_ARG_LB_ADDRESSES};
504 // Create a subchannel for each address.
505 grpc_subchannel_args sc_args;
506 for (size_t i = 0; i < addresses->num_addresses; i++) {
507 // If there were any balancer, we would have chosen grpclb policy instead.
508 GPR_ASSERT(!addresses->addresses[i].is_balancer);
509 memset(&sc_args, 0, sizeof(grpc_subchannel_args));
510 grpc_arg addr_arg =
511 grpc_create_subchannel_address_arg(&addresses->addresses[i].address);
512 grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove(
513 &args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &addr_arg, 1);
514 gpr_free(addr_arg.value.string);
515 sc_args.args = new_args;
516 grpc_subchannel* subchannel = grpc_client_channel_factory_create_subchannel(
517 client_channel_factory, &sc_args);
518 grpc_channel_args_destroy(new_args);
519 if (subchannel == nullptr) {
520 // Subchannel could not be created.
521 if (tracer_->enabled()) {
522 char* address_uri =
523 grpc_sockaddr_to_uri(&addresses->addresses[i].address);
524 gpr_log(GPR_INFO,
525 "[%s %p] could not create subchannel for address uri %s, "
526 "ignoring",
527 tracer_->name(), policy_, address_uri);
528 gpr_free(address_uri);
529 }
530 continue;
531 }
532 if (tracer_->enabled()) {
533 char* address_uri =
534 grpc_sockaddr_to_uri(&addresses->addresses[i].address);
535 gpr_log(GPR_INFO,
536 "[%s %p] subchannel list %p index %" PRIuPTR
537 ": Created subchannel %p for address uri %s",
538 tracer_->name(), policy_, this, subchannels_.size(), subchannel,
539 address_uri);
540 gpr_free(address_uri);
541 }
542 subchannels_.emplace_back(this, addresses->user_data_vtable,
543 addresses->addresses[i], subchannel, combiner);
544 }
545 }
546
547 template <typename SubchannelListType, typename SubchannelDataType>
~SubchannelList()548 SubchannelList<SubchannelListType, SubchannelDataType>::~SubchannelList() {
549 if (tracer_->enabled()) {
550 gpr_log(GPR_INFO, "[%s %p] Destroying subchannel_list %p", tracer_->name(),
551 policy_, this);
552 }
553 GRPC_COMBINER_UNREF(combiner_, "subchannel_list");
554 }
555
556 template <typename SubchannelListType, typename SubchannelDataType>
ShutdownLocked()557 void SubchannelList<SubchannelListType, SubchannelDataType>::ShutdownLocked() {
558 if (tracer_->enabled()) {
559 gpr_log(GPR_INFO, "[%s %p] Shutting down subchannel_list %p",
560 tracer_->name(), policy_, this);
561 }
562 GPR_ASSERT(!shutting_down_);
563 shutting_down_ = true;
564 for (size_t i = 0; i < subchannels_.size(); i++) {
565 SubchannelDataType* sd = &subchannels_[i];
566 sd->ShutdownLocked();
567 }
568 }
569
570 template <typename SubchannelListType, typename SubchannelDataType>
571 void SubchannelList<SubchannelListType,
ResetBackoffLocked()572 SubchannelDataType>::ResetBackoffLocked() {
573 for (size_t i = 0; i < subchannels_.size(); i++) {
574 SubchannelDataType* sd = &subchannels_[i];
575 sd->ResetBackoffLocked();
576 }
577 }
578
579 } // namespace grpc_core
580
581 #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H */
582