func (s *server) start(trans transport.Transport) (*tomb.Tomb, error) { s.workerTombM.Lock() if s.workerTomb != nil { s.workerTombM.Unlock() return nil, ErrAlreadyRunning } tm := new(tomb.Tomb) s.workerTomb = tm s.workerTombM.Unlock() stop := func() { trans.StopListening(s.Name()) s.workerTombM.Lock() s.workerTomb = nil s.workerTombM.Unlock() } var inbound chan tmsg.Request connect := func() error { select { case <-trans.Ready(): inbound = make(chan tmsg.Request, 500) return trans.Listen(s.Name(), inbound) case <-time.After(connectTimeout): log.Warnf("[Mercury:Server] Timed out after %s waiting for transport readiness", connectTimeout.String()) return ttrans.ErrTimeout } } // Block here purposefully (deliberately not in the goroutine below, because we want to report a connection error // to the caller) if err := connect(); err != nil { stop() return nil, err } tm.Go(func() error { defer stop() for { select { case req, ok := <-inbound: if !ok { // Received because the channel closed; try to reconnect log.Warn("[Mercury:Server] Inbound channel closed; trying to reconnect…") if err := connect(); err != nil { log.Criticalf("[Mercury:Server] Could not reconnect after channel close: %s", err) return err } } else { go s.handle(trans, req) } case <-tm.Dying(): return tomb.ErrDying } } }) return tm, nil }