blob: 1ccaf92cbb3cc44589be7f79f0fc3624acdbec37 [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 implements a global repository of stats objects. Each object
// has a name and a value.
// Example:
// bar1 := stats.NewInteger("foo/bar1")
// bar2 := stats.NewFloat("foo/bar2")
// bar3 := stats.NewCounter("foo/bar3")
// bar1.Set(1)
// bar2.Set(2)
// bar3.Set(3)
// The values can be retrieved with:
// v, err := stats.Value("foo/bar1")
package stats
import (
"strings"
"sync"
"time"
"v.io/v23/services/stats"
"v.io/v23/verror"
)
// StatsObject is the interface for objects stored in the stats repository.
type StatsObject interface {
// LastUpdate is used by WatchGlob to decide which updates to send.
LastUpdate() time.Time
// Value returns the current value of the object.
Value() interface{}
}
type node struct {
object StatsObject
children map[string]*node
}
var (
lock sync.RWMutex
repository *node // GUARDED_BY(lock)
)
func init() {
repository = newNode()
}
// GetStatsObject returns the object with that given name, or an error if the
// object doesn't exist.
func GetStatsObject(name string) (StatsObject, error) {
lock.RLock()
defer lock.RUnlock()
node := findNodeLocked(name, false)
if node == nil || node.object == nil {
return nil, verror.New(verror.ErrNoExist, nil, name)
}
return node.object, nil
}
// Value returns the value of an object, or an error if the object doesn't
// exist.
func Value(name string) (interface{}, error) {
obj, err := GetStatsObject(name)
if err != nil {
return 0, err
}
if obj == nil {
return nil, verror.New(stats.ErrNoValue, nil, name)
}
return obj.Value(), nil
}
// Delete deletes a StatsObject and all its children, if any.
func Delete(name string) error {
if name == "" {
return verror.New(verror.ErrNoExist, nil, name)
}
elems := strings.Split(name, "/")
last := len(elems) - 1
dirname, basename := strings.Join(elems[:last], "/"), elems[last]
lock.Lock()
defer lock.Unlock()
parent := findNodeLocked(dirname, false)
if parent == nil {
return verror.New(verror.ErrNoExist, nil, name)
}
delete(parent.children, basename)
return nil
}
func newNode() *node {
return &node{children: make(map[string]*node)}
}
// findNodeLocked finds a node, and optionally creates it if it doesn't already
// exist.
func findNodeLocked(name string, create bool) *node {
elems := strings.Split(name, "/")
node := repository
for {
if len(elems) == 0 {
return node
}
if len(elems[0]) == 0 {
elems = elems[1:]
continue
}
if next, ok := node.children[elems[0]]; ok {
node = next
elems = elems[1:]
continue
}
if create {
node.children[elems[0]] = newNode()
node = node.children[elems[0]]
elems = elems[1:]
continue
}
return nil
}
}