| // 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 ( |
| "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 |
| } |