|  | @@ -108,13 +108,16 @@
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/client_channel.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/client_channel_factory.h"
 | 
	
		
			
				|  |  | +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h"
 | 
	
		
			
				|  |  | +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/parse_address.h"
 | 
	
		
			
				|  |  |  #include "src/core/lib/channel/channel_args.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/channel/channel_stack.h"
 | 
	
		
			
				|  |  |  #include "src/core/lib/iomgr/combiner.h"
 | 
	
		
			
				|  |  |  #include "src/core/lib/iomgr/sockaddr.h"
 | 
	
		
			
				|  |  |  #include "src/core/lib/iomgr/sockaddr_utils.h"
 | 
	
	
		
			
				|  | @@ -126,6 +129,7 @@
 | 
	
		
			
				|  |  |  #include "src/core/lib/support/string.h"
 | 
	
		
			
				|  |  |  #include "src/core/lib/surface/call.h"
 | 
	
		
			
				|  |  |  #include "src/core/lib/surface/channel.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/surface/channel_init.h"
 | 
	
		
			
				|  |  |  #include "src/core/lib/transport/static_metadata.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #define GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS 20
 | 
	
	
		
			
				|  | @@ -147,6 +151,10 @@ static grpc_error *initial_metadata_add_lb_token(
 | 
	
		
			
				|  |  |                                        lb_token_mdelem_storage, lb_token);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void destroy_client_stats(void *arg) {
 | 
	
		
			
				|  |  | +  grpc_grpclb_client_stats_unref(arg);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  typedef struct wrapped_rr_closure_arg {
 | 
	
		
			
				|  |  |    /* the closure instance using this struct as argument */
 | 
	
		
			
				|  |  |    grpc_closure wrapper_closure;
 | 
	
	
		
			
				|  | @@ -163,6 +171,13 @@ typedef struct wrapped_rr_closure_arg {
 | 
	
		
			
				|  |  |     * initial metadata */
 | 
	
		
			
				|  |  |    grpc_connected_subchannel **target;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  /* the context to be populated for the subchannel call */
 | 
	
		
			
				|  |  | +  grpc_call_context_element *context;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Stats for client-side load reporting. Note that this holds a
 | 
	
		
			
				|  |  | +   * reference, which must be either passed on via context or unreffed. */
 | 
	
		
			
				|  |  | +  grpc_grpclb_client_stats *client_stats;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    /* the LB token associated with the pick */
 | 
	
		
			
				|  |  |    grpc_mdelem lb_token;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -202,6 +217,12 @@ static void wrapped_rr_closure(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  |                  (void *)*wc_arg->target, (void *)wc_arg->rr_policy);
 | 
	
		
			
				|  |  |          abort();
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | +      // Pass on client stats via context. Passes ownership of the reference.
 | 
	
		
			
				|  |  | +      GPR_ASSERT(wc_arg->client_stats != NULL);
 | 
	
		
			
				|  |  | +      wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].value = wc_arg->client_stats;
 | 
	
		
			
				|  |  | +      wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].destroy = destroy_client_stats;
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      grpc_grpclb_client_stats_unref(wc_arg->client_stats);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (grpc_lb_glb_trace) {
 | 
	
		
			
				|  |  |        gpr_log(GPR_INFO, "Unreffing RR %p", (void *)wc_arg->rr_policy);
 | 
	
	
		
			
				|  | @@ -237,6 +258,7 @@ typedef struct pending_pick {
 | 
	
		
			
				|  |  |  static void add_pending_pick(pending_pick **root,
 | 
	
		
			
				|  |  |                               const grpc_lb_policy_pick_args *pick_args,
 | 
	
		
			
				|  |  |                               grpc_connected_subchannel **target,
 | 
	
		
			
				|  |  | +                             grpc_call_context_element *context,
 | 
	
		
			
				|  |  |                               grpc_closure *on_complete) {
 | 
	
		
			
				|  |  |    pending_pick *pp = gpr_zalloc(sizeof(*pp));
 | 
	
		
			
				|  |  |    pp->next = *root;
 | 
	
	
		
			
				|  | @@ -244,6 +266,7 @@ static void add_pending_pick(pending_pick **root,
 | 
	
		
			
				|  |  |    pp->target = target;
 | 
	
		
			
				|  |  |    pp->wrapped_on_complete_arg.wrapped_closure = on_complete;
 | 
	
		
			
				|  |  |    pp->wrapped_on_complete_arg.target = target;
 | 
	
		
			
				|  |  | +  pp->wrapped_on_complete_arg.context = context;
 | 
	
		
			
				|  |  |    pp->wrapped_on_complete_arg.initial_metadata = pick_args->initial_metadata;
 | 
	
		
			
				|  |  |    pp->wrapped_on_complete_arg.lb_token_mdelem_storage =
 | 
	
		
			
				|  |  |        pick_args->lb_token_mdelem_storage;
 | 
	
	
		
			
				|  | @@ -316,6 +339,10 @@ typedef struct glb_lb_policy {
 | 
	
		
			
				|  |  |    /************************************************************/
 | 
	
		
			
				|  |  |    /*  client data associated with the LB server communication */
 | 
	
		
			
				|  |  |    /************************************************************/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Finished sending initial request. */
 | 
	
		
			
				|  |  | +  grpc_closure lb_on_sent_initial_request;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    /* Status from the LB server has been received. This signals the end of the LB
 | 
	
		
			
				|  |  |     * call. */
 | 
	
		
			
				|  |  |    grpc_closure lb_on_server_status_received;
 | 
	
	
		
			
				|  | @@ -348,6 +375,23 @@ typedef struct glb_lb_policy {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /** LB call retry timer */
 | 
	
		
			
				|  |  |    grpc_timer lb_call_retry_timer;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool initial_request_sent;
 | 
	
		
			
				|  |  | +  bool seen_initial_response;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Stats for client-side load reporting. Should be unreffed and
 | 
	
		
			
				|  |  | +   * recreated whenever lb_call is replaced. */
 | 
	
		
			
				|  |  | +  grpc_grpclb_client_stats *client_stats;
 | 
	
		
			
				|  |  | +  /* Interval and timer for next client load report. */
 | 
	
		
			
				|  |  | +  gpr_timespec client_stats_report_interval;
 | 
	
		
			
				|  |  | +  grpc_timer client_load_report_timer;
 | 
	
		
			
				|  |  | +  bool client_load_report_timer_pending;
 | 
	
		
			
				|  |  | +  bool last_client_load_report_counters_were_zero;
 | 
	
		
			
				|  |  | +  /* Closure used for either the load report timer or the callback for
 | 
	
		
			
				|  |  | +   * completion of sending the load report. */
 | 
	
		
			
				|  |  | +  grpc_closure client_load_report_closure;
 | 
	
		
			
				|  |  | +  /* Client load report message payload. */
 | 
	
		
			
				|  |  | +  grpc_byte_buffer *client_load_report_payload;
 | 
	
		
			
				|  |  |  } glb_lb_policy;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* Keeps track and reacts to changes in connectivity of the RR instance */
 | 
	
	
		
			
				|  | @@ -552,8 +596,8 @@ static bool pick_from_internal_rr_locked(
 | 
	
		
			
				|  |  |      grpc_connected_subchannel **target, wrapped_rr_closure_arg *wc_arg) {
 | 
	
		
			
				|  |  |    GPR_ASSERT(rr_policy != NULL);
 | 
	
		
			
				|  |  |    const bool pick_done = grpc_lb_policy_pick_locked(
 | 
	
		
			
				|  |  | -      exec_ctx, rr_policy, pick_args, target, (void **)&wc_arg->lb_token,
 | 
	
		
			
				|  |  | -      &wc_arg->wrapper_closure);
 | 
	
		
			
				|  |  | +      exec_ctx, rr_policy, pick_args, target, wc_arg->context,
 | 
	
		
			
				|  |  | +      (void **)&wc_arg->lb_token, &wc_arg->wrapper_closure);
 | 
	
		
			
				|  |  |    if (pick_done) {
 | 
	
		
			
				|  |  |      /* synchronous grpc_lb_policy_pick call. Unref the RR policy. */
 | 
	
		
			
				|  |  |      if (grpc_lb_glb_trace) {
 | 
	
	
		
			
				|  | @@ -567,7 +611,12 @@ static bool pick_from_internal_rr_locked(
 | 
	
		
			
				|  |  |                                    pick_args->lb_token_mdelem_storage,
 | 
	
		
			
				|  |  |                                    GRPC_MDELEM_REF(wc_arg->lb_token));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    gpr_free(wc_arg);
 | 
	
		
			
				|  |  | +    // Pass on client stats via context. Passes ownership of the reference.
 | 
	
		
			
				|  |  | +    GPR_ASSERT(wc_arg->client_stats != NULL);
 | 
	
		
			
				|  |  | +    wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].value = wc_arg->client_stats;
 | 
	
		
			
				|  |  | +    wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].destroy = destroy_client_stats;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    gpr_free(wc_arg->free_when_done);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    /* else, the pending pick will be registered and taken care of by the
 | 
	
		
			
				|  |  |     * pending pick list inside the RR policy (glb_policy->rr_policy).
 | 
	
	
		
			
				|  | @@ -690,6 +739,8 @@ static void rr_handover_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |      glb_policy->pending_picks = pp->next;
 | 
	
		
			
				|  |  |      GRPC_LB_POLICY_REF(glb_policy->rr_policy, "rr_handover_pending_pick");
 | 
	
		
			
				|  |  |      pp->wrapped_on_complete_arg.rr_policy = glb_policy->rr_policy;
 | 
	
		
			
				|  |  | +    pp->wrapped_on_complete_arg.client_stats =
 | 
	
		
			
				|  |  | +        grpc_grpclb_client_stats_ref(glb_policy->client_stats);
 | 
	
		
			
				|  |  |      if (grpc_lb_glb_trace) {
 | 
	
		
			
				|  |  |        gpr_log(GPR_INFO, "Pending pick about to PICK from 0x%" PRIxPTR "",
 | 
	
		
			
				|  |  |                (intptr_t)glb_policy->rr_policy);
 | 
	
	
		
			
				|  | @@ -864,9 +915,18 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    grpc_uri_destroy(uri);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    glb_policy->cc_factory = args->client_channel_factory;
 | 
	
		
			
				|  |  | -  glb_policy->args = grpc_channel_args_copy(args->args);
 | 
	
		
			
				|  |  |    GPR_ASSERT(glb_policy->cc_factory != NULL);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args,
 | 
	
		
			
				|  |  | +  // since we use this to trigger the client_load_reporting filter.
 | 
	
		
			
				|  |  | +  grpc_arg new_arg;
 | 
	
		
			
				|  |  | +  new_arg.key = GRPC_ARG_LB_POLICY_NAME;
 | 
	
		
			
				|  |  | +  new_arg.type = GRPC_ARG_STRING;
 | 
	
		
			
				|  |  | +  new_arg.value.string = "grpclb";
 | 
	
		
			
				|  |  | +  static const char *args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME};
 | 
	
		
			
				|  |  | +  glb_policy->args = grpc_channel_args_copy_and_add_and_remove(
 | 
	
		
			
				|  |  | +      args->args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    grpc_slice_hash_table *targets_info = NULL;
 | 
	
		
			
				|  |  |    /* Create a client channel over them to communicate with a LB service */
 | 
	
		
			
				|  |  |    char *lb_service_target_addresses =
 | 
	
	
		
			
				|  | @@ -880,6 +940,8 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    grpc_channel_args_destroy(exec_ctx, lb_channel_args);
 | 
	
		
			
				|  |  |    gpr_free(lb_service_target_addresses);
 | 
	
		
			
				|  |  |    if (glb_policy->lb_channel == NULL) {
 | 
	
		
			
				|  |  | +    gpr_free((void *)glb_policy->server_name);
 | 
	
		
			
				|  |  | +    grpc_channel_args_destroy(exec_ctx, glb_policy->args);
 | 
	
		
			
				|  |  |      gpr_free(glb_policy);
 | 
	
		
			
				|  |  |      return NULL;
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -895,6 +957,9 @@ static void glb_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
 | 
	
		
			
				|  |  |    GPR_ASSERT(glb_policy->pending_pings == NULL);
 | 
	
		
			
				|  |  |    gpr_free((void *)glb_policy->server_name);
 | 
	
		
			
				|  |  |    grpc_channel_args_destroy(exec_ctx, glb_policy->args);
 | 
	
		
			
				|  |  | +  if (glb_policy->client_stats != NULL) {
 | 
	
		
			
				|  |  | +    grpc_grpclb_client_stats_unref(glb_policy->client_stats);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    grpc_channel_destroy(glb_policy->lb_channel);
 | 
	
		
			
				|  |  |    glb_policy->lb_channel = NULL;
 | 
	
		
			
				|  |  |    grpc_connectivity_state_destroy(exec_ctx, &glb_policy->state_tracker);
 | 
	
	
		
			
				|  | @@ -1011,7 +1076,8 @@ static void glb_exit_idle_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static int glb_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
 | 
	
		
			
				|  |  |                             const grpc_lb_policy_pick_args *pick_args,
 | 
	
		
			
				|  |  | -                           grpc_connected_subchannel **target, void **user_data,
 | 
	
		
			
				|  |  | +                           grpc_connected_subchannel **target,
 | 
	
		
			
				|  |  | +                           grpc_call_context_element *context, void **user_data,
 | 
	
		
			
				|  |  |                             grpc_closure *on_complete) {
 | 
	
		
			
				|  |  |    if (pick_args->lb_token_mdelem_storage == NULL) {
 | 
	
		
			
				|  |  |      *target = NULL;
 | 
	
	
		
			
				|  | @@ -1039,6 +1105,10 @@ static int glb_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
 | 
	
		
			
				|  |  |                        grpc_schedule_on_exec_ctx);
 | 
	
		
			
				|  |  |      wc_arg->rr_policy = glb_policy->rr_policy;
 | 
	
		
			
				|  |  |      wc_arg->target = target;
 | 
	
		
			
				|  |  | +    wc_arg->context = context;
 | 
	
		
			
				|  |  | +    GPR_ASSERT(glb_policy->client_stats != NULL);
 | 
	
		
			
				|  |  | +    wc_arg->client_stats =
 | 
	
		
			
				|  |  | +        grpc_grpclb_client_stats_ref(glb_policy->client_stats);
 | 
	
		
			
				|  |  |      wc_arg->wrapped_closure = on_complete;
 | 
	
		
			
				|  |  |      wc_arg->lb_token_mdelem_storage = pick_args->lb_token_mdelem_storage;
 | 
	
		
			
				|  |  |      wc_arg->initial_metadata = pick_args->initial_metadata;
 | 
	
	
		
			
				|  | @@ -1052,7 +1122,7 @@ static int glb_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
 | 
	
		
			
				|  |  |                "picks",
 | 
	
		
			
				|  |  |                (void *)(glb_policy));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    add_pending_pick(&glb_policy->pending_picks, pick_args, target,
 | 
	
		
			
				|  |  | +    add_pending_pick(&glb_policy->pending_picks, pick_args, target, context,
 | 
	
		
			
				|  |  |                       on_complete);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (!glb_policy->started_picking) {
 | 
	
	
		
			
				|  | @@ -1093,6 +1163,104 @@ static void glb_notify_on_state_change_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |        exec_ctx, &glb_policy->state_tracker, current, notify);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void send_client_load_report_locked(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  | +                                           grpc_error *error);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void schedule_next_client_load_report(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                             glb_lb_policy *glb_policy) {
 | 
	
		
			
				|  |  | +  const gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
 | 
	
		
			
				|  |  | +  const gpr_timespec next_client_load_report_time =
 | 
	
		
			
				|  |  | +      gpr_time_add(now, glb_policy->client_stats_report_interval);
 | 
	
		
			
				|  |  | +  grpc_closure_init(&glb_policy->client_load_report_closure,
 | 
	
		
			
				|  |  | +                    send_client_load_report_locked, glb_policy,
 | 
	
		
			
				|  |  | +                    grpc_combiner_scheduler(glb_policy->base.combiner, false));
 | 
	
		
			
				|  |  | +  grpc_timer_init(exec_ctx, &glb_policy->client_load_report_timer,
 | 
	
		
			
				|  |  | +                  next_client_load_report_time,
 | 
	
		
			
				|  |  | +                  &glb_policy->client_load_report_closure, now);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void client_load_report_done_locked(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  | +                                           grpc_error *error) {
 | 
	
		
			
				|  |  | +  glb_lb_policy *glb_policy = arg;
 | 
	
		
			
				|  |  | +  grpc_byte_buffer_destroy(glb_policy->client_load_report_payload);
 | 
	
		
			
				|  |  | +  glb_policy->client_load_report_payload = NULL;
 | 
	
		
			
				|  |  | +  if (error != GRPC_ERROR_NONE || glb_policy->lb_call == NULL) {
 | 
	
		
			
				|  |  | +    glb_policy->client_load_report_timer_pending = false;
 | 
	
		
			
				|  |  | +    GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
 | 
	
		
			
				|  |  | +                              "client_load_report");
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  schedule_next_client_load_report(exec_ctx, glb_policy);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void do_send_client_load_report_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                              glb_lb_policy *glb_policy) {
 | 
	
		
			
				|  |  | +  grpc_op op;
 | 
	
		
			
				|  |  | +  memset(&op, 0, sizeof(op));
 | 
	
		
			
				|  |  | +  op.op = GRPC_OP_SEND_MESSAGE;
 | 
	
		
			
				|  |  | +  op.data.send_message.send_message = glb_policy->client_load_report_payload;
 | 
	
		
			
				|  |  | +  grpc_closure_init(&glb_policy->client_load_report_closure,
 | 
	
		
			
				|  |  | +                    client_load_report_done_locked, glb_policy,
 | 
	
		
			
				|  |  | +                    grpc_combiner_scheduler(glb_policy->base.combiner, false));
 | 
	
		
			
				|  |  | +  grpc_call_error call_error = grpc_call_start_batch_and_execute(
 | 
	
		
			
				|  |  | +      exec_ctx, glb_policy->lb_call, &op, 1,
 | 
	
		
			
				|  |  | +      &glb_policy->client_load_report_closure);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(GRPC_CALL_OK == call_error);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static bool load_report_counters_are_zero(grpc_grpclb_request *request) {
 | 
	
		
			
				|  |  | +  return request->client_stats.num_calls_started == 0 &&
 | 
	
		
			
				|  |  | +         request->client_stats.num_calls_finished == 0 &&
 | 
	
		
			
				|  |  | +         request->client_stats.num_calls_finished_with_drop_for_rate_limiting ==
 | 
	
		
			
				|  |  | +             0 &&
 | 
	
		
			
				|  |  | +         request->client_stats
 | 
	
		
			
				|  |  | +                 .num_calls_finished_with_drop_for_load_balancing == 0 &&
 | 
	
		
			
				|  |  | +         request->client_stats.num_calls_finished_with_client_failed_to_send ==
 | 
	
		
			
				|  |  | +             0 &&
 | 
	
		
			
				|  |  | +         request->client_stats.num_calls_finished_known_received == 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void send_client_load_report_locked(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  | +                                           grpc_error *error) {
 | 
	
		
			
				|  |  | +  glb_lb_policy *glb_policy = arg;
 | 
	
		
			
				|  |  | +  if (error == GRPC_ERROR_CANCELLED || glb_policy->lb_call == NULL) {
 | 
	
		
			
				|  |  | +    glb_policy->client_load_report_timer_pending = false;
 | 
	
		
			
				|  |  | +    GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
 | 
	
		
			
				|  |  | +                              "client_load_report");
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // Construct message payload.
 | 
	
		
			
				|  |  | +  GPR_ASSERT(glb_policy->client_load_report_payload == NULL);
 | 
	
		
			
				|  |  | +  grpc_grpclb_request *request =
 | 
	
		
			
				|  |  | +      grpc_grpclb_load_report_request_create(glb_policy->client_stats);
 | 
	
		
			
				|  |  | +  // Skip client load report if the counters were all zero in the last
 | 
	
		
			
				|  |  | +  // report and they are still zero in this one.
 | 
	
		
			
				|  |  | +  if (load_report_counters_are_zero(request)) {
 | 
	
		
			
				|  |  | +    if (glb_policy->last_client_load_report_counters_were_zero) {
 | 
	
		
			
				|  |  | +      grpc_grpclb_request_destroy(request);
 | 
	
		
			
				|  |  | +      schedule_next_client_load_report(exec_ctx, glb_policy);
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    glb_policy->last_client_load_report_counters_were_zero = true;
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    glb_policy->last_client_load_report_counters_were_zero = false;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  grpc_slice request_payload_slice = grpc_grpclb_request_encode(request);
 | 
	
		
			
				|  |  | +  glb_policy->client_load_report_payload =
 | 
	
		
			
				|  |  | +      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
 | 
	
		
			
				|  |  | +  grpc_slice_unref_internal(exec_ctx, request_payload_slice);
 | 
	
		
			
				|  |  | +  grpc_grpclb_request_destroy(request);
 | 
	
		
			
				|  |  | +  // If we've already sent the initial request, then we can go ahead and
 | 
	
		
			
				|  |  | +  // sent the load report.  Otherwise, we need to wait until the initial
 | 
	
		
			
				|  |  | +  // request has been sent to send this
 | 
	
		
			
				|  |  | +  // (see lb_on_sent_initial_request_locked() below).
 | 
	
		
			
				|  |  | +  if (glb_policy->initial_request_sent) {
 | 
	
		
			
				|  |  | +    do_send_client_load_report_locked(exec_ctx, glb_policy);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void lb_on_sent_initial_request_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                              void *arg, grpc_error *error);
 | 
	
		
			
				|  |  |  static void lb_on_server_status_received_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |                                                  void *arg, grpc_error *error);
 | 
	
		
			
				|  |  |  static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
	
		
			
				|  | @@ -1114,6 +1282,11 @@ static void lb_call_init_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |        &host, glb_policy->deadline, NULL);
 | 
	
		
			
				|  |  |    grpc_slice_unref_internal(exec_ctx, host);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  if (glb_policy->client_stats != NULL) {
 | 
	
		
			
				|  |  | +    grpc_grpclb_client_stats_unref(glb_policy->client_stats);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  glb_policy->client_stats = grpc_grpclb_client_stats_create();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    grpc_metadata_array_init(&glb_policy->lb_initial_metadata_recv);
 | 
	
		
			
				|  |  |    grpc_metadata_array_init(&glb_policy->lb_trailing_metadata_recv);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1125,6 +1298,9 @@ static void lb_call_init_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    grpc_slice_unref_internal(exec_ctx, request_payload_slice);
 | 
	
		
			
				|  |  |    grpc_grpclb_request_destroy(request);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  grpc_closure_init(&glb_policy->lb_on_sent_initial_request,
 | 
	
		
			
				|  |  | +                    lb_on_sent_initial_request_locked, glb_policy,
 | 
	
		
			
				|  |  | +                    grpc_combiner_scheduler(glb_policy->base.combiner, false));
 | 
	
		
			
				|  |  |    grpc_closure_init(&glb_policy->lb_on_server_status_received,
 | 
	
		
			
				|  |  |                      lb_on_server_status_received_locked, glb_policy,
 | 
	
		
			
				|  |  |                      grpc_combiner_scheduler(glb_policy->base.combiner, false));
 | 
	
	
		
			
				|  | @@ -1138,6 +1314,10 @@ static void lb_call_init_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |                     GRPC_GRPCLB_RECONNECT_JITTER,
 | 
	
		
			
				|  |  |                     GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS * 1000,
 | 
	
		
			
				|  |  |                     GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  glb_policy->initial_request_sent = false;
 | 
	
		
			
				|  |  | +  glb_policy->seen_initial_response = false;
 | 
	
		
			
				|  |  | +  glb_policy->last_client_load_report_counters_were_zero = false;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void lb_call_destroy_locked(grpc_exec_ctx *exec_ctx,
 | 
	
	
		
			
				|  | @@ -1151,6 +1331,10 @@ static void lb_call_destroy_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    grpc_byte_buffer_destroy(glb_policy->lb_request_payload);
 | 
	
		
			
				|  |  |    grpc_slice_unref_internal(exec_ctx, glb_policy->lb_call_status_details);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (!glb_policy->client_load_report_timer_pending) {
 | 
	
		
			
				|  |  | +    grpc_timer_cancel(exec_ctx, &glb_policy->client_load_report_timer);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /*
 | 
	
	
		
			
				|  | @@ -1179,21 +1363,27 @@ static void query_for_backends_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    op->flags = 0;
 | 
	
		
			
				|  |  |    op->reserved = NULL;
 | 
	
		
			
				|  |  |    op++;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    op->op = GRPC_OP_RECV_INITIAL_METADATA;
 | 
	
		
			
				|  |  |    op->data.recv_initial_metadata.recv_initial_metadata =
 | 
	
		
			
				|  |  |        &glb_policy->lb_initial_metadata_recv;
 | 
	
		
			
				|  |  |    op->flags = 0;
 | 
	
		
			
				|  |  |    op->reserved = NULL;
 | 
	
		
			
				|  |  |    op++;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    GPR_ASSERT(glb_policy->lb_request_payload != NULL);
 | 
	
		
			
				|  |  |    op->op = GRPC_OP_SEND_MESSAGE;
 | 
	
		
			
				|  |  |    op->data.send_message.send_message = glb_policy->lb_request_payload;
 | 
	
		
			
				|  |  |    op->flags = 0;
 | 
	
		
			
				|  |  |    op->reserved = NULL;
 | 
	
		
			
				|  |  |    op++;
 | 
	
		
			
				|  |  | +  /* take a weak ref (won't prevent calling of \a glb_shutdown if the strong ref
 | 
	
		
			
				|  |  | +   * count goes to zero) to be unref'd in lb_on_sent_initial_request_locked() */
 | 
	
		
			
				|  |  | +  GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "lb_on_server_status_received");
 | 
	
		
			
				|  |  | +  call_error = grpc_call_start_batch_and_execute(
 | 
	
		
			
				|  |  | +      exec_ctx, glb_policy->lb_call, ops, (size_t)(op - ops),
 | 
	
		
			
				|  |  | +      &glb_policy->lb_on_sent_initial_request);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(GRPC_CALL_OK == call_error);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  op = ops;
 | 
	
		
			
				|  |  |    op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
 | 
	
		
			
				|  |  |    op->data.recv_status_on_client.trailing_metadata =
 | 
	
		
			
				|  |  |        &glb_policy->lb_trailing_metadata_recv;
 | 
	
	
		
			
				|  | @@ -1225,6 +1415,19 @@ static void query_for_backends_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    GPR_ASSERT(GRPC_CALL_OK == call_error);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void lb_on_sent_initial_request_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                              void *arg, grpc_error *error) {
 | 
	
		
			
				|  |  | +  glb_lb_policy *glb_policy = arg;
 | 
	
		
			
				|  |  | +  glb_policy->initial_request_sent = true;
 | 
	
		
			
				|  |  | +  // If we attempted to send a client load report before the initial
 | 
	
		
			
				|  |  | +  // request was sent, send the load report now.
 | 
	
		
			
				|  |  | +  if (glb_policy->client_load_report_payload != NULL) {
 | 
	
		
			
				|  |  | +    do_send_client_load_report_locked(exec_ctx, glb_policy);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
 | 
	
		
			
				|  |  | +                            "lb_on_response_received_locked");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  |                                             grpc_error *error) {
 | 
	
		
			
				|  |  |    glb_lb_policy *glb_policy = arg;
 | 
	
	
		
			
				|  | @@ -1240,58 +1443,91 @@ static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  |      grpc_byte_buffer_reader_init(&bbr, glb_policy->lb_response_payload);
 | 
	
		
			
				|  |  |      grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr);
 | 
	
		
			
				|  |  |      grpc_byte_buffer_destroy(glb_policy->lb_response_payload);
 | 
	
		
			
				|  |  | -    grpc_grpclb_serverlist *serverlist =
 | 
	
		
			
				|  |  | -        grpc_grpclb_response_parse_serverlist(response_slice);
 | 
	
		
			
				|  |  | -    if (serverlist != NULL) {
 | 
	
		
			
				|  |  | -      GPR_ASSERT(glb_policy->lb_call != NULL);
 | 
	
		
			
				|  |  | -      grpc_slice_unref_internal(exec_ctx, response_slice);
 | 
	
		
			
				|  |  | -      if (grpc_lb_glb_trace) {
 | 
	
		
			
				|  |  | -        gpr_log(GPR_INFO, "Serverlist with %lu servers received",
 | 
	
		
			
				|  |  | -                (unsigned long)serverlist->num_servers);
 | 
	
		
			
				|  |  | -        for (size_t i = 0; i < serverlist->num_servers; ++i) {
 | 
	
		
			
				|  |  | -          grpc_resolved_address addr;
 | 
	
		
			
				|  |  | -          parse_server(serverlist->servers[i], &addr);
 | 
	
		
			
				|  |  | -          char *ipport;
 | 
	
		
			
				|  |  | -          grpc_sockaddr_to_string(&ipport, &addr, false);
 | 
	
		
			
				|  |  | -          gpr_log(GPR_INFO, "Serverlist[%lu]: %s", (unsigned long)i, ipport);
 | 
	
		
			
				|  |  | -          gpr_free(ipport);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    grpc_grpclb_initial_response *response = NULL;
 | 
	
		
			
				|  |  | +    if (!glb_policy->seen_initial_response &&
 | 
	
		
			
				|  |  | +        (response = grpc_grpclb_initial_response_parse(response_slice)) !=
 | 
	
		
			
				|  |  | +            NULL) {
 | 
	
		
			
				|  |  | +      if (response->has_client_stats_report_interval) {
 | 
	
		
			
				|  |  | +        glb_policy->client_stats_report_interval =
 | 
	
		
			
				|  |  | +            gpr_time_max(gpr_time_from_seconds(1, GPR_TIMESPAN),
 | 
	
		
			
				|  |  | +                         grpc_grpclb_duration_to_timespec(
 | 
	
		
			
				|  |  | +                             &response->client_stats_report_interval));
 | 
	
		
			
				|  |  | +        if (grpc_lb_glb_trace) {
 | 
	
		
			
				|  |  | +          gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | +                  "received initial LB response message; "
 | 
	
		
			
				|  |  | +                  "client load reporting interval = %" PRId64 ".%09d sec",
 | 
	
		
			
				|  |  | +                  glb_policy->client_stats_report_interval.tv_sec,
 | 
	
		
			
				|  |  | +                  glb_policy->client_stats_report_interval.tv_nsec);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +        /* take a weak ref (won't prevent calling of \a glb_shutdown() if the
 | 
	
		
			
				|  |  | +         * strong ref count goes to zero) to be unref'd in
 | 
	
		
			
				|  |  | +         * send_client_load_report() */
 | 
	
		
			
				|  |  | +        glb_policy->client_load_report_timer_pending = true;
 | 
	
		
			
				|  |  | +        GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "client_load_report");
 | 
	
		
			
				|  |  | +        schedule_next_client_load_report(exec_ctx, glb_policy);
 | 
	
		
			
				|  |  | +      } else if (grpc_lb_glb_trace) {
 | 
	
		
			
				|  |  | +        gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | +                "received initial LB response message; "
 | 
	
		
			
				|  |  | +                "client load reporting NOT enabled");
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | +      grpc_grpclb_initial_response_destroy(response);
 | 
	
		
			
				|  |  | +      glb_policy->seen_initial_response = true;
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      grpc_grpclb_serverlist *serverlist =
 | 
	
		
			
				|  |  | +          grpc_grpclb_response_parse_serverlist(response_slice);
 | 
	
		
			
				|  |  | +      if (serverlist != NULL) {
 | 
	
		
			
				|  |  | +        GPR_ASSERT(glb_policy->lb_call != NULL);
 | 
	
		
			
				|  |  | +        if (grpc_lb_glb_trace) {
 | 
	
		
			
				|  |  | +          gpr_log(GPR_INFO, "Serverlist with %lu servers received",
 | 
	
		
			
				|  |  | +                  (unsigned long)serverlist->num_servers);
 | 
	
		
			
				|  |  | +          for (size_t i = 0; i < serverlist->num_servers; ++i) {
 | 
	
		
			
				|  |  | +            grpc_resolved_address addr;
 | 
	
		
			
				|  |  | +            parse_server(serverlist->servers[i], &addr);
 | 
	
		
			
				|  |  | +            char *ipport;
 | 
	
		
			
				|  |  | +            grpc_sockaddr_to_string(&ipport, &addr, false);
 | 
	
		
			
				|  |  | +            gpr_log(GPR_INFO, "Serverlist[%lu]: %s", (unsigned long)i, ipport);
 | 
	
		
			
				|  |  | +            gpr_free(ipport);
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      /* update serverlist */
 | 
	
		
			
				|  |  | -      if (serverlist->num_servers > 0) {
 | 
	
		
			
				|  |  | -        if (grpc_grpclb_serverlist_equals(glb_policy->serverlist, serverlist)) {
 | 
	
		
			
				|  |  | +        /* update serverlist */
 | 
	
		
			
				|  |  | +        if (serverlist->num_servers > 0) {
 | 
	
		
			
				|  |  | +          if (grpc_grpclb_serverlist_equals(glb_policy->serverlist,
 | 
	
		
			
				|  |  | +                                            serverlist)) {
 | 
	
		
			
				|  |  | +            if (grpc_lb_glb_trace) {
 | 
	
		
			
				|  |  | +              gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | +                      "Incoming server list identical to current, ignoring.");
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            grpc_grpclb_destroy_serverlist(serverlist);
 | 
	
		
			
				|  |  | +          } else { /* new serverlist */
 | 
	
		
			
				|  |  | +            if (glb_policy->serverlist != NULL) {
 | 
	
		
			
				|  |  | +              /* dispose of the old serverlist */
 | 
	
		
			
				|  |  | +              grpc_grpclb_destroy_serverlist(glb_policy->serverlist);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            /* and update the copy in the glb_lb_policy instance. This
 | 
	
		
			
				|  |  | +             * serverlist instance will be destroyed either upon the next
 | 
	
		
			
				|  |  | +             * update or in glb_destroy() */
 | 
	
		
			
				|  |  | +            glb_policy->serverlist = serverlist;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            rr_handover_locked(exec_ctx, glb_policy);
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  |            if (grpc_lb_glb_trace) {
 | 
	
		
			
				|  |  |              gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | -                    "Incoming server list identical to current, ignoring.");
 | 
	
		
			
				|  |  | +                    "Received empty server list. Picks will stay pending until "
 | 
	
		
			
				|  |  | +                    "a response with > 0 servers is received");
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  |            grpc_grpclb_destroy_serverlist(serverlist);
 | 
	
		
			
				|  |  | -        } else { /* new serverlist */
 | 
	
		
			
				|  |  | -          if (glb_policy->serverlist != NULL) {
 | 
	
		
			
				|  |  | -            /* dispose of the old serverlist */
 | 
	
		
			
				|  |  | -            grpc_grpclb_destroy_serverlist(glb_policy->serverlist);
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | -          /* and update the copy in the glb_lb_policy instance. This serverlist
 | 
	
		
			
				|  |  | -           * instance will be destroyed either upon the next update or in
 | 
	
		
			
				|  |  | -           * glb_destroy() */
 | 
	
		
			
				|  |  | -          glb_policy->serverlist = serverlist;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -          rr_handover_locked(exec_ctx, glb_policy);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        if (grpc_lb_glb_trace) {
 | 
	
		
			
				|  |  | -          gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | -                  "Received empty server list. Picks will stay pending until a "
 | 
	
		
			
				|  |  | -                  "response with > 0 servers is received");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        grpc_grpclb_destroy_serverlist(serverlist);
 | 
	
		
			
				|  |  | +      } else { /* serverlist == NULL */
 | 
	
		
			
				|  |  | +        gpr_log(GPR_ERROR, "Invalid LB response received: '%s'. Ignoring.",
 | 
	
		
			
				|  |  | +                grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX));
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -    } else { /* serverlist == NULL */
 | 
	
		
			
				|  |  | -      gpr_log(GPR_ERROR, "Invalid LB response received: '%s'. Ignoring.",
 | 
	
		
			
				|  |  | -              grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX));
 | 
	
		
			
				|  |  | -      grpc_slice_unref_internal(exec_ctx, response_slice);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    grpc_slice_unref_internal(exec_ctx, response_slice);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      if (!glb_policy->shutting_down) {
 | 
	
		
			
				|  |  |        /* keep listening for serverlist updates */
 | 
	
		
			
				|  |  |        op->op = GRPC_OP_RECV_MESSAGE;
 | 
	
	
		
			
				|  | @@ -1403,9 +1639,29 @@ grpc_lb_policy_factory *grpc_glb_lb_factory_create() {
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* Plugin registration */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Only add client_load_reporting filter if the grpclb LB policy is used.
 | 
	
		
			
				|  |  | +static bool maybe_add_client_load_reporting_filter(
 | 
	
		
			
				|  |  | +    grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) {
 | 
	
		
			
				|  |  | +  const grpc_channel_args *args =
 | 
	
		
			
				|  |  | +      grpc_channel_stack_builder_get_channel_arguments(builder);
 | 
	
		
			
				|  |  | +  const grpc_arg *channel_arg =
 | 
	
		
			
				|  |  | +      grpc_channel_args_find(args, GRPC_ARG_LB_POLICY_NAME);
 | 
	
		
			
				|  |  | +  if (channel_arg != NULL && channel_arg->type == GRPC_ARG_STRING &&
 | 
	
		
			
				|  |  | +      strcmp(channel_arg->value.string, "grpclb") == 0) {
 | 
	
		
			
				|  |  | +    return grpc_channel_stack_builder_append_filter(
 | 
	
		
			
				|  |  | +        builder, (const grpc_channel_filter *)arg, NULL, NULL);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return true;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void grpc_lb_policy_grpclb_init() {
 | 
	
		
			
				|  |  |    grpc_register_lb_policy(grpc_glb_lb_factory_create());
 | 
	
		
			
				|  |  |    grpc_register_tracer("glb", &grpc_lb_glb_trace);
 | 
	
		
			
				|  |  | +  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
 | 
	
		
			
				|  |  | +                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
 | 
	
		
			
				|  |  | +                                   maybe_add_client_load_reporting_filter,
 | 
	
		
			
				|  |  | +                                   (void *)&grpc_client_load_reporting_filter);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void grpc_lb_policy_grpclb_shutdown() {}
 |