Repository: grpc/grpc-go
File: clientconn.go (Go)

Code before:
	if err := cc.idlenessMgr.ExitIdleMode(); err != nil {
		cc.addTraceEvent(err.Error())
		return
	}
	// If the ClientConn was not in idle mode, we need to call ExitIdle on the
	// LB policy so that connections can be created.
	cc.mu.Lock()
	cc.balancerWrapper.exitIdle()
	cc.mu.Unlock()
}

// waitForResolvedAddrs blocks until the resolver provides addresses or the
// context expires, whichever happens first.
//
// The returned boolean indicates whether the function had to wait for
// resolution (true) or not (false). If name resolution was delayed (the
// first address update was not received immediately), the function blocks
// and returns `true` once resolution happens. Otherwise, if the resolver
// had already provided addresses before, or an error occurs, it returns
// false.
//
// If firstResolveEvent.HasFired() is true, resolution has already occurred
// in the past, so the function immediately returns false.
//
// If the function blocks and waits for firstResolveEvent.Done(),
// resolution was delayed, and it returns true when the update arrives.
//
// If the context expires or the connection is closing, it returns `false`
// along with the error.
func (cc *ClientConn) waitForResolvedAddrs(ctx context.Context) (bool, error) {
	// This is on the RPC path, so we use a fast path to avoid the
	// more-expensive "select" below after the resolver has returned once.
	if cc.firstResolveEvent.HasFired() {
		return false, nil
	}
	select {
	case <-cc.firstResolveEvent.Done():
		return true, nil
	case <-ctx.Done():
		return false, status.FromContextError(ctx.Err()).Err()
	case <-cc.ctx.Done():
		return false, ErrClientConnClosing
	}
}

var emptyServiceConfig *ServiceConfig

func init() {
	cfg := parseServiceConfig("{}", defaultMaxCallAttempts)
	if cfg.Err != nil {
		panic(fmt.Sprintf("impossible error parsing empty service config: %v", cfg.Err))

Review comment (bug):
```
// waitForResolvedAddrs blocks until the resolver provides addresses or the
// context expires, whichever happens first. Error is nil unless the context expires first; otherwise
// returns a status error based on the context. The returned boolean indicates
// whether it did block or not. If the resolution has already happened once
// before, it returns false without blocking. Otherwise, it wait for the resolution
// and return true if resolution succeeded or false along with error if resolution failed.
```

Code after:
// GetState returns the connectivity.State of ClientConn.
func (cc *ClientConn) GetState() connectivity.State {
	return cc.csMgr.getState()
}

// Connect causes all subchannels in the ClientConn to attempt to connect if
// the channel is idle.  Does not wait for the connection attempts to begin
// before returning.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
// release.
func (cc *ClientConn) Connect() {
	if err := cc.idlenessMgr.ExitIdleMode(); err != nil {
		cc.addTraceEvent(err.Error())
		return
	}
	// If the ClientConn was not in idle mode, we need to call ExitIdle on the
	// LB policy so that connections can be created.
	cc.mu.Lock()
	cc.balancerWrapper.exitIdle()
	cc.mu.Unlock()
}

// waitForResolvedAddrs blocks until the resolver provides addresses or the
// context expires, whichever happens first.
//
// Error is nil unless the context expires first; otherwise returns a status
// error based on the context.
//
// The returned boolean indicates whether it did block or not. If the
// resolution has already happened once before, it returns false without
// blocking. Otherwise, it wait for the resolution and return true if
// resolution has succeeded or return false along with error if resolution has
// failed.
func (cc *ClientConn) waitForResolvedAddrs(ctx context.Context) (bool, error) {
	// This is on the RPC path, so we use a fast path to avoid the
	// more-expensive "select" below after the resolver has returned once.
	if cc.firstResolveEvent.HasFired() {
		return false, nil
	}
	internal.NewStreamWaitingForResolver()
	select {
	case <-cc.firstResolveEvent.Done():
		return true, nil
	case <-ctx.Done():
		return false, status.FromContextError(ctx.Err()).Err()
	case <-cc.ctx.Done():
		return false, ErrClientConnClosing
	}