func (c *clientSession) fanoutRequest(fanoutFn func(int) *fanout, cancelCh chan struct{}) interface{} { defer c.mutex.RUnlock() c.mutex.RLock() rpcClients := c.peerClients numClients := len(rpcClients) f := fanoutFn(numClients) var wg sync.WaitGroup wg.Add(numClients) doneCh := make(chan struct{}) go func() { select { case <-cancelCh: f.respCh.Close() case <-doneCh: f.respCh.Close() } }() go func() { defer close(doneCh) wg.Wait() }() for _, r := range rpcClients { go func(p *peerClient) { // requestCh might be closed because of session being terminated // let go of the wait defer common.RecoverAndDo(func() { wg.Done() }) p.requestCh <- func() { defer wg.Done() defer common.NoopRecoverLog() // respCh might be closed because of session being terminated resp, err := f.reqFn(p) if err != nil { log.Warn(err) } else { c.listener.HandleEvent( common.Event{ Term: resp.Term(), EventType: common.ResponseReceived, }) f.respCh.Send(reflect.ValueOf(resp)) } } }(r) } return f.respCh.Interface() }
func (c *clientSession) removeGrpcClient(id string) { defer c.mutex.Unlock() defer common.NoopRecoverLog() // session is terminating and already closed requestCh and this was invoked concurrently c.mutex.Lock() if peerClient := c.peerClients[id]; peerClient != nil { close(peerClient.requestCh) delete(c.peerClients, id) } }
func (c *clientSession) watchForTerminate() { defer c.mutex.RUnlock() defer common.NoopRecoverLog() // requestCh might be closed by an unhealthy connection in removeGrpcClient <-c.terminateCh c.mutex.RLock() log.Debug("Terminating session") for _, r := range c.peerClients { close(r.requestCh) } c.client.removeSession(c) }