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