Protect stats with RWMutex (#151)

* Protect stats with RWMutex

updateStats() now also updates rtts so it is protected by the
lock.
Statistics() should now be callable from other goroutines.

PacketsSent and PacketsRecvDuplicates can still be updated while
Statistics() is running, and thus could yield strange results
(sent<recv)

To fix this, atomics could be used, but it would require changing the
types of the fields from int to int64, thus changing the public api.

Signed-off-by: Jean Raby <jean@raby.sh>
This commit is contained in:
Jean Raby 2021-03-12 03:51:07 -05:00 committed by GitHub
parent 5f9dd908cc
commit d90f3778a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

18
ping.go
View File

@ -143,6 +143,7 @@ type Pinger struct {
avgRtt time.Duration
stdDevRtt time.Duration
stddevm2 time.Duration
statsMu sync.RWMutex
// If true, keep a record of rtts of all received packets.
// Set to false to avoid memory bloat for long running pings.
@ -255,7 +256,14 @@ type Statistics struct {
}
func (p *Pinger) updateStatistics(pkt *Packet) {
p.statsMu.Lock()
defer p.statsMu.Unlock()
p.PacketsRecv++
if p.RecordRtts {
p.rtts = append(p.rtts, pkt.Rtt)
}
if p.PacketsRecv == 1 || pkt.Rtt < p.minRtt {
p.minRtt = pkt.Rtt
}
@ -451,9 +459,12 @@ func (p *Pinger) finish() {
// pinger is running or after it is finished. OnFinish calls this function to
// get it's finished statistics.
func (p *Pinger) Statistics() *Statistics {
loss := float64(p.PacketsSent-p.PacketsRecv) / float64(p.PacketsSent) * 100
p.statsMu.RLock()
defer p.statsMu.RUnlock()
sent := p.PacketsSent
loss := float64(sent-p.PacketsRecv) / float64(sent) * 100
s := Statistics{
PacketsSent: p.PacketsSent,
PacketsSent: sent,
PacketsRecv: p.PacketsRecv,
PacketsRecvDuplicates: p.PacketsRecvDuplicates,
PacketLoss: loss,
@ -582,9 +593,6 @@ func (p *Pinger) processPacket(recv *packet) error {
return fmt.Errorf("invalid ICMP echo reply; type: '%T', '%v'", pkt, pkt)
}
if p.RecordRtts {
p.rtts = append(p.rtts, inPkt.Rtt)
}
handler := p.OnRecv
if handler != nil {
handler(inPkt)