blob: 083c68eb76ad0d59250a6d601985f379b4bb85f5 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Robin Thellend8cc0aee2014-10-16 10:58:24 -07005package stats
6
7import (
8 "sync"
9 "time"
10)
11
12// NewIntegerFunc creates a new StatsObject with the given name. The function
13// argument must return an int64 value.
14func NewIntegerFunc(name string, function func() int64) StatsObject {
15 return newFunc(name, func() interface{} { return function() })
16}
17
18// NewFloatFunc creates a new StatsObject with the given name. The function
19// argument must return a float64 value.
20func NewFloatFunc(name string, function func() float64) StatsObject {
21 return newFunc(name, func() interface{} { return function() })
22}
23
24// NewStringFunc creates a new StatsObject with the given name. The function
25// argument must return a string value.
26func NewStringFunc(name string, function func() string) StatsObject {
27 return newFunc(name, func() interface{} { return function() })
28}
29
30func newFunc(name string, function func() interface{}) StatsObject {
31 f := funcType{function: function}
32 lock.Lock()
33 defer lock.Unlock()
34 node := findNodeLocked(name, true)
35 node.object = &f
36 return &f
37}
38
39// funcType implements the StatsObject interface by calling a user provided
40// function.
41type funcType struct {
42 mu sync.Mutex
43 function func() interface{}
44 waiters []chan interface{} // GUARDED_BY(mu)
45 lastValue interface{} // GUARDED_BY(mu)
46}
47
48// LastUpdate returns always returns the current time for this type of
49// StatsObject because Value() is expected to get a current (fresh) value.
50func (f *funcType) LastUpdate() time.Time {
51 return time.Now()
52}
53
54// Value returns the value returned by the object's function. If the function
55// takes more than 100 ms to return, the last value is used.
56func (f *funcType) Value() interface{} {
57 // There are two values that can be written to the channel, one from
58 // fetch() and one from time.AfterFunc(). In some cases, they will both
59 // be written but only one will be read. A buffer size of 1 would be
60 // sufficient to avoid deadlocks, but 2 will guarantee that fetch()
61 // never blocks on a channel.
62 ch := make(chan interface{}, 2)
63 f.mu.Lock()
64 if f.waiters = append(f.waiters, ch); len(f.waiters) == 1 {
65 go f.fetch()
66 }
67 f.mu.Unlock()
68
69 defer time.AfterFunc(100*time.Millisecond, func() {
70 f.mu.Lock()
71 defer f.mu.Unlock()
72 ch <- f.lastValue
73 }).Stop()
74
75 return <-ch
76}
77
78func (f *funcType) fetch() {
79 v := f.function()
80
81 f.mu.Lock()
82 waiters := f.waiters
83 f.waiters = nil
84 f.lastValue = v
85 f.mu.Unlock()
86
87 for _, c := range waiters {
88 c <- v
89 }
90}