|  | @@ -47,39 +47,44 @@
 | 
	
		
			
				|  |  |  #include "src/core/transport/connectivity_state.h"
 | 
	
		
			
				|  |  |  #include "src/core/transport/connectivity_state.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#define INTERNAL_REF_BITS 16
 | 
	
		
			
				|  |  | +#define STRONG_REF_MASK (~(gpr_atm)((1 << INTERNAL_REF_BITS) - 1))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #define GRPC_SUBCHANNEL_MIN_CONNECT_TIMEOUT_SECONDS 20
 | 
	
		
			
				|  |  |  #define GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS 1
 | 
	
		
			
				|  |  |  #define GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER 1.6
 | 
	
		
			
				|  |  |  #define GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS 120
 | 
	
		
			
				|  |  |  #define GRPC_SUBCHANNEL_RECONNECT_JITTER 0.2
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -typedef struct {
 | 
	
		
			
				|  |  | -  /* all fields protected by subchannel->mu */
 | 
	
		
			
				|  |  | -  /** refcount */
 | 
	
		
			
				|  |  | -  int refs;
 | 
	
		
			
				|  |  | -  /** parent subchannel */
 | 
	
		
			
				|  |  | -  grpc_subchannel *subchannel;
 | 
	
		
			
				|  |  | -} connection;
 | 
	
		
			
				|  |  | +#define GET_CONNECTED_SUBCHANNEL(subchannel, barrier)      \
 | 
	
		
			
				|  |  | +  ((grpc_connected_subchannel *)(gpr_atm_##barrier##_load( \
 | 
	
		
			
				|  |  | +      &(subchannel)->connected_subchannel)))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  typedef struct {
 | 
	
		
			
				|  |  |    grpc_closure closure;
 | 
	
		
			
				|  |  | -  size_t version;
 | 
	
		
			
				|  |  |    grpc_subchannel *subchannel;
 | 
	
		
			
				|  |  |    grpc_connectivity_state connectivity_state;
 | 
	
		
			
				|  |  |  } state_watcher;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -typedef struct waiting_for_connect {
 | 
	
		
			
				|  |  | -  struct waiting_for_connect *next;
 | 
	
		
			
				|  |  | -  grpc_closure *notify;
 | 
	
		
			
				|  |  | -  grpc_pollset *pollset;
 | 
	
		
			
				|  |  | -  gpr_atm *target;
 | 
	
		
			
				|  |  | +typedef struct external_state_watcher {
 | 
	
		
			
				|  |  |    grpc_subchannel *subchannel;
 | 
	
		
			
				|  |  | -  grpc_closure continuation;
 | 
	
		
			
				|  |  | -} waiting_for_connect;
 | 
	
		
			
				|  |  | +  grpc_pollset_set *pollset_set;
 | 
	
		
			
				|  |  | +  grpc_closure *notify;
 | 
	
		
			
				|  |  | +  grpc_closure closure;
 | 
	
		
			
				|  |  | +  struct external_state_watcher *next;
 | 
	
		
			
				|  |  | +  struct external_state_watcher *prev;
 | 
	
		
			
				|  |  | +} external_state_watcher;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  struct grpc_subchannel {
 | 
	
		
			
				|  |  |    grpc_connector *connector;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  /** refcount
 | 
	
		
			
				|  |  | +      - lower INTERNAL_REF_BITS bits are for internal references:
 | 
	
		
			
				|  |  | +        these do not keep the subchannel open.
 | 
	
		
			
				|  |  | +      - upper remaining bits are for public references: these do
 | 
	
		
			
				|  |  | +        keep the subchannel open */
 | 
	
		
			
				|  |  | +  gpr_atm ref_pair;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    /** non-transport related channel filters */
 | 
	
		
			
				|  |  |    const grpc_channel_filter **filters;
 | 
	
		
			
				|  |  |    size_t num_filters;
 | 
	
	
		
			
				|  | @@ -88,15 +93,9 @@ struct grpc_subchannel {
 | 
	
		
			
				|  |  |    /** address to connect to */
 | 
	
		
			
				|  |  |    struct sockaddr *addr;
 | 
	
		
			
				|  |  |    size_t addr_len;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    /** initial string to send to peer */
 | 
	
		
			
				|  |  |    gpr_slice initial_connect_string;
 | 
	
		
			
				|  |  | -  /** master channel - the grpc_channel instance that ultimately owns
 | 
	
		
			
				|  |  | -      this channel_data via its channel stack.
 | 
	
		
			
				|  |  | -      We occasionally use this to bump the refcount on the master channel
 | 
	
		
			
				|  |  | -      to keep ourselves alive through an asynchronous operation. */
 | 
	
		
			
				|  |  | -  grpc_channel *master;
 | 
	
		
			
				|  |  | -  /** have we seen a disconnection? */
 | 
	
		
			
				|  |  | -  int disconnected;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /** set during connection */
 | 
	
		
			
				|  |  |    grpc_connect_out_args connecting_result;
 | 
	
	
		
			
				|  | @@ -105,27 +104,24 @@ struct grpc_subchannel {
 | 
	
		
			
				|  |  |    grpc_closure connected;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /** pollset_set tracking who's interested in a connection
 | 
	
		
			
				|  |  | -      being setup - owned by the master channel (in particular the
 | 
	
		
			
				|  |  | -     client_channel
 | 
	
		
			
				|  |  | -      filter there-in) */
 | 
	
		
			
				|  |  | -  grpc_pollset_set *pollset_set;
 | 
	
		
			
				|  |  | +      being setup */
 | 
	
		
			
				|  |  | +  grpc_pollset_set pollset_set;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /** active connection, or null; of type grpc_connected_subchannel */
 | 
	
		
			
				|  |  | +  gpr_atm connected_subchannel;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /** mutex protecting remaining elements */
 | 
	
		
			
				|  |  |    gpr_mu mu;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /** active connection */
 | 
	
		
			
				|  |  | -  connection *active;
 | 
	
		
			
				|  |  | -  /** version number for the active connection */
 | 
	
		
			
				|  |  | -  size_t active_version;
 | 
	
		
			
				|  |  | -  /** refcount */
 | 
	
		
			
				|  |  | -  int refs;
 | 
	
		
			
				|  |  | +  /** have we seen a disconnection? */
 | 
	
		
			
				|  |  | +  int disconnected;
 | 
	
		
			
				|  |  |    /** are we connecting */
 | 
	
		
			
				|  |  |    int connecting;
 | 
	
		
			
				|  |  | -  /** things waiting for a connection */
 | 
	
		
			
				|  |  | -  waiting_for_connect *waiting;
 | 
	
		
			
				|  |  |    /** connectivity state tracking */
 | 
	
		
			
				|  |  |    grpc_connectivity_state_tracker state_tracker;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  external_state_watcher root_external_state_watcher;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    /** next connect attempt time */
 | 
	
		
			
				|  |  |    gpr_timespec next_attempt;
 | 
	
		
			
				|  |  |    /** amount to backoff each failure */
 | 
	
	
		
			
				|  | @@ -139,151 +135,141 @@ struct grpc_subchannel {
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  struct grpc_subchannel_call {
 | 
	
		
			
				|  |  | -  connection *connection;
 | 
	
		
			
				|  |  | +  grpc_connected_subchannel *connection;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #define SUBCHANNEL_CALL_TO_CALL_STACK(call) ((grpc_call_stack *)((call) + 1))
 | 
	
		
			
				|  |  | -#define CHANNEL_STACK_FROM_CONNECTION(con) ((grpc_channel_stack *)((con) + 1))
 | 
	
		
			
				|  |  | +#define CHANNEL_STACK_FROM_CONNECTION(con) ((grpc_channel_stack *)(con))
 | 
	
		
			
				|  |  |  #define CALLSTACK_TO_SUBCHANNEL_CALL(callstack) \
 | 
	
		
			
				|  |  |    (((grpc_subchannel_call *)(callstack)) - 1)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static grpc_subchannel_call *create_call(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                         connection *con,
 | 
	
		
			
				|  |  | -                                         grpc_pollset *pollset);
 | 
	
		
			
				|  |  | -static void connectivity_state_changed_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                              grpc_subchannel *c,
 | 
	
		
			
				|  |  | -                                              const char *reason);
 | 
	
		
			
				|  |  | -static grpc_connectivity_state compute_connectivity_locked(grpc_subchannel *c);
 | 
	
		
			
				|  |  |  static gpr_timespec compute_connect_deadline(grpc_subchannel *c);
 | 
	
		
			
				|  |  |  static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *subchannel,
 | 
	
		
			
				|  |  |                                   int iomgr_success);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void subchannel_ref_locked(grpc_subchannel *c
 | 
	
		
			
				|  |  | -                                      GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
 | 
	
		
			
				|  |  | -static int subchannel_unref_locked(
 | 
	
		
			
				|  |  | -    grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) GRPC_MUST_USE_RESULT;
 | 
	
		
			
				|  |  | -static void connection_ref_locked(connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
 | 
	
		
			
				|  |  | -static grpc_subchannel *connection_unref_locked(
 | 
	
		
			
				|  |  | -    grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -    connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) GRPC_MUST_USE_RESULT;
 | 
	
		
			
				|  |  | -static void subchannel_destroy(grpc_exec_ctx *exec_ctx, grpc_subchannel *c);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  #ifdef GRPC_STREAM_REFCOUNT_DEBUG
 | 
	
		
			
				|  |  | -#define SUBCHANNEL_REF_LOCKED(p, r) \
 | 
	
		
			
				|  |  | -  subchannel_ref_locked((p), __FILE__, __LINE__, (r))
 | 
	
		
			
				|  |  | -#define SUBCHANNEL_UNREF_LOCKED(p, r) \
 | 
	
		
			
				|  |  | -  subchannel_unref_locked((p), __FILE__, __LINE__, (r))
 | 
	
		
			
				|  |  | -#define CONNECTION_REF_LOCKED(p, r) \
 | 
	
		
			
				|  |  | -  connection_ref_locked((p), __FILE__, __LINE__, (r))
 | 
	
		
			
				|  |  | -#define CONNECTION_UNREF_LOCKED(cl, p, r) \
 | 
	
		
			
				|  |  | -  connection_unref_locked((cl), (p), __FILE__, __LINE__, (r))
 | 
	
		
			
				|  |  | -#define REF_PASS_ARGS , file, line, reason
 | 
	
		
			
				|  |  | -#define REF_PASS_REASON , reason
 | 
	
		
			
				|  |  | +#define REF_REASON reason
 | 
	
		
			
				|  |  |  #define REF_LOG(name, p)                                                  \
 | 
	
		
			
				|  |  |    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "%s: %p   ref %d -> %d %s", \
 | 
	
		
			
				|  |  | -          (name), (p), (p)->refs, (p)->refs + 1, reason)
 | 
	
		
			
				|  |  | +          (name), (p), (p)->refs.count, (p)->refs.count + 1, reason)
 | 
	
		
			
				|  |  |  #define UNREF_LOG(name, p)                                                \
 | 
	
		
			
				|  |  |    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "%s: %p unref %d -> %d %s", \
 | 
	
		
			
				|  |  | -          (name), (p), (p)->refs, (p)->refs - 1, reason)
 | 
	
		
			
				|  |  | +          (name), (p), (p)->refs.count, (p)->refs.count - 1, reason)
 | 
	
		
			
				|  |  | +#define REF_MUTATE_EXTRA_ARGS \
 | 
	
		
			
				|  |  | +  GRPC_SUBCHANNEL_REF_EXTRA_ARGS, const char *purpose
 | 
	
		
			
				|  |  | +#define REF_MUTATE_PURPOSE(x) , file, line, reason, x
 | 
	
		
			
				|  |  |  #else
 | 
	
		
			
				|  |  | -#define SUBCHANNEL_REF_LOCKED(p, r) subchannel_ref_locked((p))
 | 
	
		
			
				|  |  | -#define SUBCHANNEL_UNREF_LOCKED(p, r) subchannel_unref_locked((p))
 | 
	
		
			
				|  |  | -#define CONNECTION_REF_LOCKED(p, r) connection_ref_locked((p))
 | 
	
		
			
				|  |  | -#define CONNECTION_UNREF_LOCKED(cl, p, r) connection_unref_locked((cl), (p))
 | 
	
		
			
				|  |  | -#define REF_PASS_ARGS
 | 
	
		
			
				|  |  | -#define REF_PASS_REASON
 | 
	
		
			
				|  |  | +#define REF_REASON ""
 | 
	
		
			
				|  |  |  #define REF_LOG(name, p) \
 | 
	
		
			
				|  |  |    do {                   \
 | 
	
		
			
				|  |  |    } while (0)
 | 
	
		
			
				|  |  |  #define UNREF_LOG(name, p) \
 | 
	
		
			
				|  |  |    do {                     \
 | 
	
		
			
				|  |  |    } while (0)
 | 
	
		
			
				|  |  | +#define REF_MUTATE_EXTRA_ARGS
 | 
	
		
			
				|  |  | +#define REF_MUTATE_PURPOSE(x)
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /*
 | 
	
		
			
				|  |  |   * connection implementation
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void connection_destroy(grpc_exec_ctx *exec_ctx, connection *c) {
 | 
	
		
			
				|  |  | -  GPR_ASSERT(c->refs == 0);
 | 
	
		
			
				|  |  | +static void connection_destroy(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  | +                               int success) {
 | 
	
		
			
				|  |  | +  grpc_connected_subchannel *c = arg;
 | 
	
		
			
				|  |  |    grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c));
 | 
	
		
			
				|  |  |    gpr_free(c);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void connection_ref_locked(connection *c
 | 
	
		
			
				|  |  | -                                      GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | -  REF_LOG("CONNECTION", c);
 | 
	
		
			
				|  |  | -  subchannel_ref_locked(c->subchannel REF_PASS_ARGS);
 | 
	
		
			
				|  |  | -  ++c->refs;
 | 
	
		
			
				|  |  | +void grpc_connected_subchannel_ref(grpc_connected_subchannel *c
 | 
	
		
			
				|  |  | +                                       GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | +  GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CONNECTION(c), REF_REASON);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static grpc_subchannel *connection_unref_locked(
 | 
	
		
			
				|  |  | -    grpc_exec_ctx *exec_ctx, connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | -  grpc_subchannel *destroy = NULL;
 | 
	
		
			
				|  |  | -  UNREF_LOG("CONNECTION", c);
 | 
	
		
			
				|  |  | -  if (subchannel_unref_locked(c->subchannel REF_PASS_ARGS)) {
 | 
	
		
			
				|  |  | -    destroy = c->subchannel;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (--c->refs == 0 && c->subchannel->active != c) {
 | 
	
		
			
				|  |  | -    connection_destroy(exec_ctx, c);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  return destroy;
 | 
	
		
			
				|  |  | +void grpc_connected_subchannel_unref(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                     grpc_connected_subchannel *c
 | 
	
		
			
				|  |  | +                                         GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | +  GRPC_CHANNEL_STACK_UNREF(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c),
 | 
	
		
			
				|  |  | +                           REF_REASON);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /*
 | 
	
		
			
				|  |  |   * grpc_subchannel implementation
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void subchannel_ref_locked(grpc_subchannel *c
 | 
	
		
			
				|  |  | -                                      GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | -  REF_LOG("SUBCHANNEL", c);
 | 
	
		
			
				|  |  | -  ++c->refs;
 | 
	
		
			
				|  |  | +static void subchannel_destroy(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  | +                               int success) {
 | 
	
		
			
				|  |  | +  grpc_subchannel *c = arg;
 | 
	
		
			
				|  |  | +  gpr_free((void *)c->filters);
 | 
	
		
			
				|  |  | +  grpc_channel_args_destroy(c->args);
 | 
	
		
			
				|  |  | +  gpr_free(c->addr);
 | 
	
		
			
				|  |  | +  gpr_slice_unref(c->initial_connect_string);
 | 
	
		
			
				|  |  | +  grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker);
 | 
	
		
			
				|  |  | +  grpc_connector_unref(exec_ctx, c->connector);
 | 
	
		
			
				|  |  | +  grpc_pollset_set_destroy(&c->pollset_set);
 | 
	
		
			
				|  |  | +  gpr_free(c);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static int subchannel_unref_locked(grpc_subchannel *c
 | 
	
		
			
				|  |  | -                                       GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | -  UNREF_LOG("SUBCHANNEL", c);
 | 
	
		
			
				|  |  | -  return --c->refs == 0;
 | 
	
		
			
				|  |  | +static gpr_atm ref_mutate(grpc_subchannel *c, gpr_atm delta,
 | 
	
		
			
				|  |  | +                          int barrier REF_MUTATE_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | +  gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta)
 | 
	
		
			
				|  |  | +                            : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta);
 | 
	
		
			
				|  |  | +#ifdef GRPC_STREAM_REFCOUNT_DEBUG
 | 
	
		
			
				|  |  | +  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
 | 
	
		
			
				|  |  | +          "SUBCHANNEL: %p % 12s 0x%08x -> 0x%08x [%s]", c, purpose, old_val,
 | 
	
		
			
				|  |  | +          old_val + delta, reason);
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +  return old_val;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void grpc_subchannel_ref(grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | -  gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  | -  subchannel_ref_locked(c REF_PASS_ARGS);
 | 
	
		
			
				|  |  | -  gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | +  gpr_atm old_refs;
 | 
	
		
			
				|  |  | +  old_refs = ref_mutate(c, (1 << INTERNAL_REF_BITS),
 | 
	
		
			
				|  |  | +                        0 REF_MUTATE_PURPOSE("STRONG_REF"));
 | 
	
		
			
				|  |  | +  GPR_ASSERT((old_refs & STRONG_REF_MASK) != 0);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                           grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | -  int destroy;
 | 
	
		
			
				|  |  | -  gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  | -  destroy = subchannel_unref_locked(c REF_PASS_ARGS);
 | 
	
		
			
				|  |  | -  gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | -  if (destroy) subchannel_destroy(exec_ctx, c);
 | 
	
		
			
				|  |  | +void grpc_subchannel_weak_ref(grpc_subchannel *c
 | 
	
		
			
				|  |  | +                                  GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | +  gpr_atm old_refs;
 | 
	
		
			
				|  |  | +  old_refs = ref_mutate(c, 1, 0 REF_MUTATE_PURPOSE("WEAK_REF"));
 | 
	
		
			
				|  |  | +  GPR_ASSERT(old_refs != 0);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void subchannel_destroy(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) {
 | 
	
		
			
				|  |  | -  if (c->active != NULL) {
 | 
	
		
			
				|  |  | -    connection_destroy(exec_ctx, c->active);
 | 
	
		
			
				|  |  | +static void disconnect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) {
 | 
	
		
			
				|  |  | +  grpc_connected_subchannel *con;
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(!c->disconnected);
 | 
	
		
			
				|  |  | +  c->disconnected = 1;
 | 
	
		
			
				|  |  | +  grpc_connector_shutdown(exec_ctx, c->connector);
 | 
	
		
			
				|  |  | +  con = GET_CONNECTED_SUBCHANNEL(c, no_barrier);
 | 
	
		
			
				|  |  | +  if (con != NULL) {
 | 
	
		
			
				|  |  | +    GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, con, "connection");
 | 
	
		
			
				|  |  | +    gpr_atm_no_barrier_store(&c->connected_subchannel, 0xdeadbeef);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  gpr_free((void *)c->filters);
 | 
	
		
			
				|  |  | -  grpc_channel_args_destroy(c->args);
 | 
	
		
			
				|  |  | -  gpr_free(c->addr);
 | 
	
		
			
				|  |  | -  gpr_slice_unref(c->initial_connect_string);
 | 
	
		
			
				|  |  | -  grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker);
 | 
	
		
			
				|  |  | -  grpc_connector_unref(exec_ctx, c->connector);
 | 
	
		
			
				|  |  | -  gpr_free(c);
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void grpc_subchannel_add_interested_party(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                          grpc_subchannel *c,
 | 
	
		
			
				|  |  | -                                          grpc_pollset *pollset) {
 | 
	
		
			
				|  |  | -  grpc_pollset_set_add_pollset(exec_ctx, c->pollset_set, pollset);
 | 
	
		
			
				|  |  | +void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                           grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | +  gpr_atm old_refs;
 | 
	
		
			
				|  |  | +  old_refs = ref_mutate(c, (gpr_atm)1 - (gpr_atm)(1 << INTERNAL_REF_BITS),
 | 
	
		
			
				|  |  | +                        1 REF_MUTATE_PURPOSE("STRONG_UNREF"));
 | 
	
		
			
				|  |  | +  if ((old_refs & STRONG_REF_MASK) == (1 << INTERNAL_REF_BITS)) {
 | 
	
		
			
				|  |  | +    disconnect(exec_ctx, c);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "strong-unref");
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void grpc_subchannel_del_interested_party(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                          grpc_subchannel *c,
 | 
	
		
			
				|  |  | -                                          grpc_pollset *pollset) {
 | 
	
		
			
				|  |  | -  grpc_pollset_set_del_pollset(exec_ctx, c->pollset_set, pollset);
 | 
	
		
			
				|  |  | +void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                grpc_subchannel *c
 | 
	
		
			
				|  |  | +                                    GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | +  gpr_atm old_refs;
 | 
	
		
			
				|  |  | +  old_refs = ref_mutate(c, -(gpr_atm)1, 1 REF_MUTATE_PURPOSE("WEAK_UNREF"));
 | 
	
		
			
				|  |  | +  if (old_refs == 1) {
 | 
	
		
			
				|  |  | +    grpc_exec_ctx_enqueue(exec_ctx, grpc_closure_create(subchannel_destroy, c),
 | 
	
		
			
				|  |  | +                          1);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static gpr_uint32 random_seed() {
 | 
	
	
		
			
				|  | @@ -293,10 +279,8 @@ static gpr_uint32 random_seed() {
 | 
	
		
			
				|  |  |  grpc_subchannel *grpc_subchannel_create(grpc_connector *connector,
 | 
	
		
			
				|  |  |                                          grpc_subchannel_args *args) {
 | 
	
		
			
				|  |  |    grpc_subchannel *c = gpr_malloc(sizeof(*c));
 | 
	
		
			
				|  |  | -  grpc_channel_element *parent_elem = grpc_channel_stack_last_element(
 | 
	
		
			
				|  |  | -      grpc_channel_get_channel_stack(args->master));
 | 
	
		
			
				|  |  |    memset(c, 0, sizeof(*c));
 | 
	
		
			
				|  |  | -  c->refs = 1;
 | 
	
		
			
				|  |  | +  gpr_atm_no_barrier_store(&c->ref_pair, 1 << INTERNAL_REF_BITS);
 | 
	
		
			
				|  |  |    c->connector = connector;
 | 
	
		
			
				|  |  |    grpc_connector_ref(c->connector);
 | 
	
		
			
				|  |  |    c->num_filters = args->filter_count;
 | 
	
	
		
			
				|  | @@ -305,13 +289,14 @@ grpc_subchannel *grpc_subchannel_create(grpc_connector *connector,
 | 
	
		
			
				|  |  |           sizeof(grpc_channel_filter *) * c->num_filters);
 | 
	
		
			
				|  |  |    c->addr = gpr_malloc(args->addr_len);
 | 
	
		
			
				|  |  |    memcpy(c->addr, args->addr, args->addr_len);
 | 
	
		
			
				|  |  | +  grpc_pollset_set_init(&c->pollset_set);
 | 
	
		
			
				|  |  |    c->addr_len = args->addr_len;
 | 
	
		
			
				|  |  |    grpc_set_initial_connect_string(&c->addr, &c->addr_len,
 | 
	
		
			
				|  |  |                                    &c->initial_connect_string);
 | 
	
		
			
				|  |  |    c->args = grpc_channel_args_copy(args->args);
 | 
	
		
			
				|  |  | -  c->master = args->master;
 | 
	
		
			
				|  |  | -  c->pollset_set = grpc_client_channel_get_connecting_pollset_set(parent_elem);
 | 
	
		
			
				|  |  |    c->random = random_seed();
 | 
	
		
			
				|  |  | +  c->root_external_state_watcher.next = c->root_external_state_watcher.prev =
 | 
	
		
			
				|  |  | +      &c->root_external_state_watcher;
 | 
	
		
			
				|  |  |    grpc_closure_init(&c->connected, subchannel_connected, c);
 | 
	
		
			
				|  |  |    grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE,
 | 
	
		
			
				|  |  |                                 "subchannel");
 | 
	
	
		
			
				|  | @@ -319,70 +304,18 @@ grpc_subchannel *grpc_subchannel_create(grpc_connector *connector,
 | 
	
		
			
				|  |  |    return c;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void cancel_waiting_calls(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                 grpc_subchannel *subchannel,
 | 
	
		
			
				|  |  | -                                 int iomgr_success) {
 | 
	
		
			
				|  |  | -  waiting_for_connect *w4c;
 | 
	
		
			
				|  |  | -  gpr_mu_lock(&subchannel->mu);
 | 
	
		
			
				|  |  | -  w4c = subchannel->waiting;
 | 
	
		
			
				|  |  | -  subchannel->waiting = NULL;
 | 
	
		
			
				|  |  | -  gpr_mu_unlock(&subchannel->mu);
 | 
	
		
			
				|  |  | -  while (w4c != NULL) {
 | 
	
		
			
				|  |  | -    waiting_for_connect *next = w4c->next;
 | 
	
		
			
				|  |  | -    grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel,
 | 
	
		
			
				|  |  | -                                         w4c->pollset);
 | 
	
		
			
				|  |  | -    if (w4c->notify) {
 | 
	
		
			
				|  |  | -      w4c->notify->cb(exec_ctx, w4c->notify->cb_arg, iomgr_success);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    GRPC_SUBCHANNEL_UNREF(exec_ctx, w4c->subchannel, "waiting_for_connect");
 | 
	
		
			
				|  |  | -    gpr_free(w4c);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    w4c = next;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -void grpc_subchannel_cancel_create_call(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                        grpc_subchannel *subchannel,
 | 
	
		
			
				|  |  | -                                        gpr_atm *target) {
 | 
	
		
			
				|  |  | -  waiting_for_connect *w4c;
 | 
	
		
			
				|  |  | -  int unref_count = 0;
 | 
	
		
			
				|  |  | -  gpr_mu_lock(&subchannel->mu);
 | 
	
		
			
				|  |  | -  w4c = subchannel->waiting;
 | 
	
		
			
				|  |  | -  subchannel->waiting = NULL;
 | 
	
		
			
				|  |  | -  while (w4c != NULL) {
 | 
	
		
			
				|  |  | -    waiting_for_connect *next = w4c->next;
 | 
	
		
			
				|  |  | -    if (w4c->target == target) {
 | 
	
		
			
				|  |  | -      grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel,
 | 
	
		
			
				|  |  | -                                           w4c->pollset);
 | 
	
		
			
				|  |  | -      grpc_exec_ctx_enqueue(exec_ctx, w4c->notify, 0);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      unref_count++;
 | 
	
		
			
				|  |  | -      gpr_free(w4c);
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      w4c->next = subchannel->waiting;
 | 
	
		
			
				|  |  | -      subchannel->waiting = w4c;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    w4c = next;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  gpr_mu_unlock(&subchannel->mu);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  while (unref_count-- > 0) {
 | 
	
		
			
				|  |  | -    GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannel, "waiting_for_connect");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  static void continue_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) {
 | 
	
		
			
				|  |  |    grpc_connect_in_args args;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  args.interested_parties = c->pollset_set;
 | 
	
		
			
				|  |  | +  args.interested_parties = &c->pollset_set;
 | 
	
		
			
				|  |  |    args.addr = c->addr;
 | 
	
		
			
				|  |  |    args.addr_len = c->addr_len;
 | 
	
		
			
				|  |  |    args.deadline = compute_connect_deadline(c);
 | 
	
		
			
				|  |  |    args.channel_args = c->args;
 | 
	
		
			
				|  |  |    args.initial_connect_string = c->initial_connect_string;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  grpc_connectivity_state_set(exec_ctx, &c->state_tracker,
 | 
	
		
			
				|  |  | +                              GRPC_CHANNEL_CONNECTING, "state_change");
 | 
	
		
			
				|  |  |    grpc_connector_connect(exec_ctx, c->connector, &args, &c->connecting_result,
 | 
	
		
			
				|  |  |                           &c->connected);
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -395,66 +328,6 @@ static void start_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) {
 | 
	
		
			
				|  |  |    continue_connect(exec_ctx, c);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void continue_creating_call(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  | -                                   int iomgr_success) {
 | 
	
		
			
				|  |  | -  int call_creation_finished_ok;
 | 
	
		
			
				|  |  | -  waiting_for_connect *w4c = arg;
 | 
	
		
			
				|  |  | -  grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel, w4c->pollset);
 | 
	
		
			
				|  |  | -  call_creation_finished_ok = grpc_subchannel_create_call(
 | 
	
		
			
				|  |  | -      exec_ctx, w4c->subchannel, w4c->pollset, w4c->target, w4c->notify);
 | 
	
		
			
				|  |  | -  GPR_ASSERT(call_creation_finished_ok == 1);
 | 
	
		
			
				|  |  | -  w4c->notify->cb(exec_ctx, w4c->notify->cb_arg, iomgr_success);
 | 
	
		
			
				|  |  | -  GRPC_SUBCHANNEL_UNREF(exec_ctx, w4c->subchannel, "waiting_for_connect");
 | 
	
		
			
				|  |  | -  gpr_free(w4c);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -int grpc_subchannel_create_call(grpc_exec_ctx *exec_ctx, grpc_subchannel *c,
 | 
	
		
			
				|  |  | -                                grpc_pollset *pollset, gpr_atm *target,
 | 
	
		
			
				|  |  | -                                grpc_closure *notify) {
 | 
	
		
			
				|  |  | -  connection *con;
 | 
	
		
			
				|  |  | -  grpc_subchannel_call *call;
 | 
	
		
			
				|  |  | -  GPR_TIMER_BEGIN("grpc_subchannel_create_call", 0);
 | 
	
		
			
				|  |  | -  gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  | -  if (c->active != NULL) {
 | 
	
		
			
				|  |  | -    con = c->active;
 | 
	
		
			
				|  |  | -    CONNECTION_REF_LOCKED(con, "call");
 | 
	
		
			
				|  |  | -    gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    call = create_call(exec_ctx, con, pollset);
 | 
	
		
			
				|  |  | -    if (!gpr_atm_rel_cas(target, 0, (gpr_atm)(gpr_uintptr)call)) {
 | 
	
		
			
				|  |  | -      GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, call, "failed to set");
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    GPR_TIMER_END("grpc_subchannel_create_call", 0);
 | 
	
		
			
				|  |  | -    return 1;
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    waiting_for_connect *w4c = gpr_malloc(sizeof(*w4c));
 | 
	
		
			
				|  |  | -    w4c->next = c->waiting;
 | 
	
		
			
				|  |  | -    w4c->notify = notify;
 | 
	
		
			
				|  |  | -    w4c->pollset = pollset;
 | 
	
		
			
				|  |  | -    w4c->target = target;
 | 
	
		
			
				|  |  | -    w4c->subchannel = c;
 | 
	
		
			
				|  |  | -    /* released when clearing w4c */
 | 
	
		
			
				|  |  | -    SUBCHANNEL_REF_LOCKED(c, "waiting_for_connect");
 | 
	
		
			
				|  |  | -    grpc_closure_init(&w4c->continuation, continue_creating_call, w4c);
 | 
	
		
			
				|  |  | -    c->waiting = w4c;
 | 
	
		
			
				|  |  | -    grpc_subchannel_add_interested_party(exec_ctx, c, pollset);
 | 
	
		
			
				|  |  | -    if (!c->connecting) {
 | 
	
		
			
				|  |  | -      c->connecting = 1;
 | 
	
		
			
				|  |  | -      connectivity_state_changed_locked(exec_ctx, c, "create_call");
 | 
	
		
			
				|  |  | -      /* released by connection */
 | 
	
		
			
				|  |  | -      SUBCHANNEL_REF_LOCKED(c, "connecting");
 | 
	
		
			
				|  |  | -      GRPC_CHANNEL_INTERNAL_REF(c->master, "connecting");
 | 
	
		
			
				|  |  | -      gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      start_connect(exec_ctx, c);
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    GPR_TIMER_END("grpc_subchannel_create_call", 0);
 | 
	
		
			
				|  |  | -    return 0;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c) {
 | 
	
		
			
				|  |  |    grpc_connectivity_state state;
 | 
	
		
			
				|  |  |    gpr_mu_lock(&c->mu);
 | 
	
	
		
			
				|  | @@ -463,153 +336,138 @@ grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c) {
 | 
	
		
			
				|  |  |    return state;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void grpc_subchannel_notify_on_state_change(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                            grpc_subchannel *c,
 | 
	
		
			
				|  |  | -                                            grpc_connectivity_state *state,
 | 
	
		
			
				|  |  | -                                            grpc_closure *notify) {
 | 
	
		
			
				|  |  | -  int do_connect = 0;
 | 
	
		
			
				|  |  | -  gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  | -  if (grpc_connectivity_state_notify_on_state_change(
 | 
	
		
			
				|  |  | -          exec_ctx, &c->state_tracker, state, notify)) {
 | 
	
		
			
				|  |  | -    do_connect = 1;
 | 
	
		
			
				|  |  | -    c->connecting = 1;
 | 
	
		
			
				|  |  | -    /* released by connection */
 | 
	
		
			
				|  |  | -    SUBCHANNEL_REF_LOCKED(c, "connecting");
 | 
	
		
			
				|  |  | -    GRPC_CHANNEL_INTERNAL_REF(c->master, "connecting");
 | 
	
		
			
				|  |  | -    connectivity_state_changed_locked(exec_ctx, c, "state_change");
 | 
	
		
			
				|  |  | +static void on_external_state_watcher_done(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  | +                                           int success) {
 | 
	
		
			
				|  |  | +  external_state_watcher *w = arg;
 | 
	
		
			
				|  |  | +  grpc_closure *follow_up = w->notify;
 | 
	
		
			
				|  |  | +  if (w->pollset_set != NULL) {
 | 
	
		
			
				|  |  | +    grpc_pollset_set_del_pollset_set(exec_ctx, &w->subchannel->pollset_set,
 | 
	
		
			
				|  |  | +                                     w->pollset_set);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (do_connect) {
 | 
	
		
			
				|  |  | -    start_connect(exec_ctx, c);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -int grpc_subchannel_state_change_unsubscribe(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                             grpc_subchannel *c,
 | 
	
		
			
				|  |  | -                                             grpc_closure *subscribed_notify) {
 | 
	
		
			
				|  |  | -  int success;
 | 
	
		
			
				|  |  | -  gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  | -  success = grpc_connectivity_state_change_unsubscribe(
 | 
	
		
			
				|  |  | -      exec_ctx, &c->state_tracker, subscribed_notify);
 | 
	
		
			
				|  |  | -  gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | -  return success;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -void grpc_subchannel_process_transport_op(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                          grpc_subchannel *c,
 | 
	
		
			
				|  |  | -                                          grpc_transport_op *op) {
 | 
	
		
			
				|  |  | -  connection *con = NULL;
 | 
	
		
			
				|  |  | -  grpc_subchannel *destroy;
 | 
	
		
			
				|  |  | -  int cancel_alarm = 0;
 | 
	
		
			
				|  |  | -  gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  | -  if (c->active != NULL) {
 | 
	
		
			
				|  |  | -    con = c->active;
 | 
	
		
			
				|  |  | -    CONNECTION_REF_LOCKED(con, "transport-op");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (op->disconnect) {
 | 
	
		
			
				|  |  | -    c->disconnected = 1;
 | 
	
		
			
				|  |  | -    connectivity_state_changed_locked(exec_ctx, c, "disconnect");
 | 
	
		
			
				|  |  | -    if (c->have_alarm) {
 | 
	
		
			
				|  |  | -      cancel_alarm = 1;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (con != NULL) {
 | 
	
		
			
				|  |  | -    grpc_channel_stack *channel_stack = CHANNEL_STACK_FROM_CONNECTION(con);
 | 
	
		
			
				|  |  | -    grpc_channel_element *top_elem =
 | 
	
		
			
				|  |  | -        grpc_channel_stack_element(channel_stack, 0);
 | 
	
		
			
				|  |  | -    top_elem->filter->start_transport_op(exec_ctx, top_elem, op);
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&w->subchannel->mu);
 | 
	
		
			
				|  |  | +  w->next->prev = w->prev;
 | 
	
		
			
				|  |  | +  w->prev->next = w->next;
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&w->subchannel->mu);
 | 
	
		
			
				|  |  | +  GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, w->subchannel, "external_state_watcher");
 | 
	
		
			
				|  |  | +  gpr_free(w);
 | 
	
		
			
				|  |  | +  follow_up->cb(exec_ctx, follow_up->cb_arg, success);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void grpc_subchannel_notify_on_state_change(
 | 
	
		
			
				|  |  | +    grpc_exec_ctx *exec_ctx, grpc_subchannel *c,
 | 
	
		
			
				|  |  | +    grpc_pollset_set *interested_parties, grpc_connectivity_state *state,
 | 
	
		
			
				|  |  | +    grpc_closure *notify) {
 | 
	
		
			
				|  |  | +  int do_connect = 0;
 | 
	
		
			
				|  |  | +  external_state_watcher *w;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  if (state == NULL) {
 | 
	
		
			
				|  |  |      gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  | -    destroy = CONNECTION_UNREF_LOCKED(exec_ctx, con, "transport-op");
 | 
	
		
			
				|  |  | +    for (w = c->root_external_state_watcher.next;
 | 
	
		
			
				|  |  | +         w != &c->root_external_state_watcher; w = w->next) {
 | 
	
		
			
				|  |  | +      if (w->notify == notify) {
 | 
	
		
			
				|  |  | +        grpc_connectivity_state_notify_on_state_change(
 | 
	
		
			
				|  |  | +            exec_ctx, &c->state_tracker, NULL, &w->closure);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |      gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | -    if (destroy) {
 | 
	
		
			
				|  |  | -      subchannel_destroy(exec_ctx, destroy);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    w = gpr_malloc(sizeof(*w));
 | 
	
		
			
				|  |  | +    w->subchannel = c;
 | 
	
		
			
				|  |  | +    w->pollset_set = interested_parties;
 | 
	
		
			
				|  |  | +    w->notify = notify;
 | 
	
		
			
				|  |  | +    grpc_closure_init(&w->closure, on_external_state_watcher_done, w);
 | 
	
		
			
				|  |  | +    if (interested_parties != NULL) {
 | 
	
		
			
				|  |  | +      grpc_pollset_set_add_pollset_set(exec_ctx, &c->pollset_set,
 | 
	
		
			
				|  |  | +                                       interested_parties);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    GRPC_SUBCHANNEL_WEAK_REF(c, "external_state_watcher");
 | 
	
		
			
				|  |  | +    gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  | +    w->next = &c->root_external_state_watcher;
 | 
	
		
			
				|  |  | +    w->prev = w->next->prev;
 | 
	
		
			
				|  |  | +    w->next->prev = w->prev->next = w;
 | 
	
		
			
				|  |  | +    if (grpc_connectivity_state_notify_on_state_change(
 | 
	
		
			
				|  |  | +            exec_ctx, &c->state_tracker, state, &w->closure)) {
 | 
	
		
			
				|  |  | +      do_connect = 1;
 | 
	
		
			
				|  |  | +      c->connecting = 1;
 | 
	
		
			
				|  |  | +      /* released by connection */
 | 
	
		
			
				|  |  | +      GRPC_SUBCHANNEL_WEAK_REF(c, "connecting");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  if (cancel_alarm) {
 | 
	
		
			
				|  |  | -    grpc_timer_cancel(exec_ctx, &c->alarm);
 | 
	
		
			
				|  |  | +  if (do_connect) {
 | 
	
		
			
				|  |  | +    start_connect(exec_ctx, c);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  if (op->disconnect) {
 | 
	
		
			
				|  |  | -    grpc_connector_shutdown(exec_ctx, c->connector);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +void grpc_connected_subchannel_process_transport_op(
 | 
	
		
			
				|  |  | +    grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con,
 | 
	
		
			
				|  |  | +    grpc_transport_op *op) {
 | 
	
		
			
				|  |  | +  grpc_channel_stack *channel_stack = CHANNEL_STACK_FROM_CONNECTION(con);
 | 
	
		
			
				|  |  | +  grpc_channel_element *top_elem = grpc_channel_stack_element(channel_stack, 0);
 | 
	
		
			
				|  |  | +  top_elem->filter->start_transport_op(exec_ctx, top_elem, op);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void on_state_changed(grpc_exec_ctx *exec_ctx, void *p,
 | 
	
		
			
				|  |  | -                             int iomgr_success) {
 | 
	
		
			
				|  |  | +static void subchannel_on_child_state_changed(grpc_exec_ctx *exec_ctx, void *p,
 | 
	
		
			
				|  |  | +                                              int iomgr_success) {
 | 
	
		
			
				|  |  |    state_watcher *sw = p;
 | 
	
		
			
				|  |  |    grpc_subchannel *c = sw->subchannel;
 | 
	
		
			
				|  |  |    gpr_mu *mu = &c->mu;
 | 
	
		
			
				|  |  | -  int destroy;
 | 
	
		
			
				|  |  | -  grpc_transport_op op;
 | 
	
		
			
				|  |  | -  grpc_channel_element *elem;
 | 
	
		
			
				|  |  | -  connection *destroy_connection = NULL;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    gpr_mu_lock(mu);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /* if we failed or there is a version number mismatch, just leave
 | 
	
		
			
				|  |  | -     this closure */
 | 
	
		
			
				|  |  | -  if (!iomgr_success || sw->subchannel->active_version != sw->version) {
 | 
	
		
			
				|  |  | -    goto done;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  switch (sw->connectivity_state) {
 | 
	
		
			
				|  |  | -    case GRPC_CHANNEL_CONNECTING:
 | 
	
		
			
				|  |  | -    case GRPC_CHANNEL_READY:
 | 
	
		
			
				|  |  | -    case GRPC_CHANNEL_IDLE:
 | 
	
		
			
				|  |  | -      /* all is still good: keep watching */
 | 
	
		
			
				|  |  | -      memset(&op, 0, sizeof(op));
 | 
	
		
			
				|  |  | -      op.connectivity_state = &sw->connectivity_state;
 | 
	
		
			
				|  |  | -      op.on_connectivity_state_change = &sw->closure;
 | 
	
		
			
				|  |  | -      elem = grpc_channel_stack_element(
 | 
	
		
			
				|  |  | -          CHANNEL_STACK_FROM_CONNECTION(c->active), 0);
 | 
	
		
			
				|  |  | -      elem->filter->start_transport_op(exec_ctx, elem, &op);
 | 
	
		
			
				|  |  | -      /* early out */
 | 
	
		
			
				|  |  | -      gpr_mu_unlock(mu);
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | -    case GRPC_CHANNEL_FATAL_FAILURE:
 | 
	
		
			
				|  |  | -    case GRPC_CHANNEL_TRANSIENT_FAILURE:
 | 
	
		
			
				|  |  | -      /* things have gone wrong, deactivate and enter idle */
 | 
	
		
			
				|  |  | -      if (sw->subchannel->active->refs == 0) {
 | 
	
		
			
				|  |  | -        destroy_connection = sw->subchannel->active;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      sw->subchannel->active = NULL;
 | 
	
		
			
				|  |  | -      grpc_connectivity_state_set(exec_ctx, &c->state_tracker,
 | 
	
		
			
				|  |  | -                                  c->disconnected
 | 
	
		
			
				|  |  | -                                      ? GRPC_CHANNEL_FATAL_FAILURE
 | 
	
		
			
				|  |  | -                                      : GRPC_CHANNEL_TRANSIENT_FAILURE,
 | 
	
		
			
				|  |  | -                                  "connection_failed");
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | +  /* if we failed just leave this closure */
 | 
	
		
			
				|  |  | +  if (iomgr_success) {
 | 
	
		
			
				|  |  | +    if (sw->connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
 | 
	
		
			
				|  |  | +      /* any errors on a subchannel ==> we're done, create a new one */
 | 
	
		
			
				|  |  | +      sw->connectivity_state = GRPC_CHANNEL_FATAL_FAILURE;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    grpc_connectivity_state_set(exec_ctx, &c->state_tracker,
 | 
	
		
			
				|  |  | +                                sw->connectivity_state, "reflect_child");
 | 
	
		
			
				|  |  | +    if (sw->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE) {
 | 
	
		
			
				|  |  | +      grpc_connected_subchannel_notify_on_state_change(
 | 
	
		
			
				|  |  | +          exec_ctx, GET_CONNECTED_SUBCHANNEL(c, no_barrier), NULL,
 | 
	
		
			
				|  |  | +          &sw->connectivity_state, &sw->closure);
 | 
	
		
			
				|  |  | +      GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher");
 | 
	
		
			
				|  |  | +      sw = NULL;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -done:
 | 
	
		
			
				|  |  | -  connectivity_state_changed_locked(exec_ctx, c, "transport_state_changed");
 | 
	
		
			
				|  |  | -  destroy = SUBCHANNEL_UNREF_LOCKED(c, "state_watcher");
 | 
	
		
			
				|  |  | -  gpr_free(sw);
 | 
	
		
			
				|  |  |    gpr_mu_unlock(mu);
 | 
	
		
			
				|  |  | -  if (destroy) {
 | 
	
		
			
				|  |  | -    subchannel_destroy(exec_ctx, c);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (destroy_connection != NULL) {
 | 
	
		
			
				|  |  | -    connection_destroy(exec_ctx, destroy_connection);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "state_watcher");
 | 
	
		
			
				|  |  | +  gpr_free(sw);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void connected_subchannel_state_op(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                          grpc_connected_subchannel *con,
 | 
	
		
			
				|  |  | +                                          grpc_pollset_set *interested_parties,
 | 
	
		
			
				|  |  | +                                          grpc_connectivity_state *state,
 | 
	
		
			
				|  |  | +                                          grpc_closure *closure) {
 | 
	
		
			
				|  |  | +  grpc_transport_op op;
 | 
	
		
			
				|  |  | +  grpc_channel_element *elem;
 | 
	
		
			
				|  |  | +  memset(&op, 0, sizeof(op));
 | 
	
		
			
				|  |  | +  op.connectivity_state = state;
 | 
	
		
			
				|  |  | +  op.on_connectivity_state_change = closure;
 | 
	
		
			
				|  |  | +  op.bind_pollset_set = interested_parties;
 | 
	
		
			
				|  |  | +  elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0);
 | 
	
		
			
				|  |  | +  elem->filter->start_transport_op(exec_ctx, elem, &op);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void grpc_connected_subchannel_notify_on_state_change(
 | 
	
		
			
				|  |  | +    grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con,
 | 
	
		
			
				|  |  | +    grpc_pollset_set *interested_parties, grpc_connectivity_state *state,
 | 
	
		
			
				|  |  | +    grpc_closure *closure) {
 | 
	
		
			
				|  |  | +  connected_subchannel_state_op(exec_ctx, con, interested_parties, state,
 | 
	
		
			
				|  |  | +                                closure);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void publish_transport(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) {
 | 
	
		
			
				|  |  |    size_t channel_stack_size;
 | 
	
		
			
				|  |  | -  connection *con;
 | 
	
		
			
				|  |  | +  grpc_connected_subchannel *con;
 | 
	
		
			
				|  |  |    grpc_channel_stack *stk;
 | 
	
		
			
				|  |  |    size_t num_filters;
 | 
	
		
			
				|  |  |    const grpc_channel_filter **filters;
 | 
	
		
			
				|  |  | -  waiting_for_connect *w4c;
 | 
	
		
			
				|  |  | -  grpc_transport_op op;
 | 
	
		
			
				|  |  | -  state_watcher *sw;
 | 
	
		
			
				|  |  | -  connection *destroy_connection = NULL;
 | 
	
		
			
				|  |  | -  grpc_channel_element *elem;
 | 
	
		
			
				|  |  | +  state_watcher *sw_subchannel;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* build final filter list */
 | 
	
		
			
				|  |  |    num_filters = c->num_filters + c->connecting_result.num_filters + 1;
 | 
	
	
		
			
				|  | @@ -621,74 +479,52 @@ static void publish_transport(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* construct channel stack */
 | 
	
		
			
				|  |  |    channel_stack_size = grpc_channel_stack_size(filters, num_filters);
 | 
	
		
			
				|  |  | -  con = gpr_malloc(sizeof(connection) + channel_stack_size);
 | 
	
		
			
				|  |  | -  stk = (grpc_channel_stack *)(con + 1);
 | 
	
		
			
				|  |  | -  con->refs = 0;
 | 
	
		
			
				|  |  | -  con->subchannel = c;
 | 
	
		
			
				|  |  | -  grpc_channel_stack_init(exec_ctx, filters, num_filters, c->master, c->args,
 | 
	
		
			
				|  |  | -                          stk);
 | 
	
		
			
				|  |  | +  con = gpr_malloc(channel_stack_size);
 | 
	
		
			
				|  |  | +  stk = CHANNEL_STACK_FROM_CONNECTION(con);
 | 
	
		
			
				|  |  | +  grpc_channel_stack_init(exec_ctx, 1, connection_destroy, con, filters,
 | 
	
		
			
				|  |  | +                          num_filters, c->args, "CONNECTED_SUBCHANNEL", stk);
 | 
	
		
			
				|  |  |    grpc_connected_channel_bind_transport(stk, c->connecting_result.transport);
 | 
	
		
			
				|  |  |    gpr_free((void *)c->connecting_result.filters);
 | 
	
		
			
				|  |  |    memset(&c->connecting_result, 0, sizeof(c->connecting_result));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* initialize state watcher */
 | 
	
		
			
				|  |  | -  sw = gpr_malloc(sizeof(*sw));
 | 
	
		
			
				|  |  | -  grpc_closure_init(&sw->closure, on_state_changed, sw);
 | 
	
		
			
				|  |  | -  sw->subchannel = c;
 | 
	
		
			
				|  |  | -  sw->connectivity_state = GRPC_CHANNEL_READY;
 | 
	
		
			
				|  |  | +  sw_subchannel = gpr_malloc(sizeof(*sw_subchannel));
 | 
	
		
			
				|  |  | +  sw_subchannel->subchannel = c;
 | 
	
		
			
				|  |  | +  sw_subchannel->connectivity_state = GRPC_CHANNEL_READY;
 | 
	
		
			
				|  |  | +  grpc_closure_init(&sw_subchannel->closure, subchannel_on_child_state_changed,
 | 
	
		
			
				|  |  | +                    sw_subchannel);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    if (c->disconnected) {
 | 
	
		
			
				|  |  |      gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | -    gpr_free(sw);
 | 
	
		
			
				|  |  | +    gpr_free(sw_subchannel);
 | 
	
		
			
				|  |  |      gpr_free((void *)filters);
 | 
	
		
			
				|  |  |      grpc_channel_stack_destroy(exec_ctx, stk);
 | 
	
		
			
				|  |  | -    GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->master, "connecting");
 | 
	
		
			
				|  |  | -    GRPC_SUBCHANNEL_UNREF(exec_ctx, c, "connecting");
 | 
	
		
			
				|  |  | +    gpr_free(con);
 | 
	
		
			
				|  |  | +    GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting");
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* publish */
 | 
	
		
			
				|  |  | -  if (c->active != NULL && c->active->refs == 0) {
 | 
	
		
			
				|  |  | -    destroy_connection = c->active;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  c->active = con;
 | 
	
		
			
				|  |  | -  c->active_version++;
 | 
	
		
			
				|  |  | -  sw->version = c->active_version;
 | 
	
		
			
				|  |  | +  GPR_ASSERT(gpr_atm_no_barrier_cas(&c->connected_subchannel, 0, (gpr_atm)con));
 | 
	
		
			
				|  |  |    c->connecting = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /* watch for changes; subchannel ref for connecting is donated
 | 
	
		
			
				|  |  | +  /* setup subchannel watching connected subchannel for changes; subchannel ref
 | 
	
		
			
				|  |  | +     for connecting is donated
 | 
	
		
			
				|  |  |       to the state watcher */
 | 
	
		
			
				|  |  | -  memset(&op, 0, sizeof(op));
 | 
	
		
			
				|  |  | -  op.connectivity_state = &sw->connectivity_state;
 | 
	
		
			
				|  |  | -  op.on_connectivity_state_change = &sw->closure;
 | 
	
		
			
				|  |  | -  op.bind_pollset_set = c->pollset_set;
 | 
	
		
			
				|  |  | -  SUBCHANNEL_REF_LOCKED(c, "state_watcher");
 | 
	
		
			
				|  |  | -  GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->master, "connecting");
 | 
	
		
			
				|  |  | -  GPR_ASSERT(!SUBCHANNEL_UNREF_LOCKED(c, "connecting"));
 | 
	
		
			
				|  |  | -  elem =
 | 
	
		
			
				|  |  | -      grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(c->active), 0);
 | 
	
		
			
				|  |  | -  elem->filter->start_transport_op(exec_ctx, elem, &op);
 | 
	
		
			
				|  |  | +  GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher");
 | 
	
		
			
				|  |  | +  GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting");
 | 
	
		
			
				|  |  | +  grpc_connected_subchannel_notify_on_state_change(
 | 
	
		
			
				|  |  | +      exec_ctx, con, &c->pollset_set, &sw_subchannel->connectivity_state,
 | 
	
		
			
				|  |  | +      &sw_subchannel->closure);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* signal completion */
 | 
	
		
			
				|  |  | -  connectivity_state_changed_locked(exec_ctx, c, "connected");
 | 
	
		
			
				|  |  | -  w4c = c->waiting;
 | 
	
		
			
				|  |  | -  c->waiting = NULL;
 | 
	
		
			
				|  |  | +  grpc_connectivity_state_set(exec_ctx, &c->state_tracker, GRPC_CHANNEL_READY,
 | 
	
		
			
				|  |  | +                              "connected");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  while (w4c != NULL) {
 | 
	
		
			
				|  |  | -    waiting_for_connect *next = w4c->next;
 | 
	
		
			
				|  |  | -    grpc_exec_ctx_enqueue(exec_ctx, &w4c->continuation, 1);
 | 
	
		
			
				|  |  | -    w4c = next;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    gpr_free((void *)filters);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (destroy_connection != NULL) {
 | 
	
		
			
				|  |  | -    connection_destroy(exec_ctx, destroy_connection);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* Generate a random number between 0 and 1. */
 | 
	
	
		
			
				|  | @@ -742,29 +578,31 @@ static void on_alarm(grpc_exec_ctx *exec_ctx, void *arg, int iomgr_success) {
 | 
	
		
			
				|  |  |    if (c->disconnected) {
 | 
	
		
			
				|  |  |      iomgr_success = 0;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  connectivity_state_changed_locked(exec_ctx, c, "alarm");
 | 
	
		
			
				|  |  |    gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  |    if (iomgr_success) {
 | 
	
		
			
				|  |  |      update_reconnect_parameters(c);
 | 
	
		
			
				|  |  |      continue_connect(exec_ctx, c);
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  | -    cancel_waiting_calls(exec_ctx, c, iomgr_success);
 | 
	
		
			
				|  |  | -    GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->master, "connecting");
 | 
	
		
			
				|  |  | -    GRPC_SUBCHANNEL_UNREF(exec_ctx, c, "connecting");
 | 
	
		
			
				|  |  | +    GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  |                                   int iomgr_success) {
 | 
	
		
			
				|  |  |    grpc_subchannel *c = arg;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    if (c->connecting_result.transport != NULL) {
 | 
	
		
			
				|  |  |      publish_transport(exec_ctx, c);
 | 
	
		
			
				|  |  | +  } else if (c->disconnected) {
 | 
	
		
			
				|  |  | +    GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting");
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  |      gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
 | 
	
		
			
				|  |  |      gpr_mu_lock(&c->mu);
 | 
	
		
			
				|  |  |      GPR_ASSERT(!c->have_alarm);
 | 
	
		
			
				|  |  |      c->have_alarm = 1;
 | 
	
		
			
				|  |  | -    connectivity_state_changed_locked(exec_ctx, c, "connect_failed");
 | 
	
		
			
				|  |  | +    grpc_connectivity_state_set(exec_ctx, &c->state_tracker,
 | 
	
		
			
				|  |  | +                                GRPC_CHANNEL_TRANSIENT_FAILURE,
 | 
	
		
			
				|  |  | +                                "connect_failed");
 | 
	
		
			
				|  |  |      grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, on_alarm, c, now);
 | 
	
		
			
				|  |  |      gpr_mu_unlock(&c->mu);
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -781,29 +619,6 @@ static gpr_timespec compute_connect_deadline(grpc_subchannel *c) {
 | 
	
		
			
				|  |  |                                                            : min_deadline;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static grpc_connectivity_state compute_connectivity_locked(grpc_subchannel *c) {
 | 
	
		
			
				|  |  | -  if (c->disconnected) {
 | 
	
		
			
				|  |  | -    return GRPC_CHANNEL_FATAL_FAILURE;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (c->connecting) {
 | 
	
		
			
				|  |  | -    if (c->have_alarm) {
 | 
	
		
			
				|  |  | -      return GRPC_CHANNEL_TRANSIENT_FAILURE;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    return GRPC_CHANNEL_CONNECTING;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (c->active) {
 | 
	
		
			
				|  |  | -    return GRPC_CHANNEL_READY;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  return GRPC_CHANNEL_IDLE;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -static void connectivity_state_changed_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                              grpc_subchannel *c,
 | 
	
		
			
				|  |  | -                                              const char *reason) {
 | 
	
		
			
				|  |  | -  grpc_connectivity_state current = compute_connectivity_locked(c);
 | 
	
		
			
				|  |  | -  grpc_connectivity_state_set(exec_ctx, &c->state_tracker, current, reason);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  /*
 | 
	
		
			
				|  |  |   * grpc_subchannel_call implementation
 | 
	
		
			
				|  |  |   */
 | 
	
	
		
			
				|  | @@ -811,37 +626,22 @@ static void connectivity_state_changed_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |  static void subchannel_call_destroy(grpc_exec_ctx *exec_ctx, void *call,
 | 
	
		
			
				|  |  |                                      int success) {
 | 
	
		
			
				|  |  |    grpc_subchannel_call *c = call;
 | 
	
		
			
				|  |  | -  gpr_mu *mu = &c->connection->subchannel->mu;
 | 
	
		
			
				|  |  | -  grpc_subchannel *destroy;
 | 
	
		
			
				|  |  |    GPR_TIMER_BEGIN("grpc_subchannel_call_unref.destroy", 0);
 | 
	
		
			
				|  |  |    grpc_call_stack_destroy(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c));
 | 
	
		
			
				|  |  | -  gpr_mu_lock(mu);
 | 
	
		
			
				|  |  | -  destroy = CONNECTION_UNREF_LOCKED(exec_ctx, c->connection, "call");
 | 
	
		
			
				|  |  | -  gpr_mu_unlock(mu);
 | 
	
		
			
				|  |  | +  GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, c->connection, "subchannel_call");
 | 
	
		
			
				|  |  |    gpr_free(c);
 | 
	
		
			
				|  |  | -  if (destroy != NULL) {
 | 
	
		
			
				|  |  | -    subchannel_destroy(exec_ctx, destroy);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |    GPR_TIMER_END("grpc_subchannel_call_unref.destroy", 0);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void grpc_subchannel_call_ref(grpc_subchannel_call *c
 | 
	
		
			
				|  |  |                                    GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | -#ifdef GRPC_STREAM_REFCOUNT_DEBUG
 | 
	
		
			
				|  |  | -  grpc_call_stack_ref(SUBCHANNEL_CALL_TO_CALL_STACK(c), reason);
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -  grpc_call_stack_ref(SUBCHANNEL_CALL_TO_CALL_STACK(c));
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | +  GRPC_CALL_STACK_REF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |                                  grpc_subchannel_call *c
 | 
	
		
			
				|  |  |                                      GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
 | 
	
		
			
				|  |  | -#ifdef GRPC_STREAM_REFCOUNT_DEBUG
 | 
	
		
			
				|  |  | -  grpc_call_stack_unref(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c), reason);
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -  grpc_call_stack_unref(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c));
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | +  GRPC_CALL_STACK_UNREF(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  char *grpc_subchannel_call_get_peer(grpc_exec_ctx *exec_ctx,
 | 
	
	
		
			
				|  | @@ -859,24 +659,26 @@ void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    top_elem->filter->start_transport_stream_op(exec_ctx, top_elem, op);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static grpc_subchannel_call *create_call(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | -                                         connection *con,
 | 
	
		
			
				|  |  | -                                         grpc_pollset *pollset) {
 | 
	
		
			
				|  |  | +grpc_connected_subchannel *grpc_subchannel_get_connected_subchannel(
 | 
	
		
			
				|  |  | +    grpc_subchannel *c) {
 | 
	
		
			
				|  |  | +  return GET_CONNECTED_SUBCHANNEL(c, acq);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +grpc_subchannel_call *grpc_connected_subchannel_create_call(
 | 
	
		
			
				|  |  | +    grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con,
 | 
	
		
			
				|  |  | +    grpc_pollset *pollset) {
 | 
	
		
			
				|  |  |    grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con);
 | 
	
		
			
				|  |  |    grpc_subchannel_call *call =
 | 
	
		
			
				|  |  |        gpr_malloc(sizeof(grpc_subchannel_call) + chanstk->call_stack_size);
 | 
	
		
			
				|  |  |    grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(call);
 | 
	
		
			
				|  |  |    call->connection = con;
 | 
	
		
			
				|  |  | +  GRPC_CONNECTED_SUBCHANNEL_REF(con, "subchannel_call");
 | 
	
		
			
				|  |  |    grpc_call_stack_init(exec_ctx, chanstk, 1, subchannel_call_destroy, call,
 | 
	
		
			
				|  |  |                         NULL, NULL, callstk);
 | 
	
		
			
				|  |  |    grpc_call_stack_set_pollset(exec_ctx, callstk, pollset);
 | 
	
		
			
				|  |  |    return call;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel) {
 | 
	
		
			
				|  |  | -  return subchannel->master;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  grpc_call_stack *grpc_subchannel_call_get_call_stack(
 | 
	
		
			
				|  |  |      grpc_subchannel_call *subchannel_call) {
 | 
	
		
			
				|  |  |    return SUBCHANNEL_CALL_TO_CALL_STACK(subchannel_call);
 |