github.com/decred/dcrlnd@v0.7.6/peer/forcedpings.go (about) 1 package peer 2 3 import ( 4 "context" 5 "fmt" 6 "sync/atomic" 7 "time" 8 ) 9 10 // EnforcePing attempts to send a ping to the peer and wait for the response. If 11 // the passed context is canceled before the response is received, then the 12 // peer is forced to disconnect. 13 func (p *Brontide) EnforcePing(ctx context.Context) (time.Duration, error) { 14 c := make(chan time.Duration) 15 p.enforcePingMtx.Lock() 16 needsPing := len(p.enforcePongChans) == 0 17 p.enforcePongChans = append(p.enforcePongChans, c) 18 p.enforcePingMtx.Unlock() 19 20 if needsPing { 21 go func() { 22 select { 23 case <-p.quit: 24 case p.enforcePingChan <- struct{}{}: 25 } 26 }() 27 } 28 29 select { 30 case <-p.quit: 31 return 0, fmt.Errorf("peer has quitted") 32 case <-ctx.Done(): 33 // Context closed before a reply was received. Disconnect the 34 // peer. 35 err := fmt.Errorf("enforced ping context error: %v", ctx.Err()) 36 p.Disconnect(err) 37 return 0, err 38 case res := <-c: 39 return res, nil 40 } 41 } 42 43 // handlePong calls the forced ping handlers with either the error or the 44 // current ping time. 45 func (p *Brontide) handlePong() { 46 p.enforcePingMtx.Lock() 47 pongChans := p.enforcePongChans 48 p.enforcePongChans = nil 49 p.enforcePingMtx.Unlock() 50 51 microPT := atomic.LoadInt64(&p.pingTime) 52 pingTime := time.Duration(microPT) * time.Microsecond 53 54 for _, c := range pongChans { 55 c := c 56 go func() { c <- pingTime }() 57 } 58 }