veyron/lib/stats: Add Map object
Add a Map object to export a set of key/value pairs. The key/value pairs
can be updated atomically.
Export some system stats, including runtime.MemStats which uses the new
Map object.
Change-Id: Ib2cc9ca773df81751f60721115de08389188cfd0
diff --git a/lib/stats/map.go b/lib/stats/map.go
new file mode 100644
index 0000000..89312f6
--- /dev/null
+++ b/lib/stats/map.go
@@ -0,0 +1,165 @@
+package stats
+
+import (
+ "path"
+ "sort"
+ "sync"
+ "time"
+)
+
+// NewMap creates a new Map StatsObject with the given name and
+// returns a pointer to it.
+func NewMap(name string) *Map {
+ lock.Lock()
+ defer lock.Unlock()
+ node := findNodeLocked(name, true)
+ m := Map{name: name, value: make(map[string]mapValue)}
+ node.object = &m
+ return &m
+}
+
+// Map implements the StatsObject interface. The map keys are strings and the
+// values can be bool, int64, uint64, float64, or string.
+type Map struct {
+ mu sync.RWMutex // ACQUIRED_BEFORE(stats.lock)
+ name string
+ value map[string]mapValue // GUARDED_BY(mu)
+}
+
+type mapValue struct {
+ lastUpdate time.Time
+ value interface{}
+}
+
+// Set sets the values of the given keys. There must be exactly one value for
+// each key.
+func (m *Map) Set(kvpairs []KeyValue) {
+ now := time.Now()
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ for _, kv := range kvpairs {
+ var v interface{}
+ switch value := kv.Value.(type) {
+ case bool:
+ v = bool(value)
+ case int:
+ v = int64(value)
+ case int8:
+ v = int64(value)
+ case int16:
+ v = int64(value)
+ case int32:
+ v = int64(value)
+ case int64:
+ v = int64(value)
+ case uint:
+ v = uint64(value)
+ case uint8:
+ v = uint64(value)
+ case uint16:
+ v = uint64(value)
+ case uint32:
+ v = uint64(value)
+ case uint64:
+ v = uint64(value)
+ case float32:
+ v = float64(value)
+ case float64:
+ v = float64(value)
+ case string:
+ v = string(value)
+ default:
+ panic("attempt to use an unsupported type as value")
+ }
+ m.value[kv.Key] = mapValue{now, v}
+ }
+ m.insertMissingNodes()
+}
+
+// Delete deletes the given keys from the map object.
+func (m *Map) Delete(keys []string) {
+ // The lock order is important.
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ lock.Lock()
+ defer lock.Unlock()
+ n := findNodeLocked(m.name, false)
+ for _, k := range keys {
+ delete(m.value, k)
+ if n != nil {
+ delete(n.children, k)
+ }
+ }
+}
+
+// Keys returns a sorted list of all the keys in the map.
+func (m *Map) Keys() []string {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ keys := []string{}
+ for k, _ := range m.value {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ return keys
+}
+
+// LastUpdate always returns a zero-value Time for a Map.
+func (m *Map) LastUpdate() time.Time {
+ return time.Time{}
+}
+
+// Value always returns nil for a Map.
+func (m *Map) Value() interface{} {
+ return nil
+}
+
+// insertMissingNodes inserts all the missing nodes.
+func (m *Map) insertMissingNodes() {
+ missing := []string{}
+ lock.RLock()
+ for key, _ := range m.value {
+ oName := path.Join(m.name, key)
+ if n := findNodeLocked(oName, false); n == nil {
+ missing = append(missing, key)
+ }
+ }
+ lock.RUnlock()
+ if len(missing) == 0 {
+ return
+ }
+
+ lock.Lock()
+ for _, key := range missing {
+ oName := path.Join(m.name, key)
+ if n := findNodeLocked(oName, true); n.object == nil {
+ n.object = &mapValueWrapper{m, key}
+ }
+ }
+ lock.Unlock()
+}
+
+type mapValueWrapper struct {
+ m *Map
+ key string
+}
+
+// LastUpdate returns the time at which the parent map object was last updated.
+func (w *mapValueWrapper) LastUpdate() time.Time {
+ w.m.mu.RLock()
+ defer w.m.mu.RUnlock()
+ if v, ok := w.m.value[w.key]; ok {
+ return v.lastUpdate
+ }
+ return time.Time{}
+}
+
+// Value returns the current value for the map key.
+func (w *mapValueWrapper) Value() interface{} {
+ w.m.mu.RLock()
+ defer w.m.mu.RUnlock()
+ if v, ok := w.m.value[w.key]; ok {
+ return v.value
+ }
+ return nil
+}