|  | @@ -37,6 +37,36 @@ namespace grpc_core {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /// Interface for load balancing policies.
 | 
	
		
			
				|  |  |  ///
 | 
	
		
			
				|  |  | +/// The following concepts are used here:
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// Channel: An abstraction that manages connections to backend servers
 | 
	
		
			
				|  |  | +///   on behalf of a client application.  The application creates a channel
 | 
	
		
			
				|  |  | +///   for a given server name and then sends RPCs on it, and the channel
 | 
	
		
			
				|  |  | +///   figures out which backend server to send each RPC to.  A channel
 | 
	
		
			
				|  |  | +///   contains a resolver, a load balancing policy (or a tree of LB policies),
 | 
	
		
			
				|  |  | +///   and a set of one or more subchannels.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// Subchannel: A subchannel represents a connection to one backend server.
 | 
	
		
			
				|  |  | +///   The LB policy decides which subchannels to create, manages the
 | 
	
		
			
				|  |  | +///   connectivity state of those subchannels, and decides which subchannel
 | 
	
		
			
				|  |  | +///   to send any given RPC to.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// Resolver: A plugin that takes a gRPC server URI and resolves it to a
 | 
	
		
			
				|  |  | +///   list of one or more addresses and a service config, as described
 | 
	
		
			
				|  |  | +///   in https://github.com/grpc/grpc/blob/master/doc/naming.md.  See
 | 
	
		
			
				|  |  | +///   resolver.h for the resolver API.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// Load Balancing (LB) Policy: A plugin that takes a list of addresses
 | 
	
		
			
				|  |  | +///   from the resolver, maintains and manages a subchannel for each
 | 
	
		
			
				|  |  | +///   backend address, and decides which subchannel to send each RPC on.
 | 
	
		
			
				|  |  | +///   An LB policy has two parts:
 | 
	
		
			
				|  |  | +///   - A LoadBalancingPolicy, which deals with the control plane work of
 | 
	
		
			
				|  |  | +///     managing subchannels.
 | 
	
		
			
				|  |  | +///   - A SubchannelPicker, which handles the data plane work of
 | 
	
		
			
				|  |  | +///     determining which subchannel a given RPC should be sent on.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/// LoadBalacingPolicy API.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  |  /// Note: All methods with a "Locked" suffix must be called from the
 | 
	
		
			
				|  |  |  /// combiner passed to the constructor.
 | 
	
		
			
				|  |  |  ///
 | 
	
	
		
			
				|  | @@ -46,36 +76,70 @@ namespace grpc_core {
 | 
	
		
			
				|  |  |  // interested_parties() hooks from the API.
 | 
	
		
			
				|  |  |  class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 | 
	
		
			
				|  |  |   public:
 | 
	
		
			
				|  |  | -  /// State used for an LB pick.
 | 
	
		
			
				|  |  | -  struct PickState {
 | 
	
		
			
				|  |  | +  /// Arguments used when picking a subchannel for an RPC.
 | 
	
		
			
				|  |  | +  struct PickArgs {
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// Input parameters.
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  |      /// Initial metadata associated with the picking call.
 | 
	
		
			
				|  |  | -    /// This is both an input and output parameter; the LB policy may
 | 
	
		
			
				|  |  | -    /// use metadata here to influence its routing decision, and it may
 | 
	
		
			
				|  |  | -    /// add new metadata here to be sent with the call to the chosen backend.
 | 
	
		
			
				|  |  | +    /// The LB policy may use the existing metadata to influence its routing
 | 
	
		
			
				|  |  | +    /// decision, and it may add new metadata elements to be sent with the
 | 
	
		
			
				|  |  | +    /// call to the chosen backend.
 | 
	
		
			
				|  |  | +    // TODO(roth): Provide a more generic metadata API here.
 | 
	
		
			
				|  |  |      grpc_metadata_batch* initial_metadata = nullptr;
 | 
	
		
			
				|  |  |      /// Storage for LB token in \a initial_metadata, or nullptr if not used.
 | 
	
		
			
				|  |  |      // TODO(roth): Remove this from the API.  Maybe have the LB policy
 | 
	
		
			
				|  |  |      // allocate this on the arena instead?
 | 
	
		
			
				|  |  |      grpc_linked_mdelem lb_token_mdelem_storage;
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// Output parameters.
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// Will be set to the selected subchannel, or nullptr on failure or when
 | 
	
		
			
				|  |  | +    /// the LB policy decides to drop the call.
 | 
	
		
			
				|  |  | +    RefCountedPtr<ConnectedSubchannel> connected_subchannel;
 | 
	
		
			
				|  |  |      /// Callback set by lb policy to be notified of trailing metadata.
 | 
	
		
			
				|  |  |      /// The callback must be scheduled on grpc_schedule_on_exec_ctx.
 | 
	
		
			
				|  |  | +    // TODO(roth): Provide a cleaner callback API.
 | 
	
		
			
				|  |  |      grpc_closure* recv_trailing_metadata_ready = nullptr;
 | 
	
		
			
				|  |  |      /// The address that will be set to point to the original
 | 
	
		
			
				|  |  |      /// recv_trailing_metadata_ready callback, to be invoked by the LB
 | 
	
		
			
				|  |  |      /// policy's recv_trailing_metadata_ready callback when complete.
 | 
	
		
			
				|  |  |      /// Must be non-null if recv_trailing_metadata_ready is non-null.
 | 
	
		
			
				|  |  | +    // TODO(roth): Consider making the recv_trailing_metadata closure a
 | 
	
		
			
				|  |  | +    // synchronous callback, in which case it is not responsible for
 | 
	
		
			
				|  |  | +    // chaining to the next callback, so this can be removed from the API.
 | 
	
		
			
				|  |  |      grpc_closure** original_recv_trailing_metadata_ready = nullptr;
 | 
	
		
			
				|  |  |      /// If this is not nullptr, then the client channel will point it to the
 | 
	
		
			
				|  |  |      /// call's trailing metadata before invoking recv_trailing_metadata_ready.
 | 
	
		
			
				|  |  |      /// If this is nullptr, then the callback will still be called.
 | 
	
		
			
				|  |  |      /// The lb does not have ownership of the metadata.
 | 
	
		
			
				|  |  | +    // TODO(roth): If we make this a synchronous callback, then this can
 | 
	
		
			
				|  |  | +    // be passed to the callback as a parameter and can be removed from
 | 
	
		
			
				|  |  | +    // the API here.
 | 
	
		
			
				|  |  |      grpc_metadata_batch** recv_trailing_metadata = nullptr;
 | 
	
		
			
				|  |  | -    /// Will be set to the selected subchannel, or nullptr on failure or when
 | 
	
		
			
				|  |  | -    /// the LB policy decides to drop the call.
 | 
	
		
			
				|  |  | -    RefCountedPtr<ConnectedSubchannel> connected_subchannel;
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /// A picker is the object used to actual perform picks.
 | 
	
		
			
				|  |  | +  /// The result of picking a subchannel for an RPC.
 | 
	
		
			
				|  |  | +  enum PickResult {
 | 
	
		
			
				|  |  | +    // Pick complete.  If connected_subchannel is non-null, client channel
 | 
	
		
			
				|  |  | +    // can immediately proceed with the call on connected_subchannel;
 | 
	
		
			
				|  |  | +    // otherwise, call should be dropped.
 | 
	
		
			
				|  |  | +    PICK_COMPLETE,
 | 
	
		
			
				|  |  | +    // Pick cannot be completed until something changes on the control
 | 
	
		
			
				|  |  | +    // plane.  Client channel will queue the pick and try again the
 | 
	
		
			
				|  |  | +    // next time the picker is updated.
 | 
	
		
			
				|  |  | +    PICK_QUEUE,
 | 
	
		
			
				|  |  | +    // LB policy is in transient failure.  If the pick is wait_for_ready,
 | 
	
		
			
				|  |  | +    // client channel will wait for the next picker and try again;
 | 
	
		
			
				|  |  | +    // otherwise, the call will be failed immediately (although it may
 | 
	
		
			
				|  |  | +    // be retried if the client channel is configured to do so).
 | 
	
		
			
				|  |  | +    // The Pick() method will set its error parameter if this value is
 | 
	
		
			
				|  |  | +    // returned.
 | 
	
		
			
				|  |  | +    PICK_TRANSIENT_FAILURE,
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// A subchannel picker is the object used to pick the subchannel to
 | 
	
		
			
				|  |  | +  /// use for a given RPC.
 | 
	
		
			
				|  |  |    ///
 | 
	
		
			
				|  |  |    /// Pickers are intended to encapsulate all of the state and logic
 | 
	
		
			
				|  |  |    /// needed on the data plane (i.e., to actually process picks for
 | 
	
	
		
			
				|  | @@ -92,90 +156,14 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 | 
	
		
			
				|  |  |    // synchronization mechanisms, to avoid lock contention between the two.
 | 
	
		
			
				|  |  |    class SubchannelPicker {
 | 
	
		
			
				|  |  |     public:
 | 
	
		
			
				|  |  | -    enum PickResult {
 | 
	
		
			
				|  |  | -      // Pick complete.  If connected_subchannel is non-null, client channel
 | 
	
		
			
				|  |  | -      // can immediately proceed with the call on connected_subchannel;
 | 
	
		
			
				|  |  | -      // otherwise, call should be dropped.
 | 
	
		
			
				|  |  | -      PICK_COMPLETE,
 | 
	
		
			
				|  |  | -      // Pick cannot be completed until something changes on the control
 | 
	
		
			
				|  |  | -      // plane.  Client channel will queue the pick and try again the
 | 
	
		
			
				|  |  | -      // next time the picker is updated.
 | 
	
		
			
				|  |  | -      PICK_QUEUE,
 | 
	
		
			
				|  |  | -      // LB policy is in transient failure.  If the pick is wait_for_ready,
 | 
	
		
			
				|  |  | -      // client channel will wait for the next picker and try again;
 | 
	
		
			
				|  |  | -      // otherwise, the call will be failed immediately (although it may
 | 
	
		
			
				|  |  | -      // be retried if the client channel is configured to do so).
 | 
	
		
			
				|  |  | -      // The Pick() method will set its error parameter if this value is
 | 
	
		
			
				|  |  | -      // returned.
 | 
	
		
			
				|  |  | -      PICK_TRANSIENT_FAILURE,
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |      SubchannelPicker() = default;
 | 
	
		
			
				|  |  |      virtual ~SubchannelPicker() = default;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    virtual PickResult Pick(PickState* pick, grpc_error** error) GRPC_ABSTRACT;
 | 
	
		
			
				|  |  | +    virtual PickResult Pick(PickArgs* pick, grpc_error** error) GRPC_ABSTRACT;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      GRPC_ABSTRACT_BASE_CLASS
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // A picker that returns PICK_QUEUE for all picks.
 | 
	
		
			
				|  |  | -  // Also calls the parent LB policy's ExitIdleLocked() method when the
 | 
	
		
			
				|  |  | -  // first pick is seen.
 | 
	
		
			
				|  |  | -  class QueuePicker : public SubchannelPicker {
 | 
	
		
			
				|  |  | -   public:
 | 
	
		
			
				|  |  | -    explicit QueuePicker(RefCountedPtr<LoadBalancingPolicy> parent)
 | 
	
		
			
				|  |  | -        : parent_(std::move(parent)) {}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    PickResult Pick(PickState* pick, grpc_error** error) override {
 | 
	
		
			
				|  |  | -      // We invoke the parent's ExitIdleLocked() via a closure instead
 | 
	
		
			
				|  |  | -      // of doing it directly here, for two reasons:
 | 
	
		
			
				|  |  | -      // 1. ExitIdleLocked() may cause the policy's state to change and
 | 
	
		
			
				|  |  | -      //    a new picker to be delivered to the channel.  If that new
 | 
	
		
			
				|  |  | -      //    picker is delivered before ExitIdleLocked() returns, then by
 | 
	
		
			
				|  |  | -      //    the time this function returns, the pick will already have
 | 
	
		
			
				|  |  | -      //    been processed, and we'll be trying to re-process the same
 | 
	
		
			
				|  |  | -      //    pick again, leading to a crash.
 | 
	
		
			
				|  |  | -      // 2. In a subsequent PR, we will split the data plane and control
 | 
	
		
			
				|  |  | -      //    plane synchronization into separate combiners, at which
 | 
	
		
			
				|  |  | -      //    point this will need to hop from the data plane combiner into
 | 
	
		
			
				|  |  | -      //    the control plane combiner.
 | 
	
		
			
				|  |  | -      if (!exit_idle_called_) {
 | 
	
		
			
				|  |  | -        exit_idle_called_ = true;
 | 
	
		
			
				|  |  | -        parent_->Ref().release();  // ref held by closure.
 | 
	
		
			
				|  |  | -        GRPC_CLOSURE_SCHED(
 | 
	
		
			
				|  |  | -            GRPC_CLOSURE_CREATE(&CallExitIdle, parent_.get(),
 | 
	
		
			
				|  |  | -                                grpc_combiner_scheduler(parent_->combiner())),
 | 
	
		
			
				|  |  | -            GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      return PICK_QUEUE;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -   private:
 | 
	
		
			
				|  |  | -    static void CallExitIdle(void* arg, grpc_error* error) {
 | 
	
		
			
				|  |  | -      LoadBalancingPolicy* parent = static_cast<LoadBalancingPolicy*>(arg);
 | 
	
		
			
				|  |  | -      parent->ExitIdleLocked();
 | 
	
		
			
				|  |  | -      parent->Unref();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    RefCountedPtr<LoadBalancingPolicy> parent_;
 | 
	
		
			
				|  |  | -    bool exit_idle_called_ = false;
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // A picker that returns PICK_TRANSIENT_FAILURE for all picks.
 | 
	
		
			
				|  |  | -  class TransientFailurePicker : public SubchannelPicker {
 | 
	
		
			
				|  |  | -   public:
 | 
	
		
			
				|  |  | -    explicit TransientFailurePicker(grpc_error* error) : error_(error) {}
 | 
	
		
			
				|  |  | -    ~TransientFailurePicker() { GRPC_ERROR_UNREF(error_); }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    PickResult Pick(PickState* pick, grpc_error** error) override {
 | 
	
		
			
				|  |  | -      *error = GRPC_ERROR_REF(error_);
 | 
	
		
			
				|  |  | -      return PICK_TRANSIENT_FAILURE;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -   private:
 | 
	
		
			
				|  |  | -    grpc_error* error_;
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    /// A proxy object used by the LB policy to communicate with the client
 | 
	
		
			
				|  |  |    /// channel.
 | 
	
		
			
				|  |  |    class ChannelControlHelper {
 | 
	
	
		
			
				|  | @@ -188,6 +176,8 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 | 
	
		
			
				|  |  |          GRPC_ABSTRACT;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /// Creates a channel with the specified target and channel args.
 | 
	
		
			
				|  |  | +    /// This can be used in cases where the LB policy needs to create a
 | 
	
		
			
				|  |  | +    /// channel for its own use (e.g., to talk to an external load balancer).
 | 
	
		
			
				|  |  |      virtual grpc_channel* CreateChannel(
 | 
	
		
			
				|  |  |          const char* target, const grpc_channel_args& args) GRPC_ABSTRACT;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -203,7 +193,8 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 | 
	
		
			
				|  |  |      GRPC_ABSTRACT_BASE_CLASS
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // Configuration for an LB policy instance.
 | 
	
		
			
				|  |  | +  /// Configuration for an LB policy instance.
 | 
	
		
			
				|  |  | +  // TODO(roth): Find a better JSON representation for this API.
 | 
	
		
			
				|  |  |    class Config : public RefCounted<Config> {
 | 
	
		
			
				|  |  |     public:
 | 
	
		
			
				|  |  |      Config(const grpc_json* lb_config,
 | 
	
	
		
			
				|  | @@ -234,9 +225,13 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 | 
	
		
			
				|  |  |      /// their constructor.
 | 
	
		
			
				|  |  |      UniquePtr<ChannelControlHelper> channel_control_helper;
 | 
	
		
			
				|  |  |      /// Channel args.
 | 
	
		
			
				|  |  | +    // TODO(roth): Find a better channel args representation for this API.
 | 
	
		
			
				|  |  |      const grpc_channel_args* args = nullptr;
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  explicit LoadBalancingPolicy(Args args, intptr_t initial_refcount = 1);
 | 
	
		
			
				|  |  | +  virtual ~LoadBalancingPolicy();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    // Not copyable nor movable.
 | 
	
		
			
				|  |  |    LoadBalancingPolicy(const LoadBalancingPolicy&) = delete;
 | 
	
		
			
				|  |  |    LoadBalancingPolicy& operator=(const LoadBalancingPolicy&) = delete;
 | 
	
	
		
			
				|  | @@ -262,40 +257,62 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 | 
	
		
			
				|  |  |    virtual void ResetBackoffLocked() GRPC_ABSTRACT;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /// Populates child_subchannels and child_channels with the uuids of this
 | 
	
		
			
				|  |  | -  /// LB policy's referenced children. This is not invoked from the
 | 
	
		
			
				|  |  | -  /// client_channel's combiner. The implementation is responsible for
 | 
	
		
			
				|  |  | -  /// providing its own synchronization.
 | 
	
		
			
				|  |  | +  /// LB policy's referenced children.
 | 
	
		
			
				|  |  | +  ///
 | 
	
		
			
				|  |  | +  /// This is not invoked from the client_channel's combiner. The
 | 
	
		
			
				|  |  | +  /// implementation is responsible for providing its own synchronization.
 | 
	
		
			
				|  |  |    virtual void FillChildRefsForChannelz(
 | 
	
		
			
				|  |  |        channelz::ChildRefsList* child_subchannels,
 | 
	
		
			
				|  |  |        channelz::ChildRefsList* child_channels) GRPC_ABSTRACT;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  void Orphan() override {
 | 
	
		
			
				|  |  | -    // Invoke ShutdownAndUnrefLocked() inside of the combiner.
 | 
	
		
			
				|  |  | -    GRPC_CLOSURE_SCHED(
 | 
	
		
			
				|  |  | -        GRPC_CLOSURE_CREATE(&LoadBalancingPolicy::ShutdownAndUnrefLocked, this,
 | 
	
		
			
				|  |  | -                            grpc_combiner_scheduler(combiner_)),
 | 
	
		
			
				|  |  | -        GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | +  void set_channelz_node(
 | 
	
		
			
				|  |  | +      RefCountedPtr<channelz::ClientChannelNode> channelz_node) {
 | 
	
		
			
				|  |  | +    channelz_node_ = std::move(channelz_node);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  grpc_pollset_set* interested_parties() const { return interested_parties_; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void Orphan() override;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    /// Returns the JSON node of policy (with both policy name and config content)
 | 
	
		
			
				|  |  |    /// given the JSON node of a LoadBalancingConfig array.
 | 
	
		
			
				|  |  |    static grpc_json* ParseLoadBalancingConfig(const grpc_json* lb_config_array);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  grpc_pollset_set* interested_parties() const { return interested_parties_; }
 | 
	
		
			
				|  |  | +  // A picker that returns PICK_QUEUE for all picks.
 | 
	
		
			
				|  |  | +  // Also calls the parent LB policy's ExitIdleLocked() method when the
 | 
	
		
			
				|  |  | +  // first pick is seen.
 | 
	
		
			
				|  |  | +  class QueuePicker : public SubchannelPicker {
 | 
	
		
			
				|  |  | +   public:
 | 
	
		
			
				|  |  | +    explicit QueuePicker(RefCountedPtr<LoadBalancingPolicy> parent)
 | 
	
		
			
				|  |  | +        : parent_(std::move(parent)) {}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  void set_channelz_node(
 | 
	
		
			
				|  |  | -      RefCountedPtr<channelz::ClientChannelNode> channelz_node) {
 | 
	
		
			
				|  |  | -    channelz_node_ = std::move(channelz_node);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +    PickResult Pick(PickArgs* pick, grpc_error** error) override;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  GRPC_ABSTRACT_BASE_CLASS
 | 
	
		
			
				|  |  | +   private:
 | 
	
		
			
				|  |  | +    static void CallExitIdle(void* arg, grpc_error* error);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | - protected:
 | 
	
		
			
				|  |  | -  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
 | 
	
		
			
				|  |  | +    RefCountedPtr<LoadBalancingPolicy> parent_;
 | 
	
		
			
				|  |  | +    bool exit_idle_called_ = false;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  explicit LoadBalancingPolicy(Args args, intptr_t initial_refcount = 1);
 | 
	
		
			
				|  |  | -  virtual ~LoadBalancingPolicy();
 | 
	
		
			
				|  |  | +  // A picker that returns PICK_TRANSIENT_FAILURE for all picks.
 | 
	
		
			
				|  |  | +  class TransientFailurePicker : public SubchannelPicker {
 | 
	
		
			
				|  |  | +   public:
 | 
	
		
			
				|  |  | +    explicit TransientFailurePicker(grpc_error* error) : error_(error) {}
 | 
	
		
			
				|  |  | +    ~TransientFailurePicker() override { GRPC_ERROR_UNREF(error_); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    PickResult Pick(PickArgs* pick, grpc_error** error) override {
 | 
	
		
			
				|  |  | +      *error = GRPC_ERROR_REF(error_);
 | 
	
		
			
				|  |  | +      return PICK_TRANSIENT_FAILURE;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +   private:
 | 
	
		
			
				|  |  | +    grpc_error* error_;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  GRPC_ABSTRACT_BASE_CLASS
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  |    grpc_combiner* combiner() const { return combiner_; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    // Note: LB policies MUST NOT call any method on the helper from their
 | 
	
	
		
			
				|  | @@ -309,18 +326,11 @@ class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
 | 
	
		
			
				|  |  |      return channelz_node_.get();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /// Shuts down the policy.  Any pending picks that have not been
 | 
	
		
			
				|  |  | -  /// handed off to a new policy via HandOffPendingPicksLocked() will be
 | 
	
		
			
				|  |  | -  /// failed.
 | 
	
		
			
				|  |  | +  /// Shuts down the policy.
 | 
	
		
			
				|  |  |    virtual void ShutdownLocked() GRPC_ABSTRACT;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |   private:
 | 
	
		
			
				|  |  | -  static void ShutdownAndUnrefLocked(void* arg, grpc_error* ignored) {
 | 
	
		
			
				|  |  | -    LoadBalancingPolicy* policy = static_cast<LoadBalancingPolicy*>(arg);
 | 
	
		
			
				|  |  | -    policy->ShutdownLocked();
 | 
	
		
			
				|  |  | -    policy->channel_control_helper_.reset();
 | 
	
		
			
				|  |  | -    policy->Unref();
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  static void ShutdownAndUnrefLocked(void* arg, grpc_error* ignored);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /// Combiner under which LB policy actions take place.
 | 
	
		
			
				|  |  |    grpc_combiner* combiner_;
 |