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  }