Skip to content

Commit

Permalink
internal: fix deadlock during switch_balancer and NewSubConn() (#4536)
Browse files Browse the repository at this point in the history
  • Loading branch information
menghanl committed Jun 11, 2021
1 parent 2d3b1f9 commit 4554924
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 0 deletions.
5 changes: 5 additions & 0 deletions clientconn.go
Expand Up @@ -711,7 +711,12 @@ func (cc *ClientConn) switchBalancer(name string) {
return
}
if cc.balancerWrapper != nil {
// Don't hold cc.mu while closing the balancers. The balancers may call
// methods that require cc.mu (e.g. cc.NewSubConn()). Holding the mutex
// would cause a deadlock in that case.
cc.mu.Unlock()
cc.balancerWrapper.close()
cc.mu.Lock()
}

builder := balancer.Get(name)
Expand Down
10 changes: 10 additions & 0 deletions resolver_conn_wrapper.go
Expand Up @@ -39,6 +39,8 @@ type ccResolverWrapper struct {
resolver resolver.Resolver
done *grpcsync.Event
curState resolver.State

incomingMu sync.Mutex // Synchronizes all the incoming calls.
}

// newCCResolverWrapper uses the resolver.Builder to build a Resolver and
Expand Down Expand Up @@ -90,6 +92,8 @@ func (ccr *ccResolverWrapper) close() {
}

func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
ccr.incomingMu.Lock()
defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() {
return nil
}
Expand All @@ -105,6 +109,8 @@ func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
}

func (ccr *ccResolverWrapper) ReportError(err error) {
ccr.incomingMu.Lock()
defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() {
return
}
Expand All @@ -114,6 +120,8 @@ func (ccr *ccResolverWrapper) ReportError(err error) {

// NewAddress is called by the resolver implementation to send addresses to gRPC.
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
ccr.incomingMu.Lock()
defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() {
return
}
Expand All @@ -128,6 +136,8 @@ func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
// NewServiceConfig is called by the resolver implementation to send service
// configs to gRPC.
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
ccr.incomingMu.Lock()
defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() {
return
}
Expand Down

0 comments on commit 4554924

Please sign in to comment.