Use atomics for statistics handling (fixes #45)

This is one of those rare cases where that's actually cleaner, I
think...
This commit is contained in:
Jakob Borg 2016-05-22 09:23:44 +09:00
parent 7b43ba809b
commit 7dddc0de9e

View File

@ -9,13 +9,12 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"sync" "sync/atomic"
"time" "time"
) )
type stats struct { type stats struct {
mut sync.Mutex // Incremented atomically
reset time.Time
announces int64 announces int64
queries int64 queries int64
answers int64 answers int64
@ -23,36 +22,39 @@ type stats struct {
} }
func (s *stats) Announce() { func (s *stats) Announce() {
s.mut.Lock() atomic.AddInt64(&s.announces, 1)
s.announces++
s.mut.Unlock()
} }
func (s *stats) Query() { func (s *stats) Query() {
s.mut.Lock() atomic.AddInt64(&s.queries, 1)
s.queries++
s.mut.Unlock()
} }
func (s *stats) Answer() { func (s *stats) Answer() {
s.mut.Lock() atomic.AddInt64(&s.answers, 1)
s.answers++
s.mut.Unlock()
} }
func (s *stats) Error() { func (s *stats) Error() {
s.mut.Lock() atomic.AddInt64(&s.errors, 1)
s.errors++
s.mut.Unlock()
} }
// Reset returns a copy of the current stats and resets the counters to
// zero.
func (s *stats) Reset() stats { func (s *stats) Reset() stats {
s.mut.Lock() // Create a copy of the stats using atomic reads
ns := *s copy := stats{
s.announces, s.queries, s.answers, s.errors = 0, 0, 0, 0 announces: atomic.LoadInt64(&s.announces),
s.reset = time.Now() queries: atomic.LoadInt64(&s.queries),
s.mut.Unlock() answers: atomic.LoadInt64(&s.answers),
return ns errors: atomic.LoadInt64(&s.errors),
}
// Reset the stats by subtracting the values that we copied
atomic.AddInt64(&s.announces, -copy.announces)
atomic.AddInt64(&s.queries, -copy.queries)
atomic.AddInt64(&s.answers, -copy.answers)
atomic.AddInt64(&s.errors, -copy.errors)
return copy
} }
type statssrv struct { type statssrv struct {
@ -62,11 +64,14 @@ type statssrv struct {
} }
func (s *statssrv) Serve() { func (s *statssrv) Serve() {
lastReset := time.Now()
for { for {
time.Sleep(next(s.intv)) time.Sleep(next(s.intv))
stats := globalStats.Reset() stats := globalStats.Reset()
d := time.Since(stats.reset).Seconds() d := time.Since(lastReset).Seconds()
lastReset = time.Now()
log.Printf("Stats: %.02f announces/s, %.02f queries/s, %.02f answers/s, %.02f errors/s", log.Printf("Stats: %.02f announces/s, %.02f queries/s, %.02f answers/s, %.02f errors/s",
float64(stats.announces)/d, float64(stats.queries)/d, float64(stats.answers)/d, float64(stats.errors)/d) float64(stats.announces)/d, float64(stats.queries)/d, float64(stats.answers)/d, float64(stats.errors)/d)