blob: 083c68eb76ad0d59250a6d601985f379b4bb85f5 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package stats
import (
"sync"
"time"
)
// NewIntegerFunc creates a new StatsObject with the given name. The function
// argument must return an int64 value.
func NewIntegerFunc(name string, function func() int64) StatsObject {
return newFunc(name, func() interface{} { return function() })
}
// NewFloatFunc creates a new StatsObject with the given name. The function
// argument must return a float64 value.
func NewFloatFunc(name string, function func() float64) StatsObject {
return newFunc(name, func() interface{} { return function() })
}
// NewStringFunc creates a new StatsObject with the given name. The function
// argument must return a string value.
func NewStringFunc(name string, function func() string) StatsObject {
return newFunc(name, func() interface{} { return function() })
}
func newFunc(name string, function func() interface{}) StatsObject {
f := funcType{function: function}
lock.Lock()
defer lock.Unlock()
node := findNodeLocked(name, true)
node.object = &f
return &f
}
// funcType implements the StatsObject interface by calling a user provided
// function.
type funcType struct {
mu sync.Mutex
function func() interface{}
waiters []chan interface{} // GUARDED_BY(mu)
lastValue interface{} // GUARDED_BY(mu)
}
// LastUpdate returns always returns the current time for this type of
// StatsObject because Value() is expected to get a current (fresh) value.
func (f *funcType) LastUpdate() time.Time {
return time.Now()
}
// Value returns the value returned by the object's function. If the function
// takes more than 100 ms to return, the last value is used.
func (f *funcType) Value() interface{} {
// There are two values that can be written to the channel, one from
// fetch() and one from time.AfterFunc(). In some cases, they will both
// be written but only one will be read. A buffer size of 1 would be
// sufficient to avoid deadlocks, but 2 will guarantee that fetch()
// never blocks on a channel.
ch := make(chan interface{}, 2)
f.mu.Lock()
if f.waiters = append(f.waiters, ch); len(f.waiters) == 1 {
go f.fetch()
}
f.mu.Unlock()
defer time.AfterFunc(100*time.Millisecond, func() {
f.mu.Lock()
defer f.mu.Unlock()
ch <- f.lastValue
}).Stop()
return <-ch
}
func (f *funcType) fetch() {
v := f.function()
f.mu.Lock()
waiters := f.waiters
f.waiters = nil
f.lastValue = v
f.mu.Unlock()
for _, c := range waiters {
c <- v
}
}