mirror of
https://github.com/golang/go
synced 2024-10-01 05:18:33 -06:00
Refactor exvar to use interface types, and add mapVar.
R=r APPROVED=r DELTA=170 (136 added, 6 deleted, 28 changed) OCL=27628 CL=27652
This commit is contained in:
parent
d5fa81e1a9
commit
de489fb38c
137
src/lib/exvar.go
137
src/lib/exvar.go
@ -11,44 +11,141 @@ import (
|
||||
"sync";
|
||||
)
|
||||
|
||||
// If mismatched names are used (e.g. calling IncrementInt on a mapVar), the
|
||||
// var name is silently mapped to these. We will consider variables starting
|
||||
// with reservedPrefix to be reserved by this package, and so we avoid the
|
||||
// possibility of a user doing IncrementInt("x-mismatched-map", 1).
|
||||
// TODO(dsymonds): Enforce this.
|
||||
const (
|
||||
reservedPrefix = "x-";
|
||||
mismatchedInt = reservedPrefix + "mismatched-int";
|
||||
mismatchedMap = reservedPrefix + "mismatched-map";
|
||||
)
|
||||
|
||||
// exVar is an abstract type for all exported variables.
|
||||
type exVar interface {
|
||||
String() string;
|
||||
}
|
||||
|
||||
// intVar is an integer variable, and satisfies the exVar interface.
|
||||
type intVar int;
|
||||
|
||||
func (i intVar) String() string {
|
||||
return fmt.Sprint(int(i))
|
||||
}
|
||||
|
||||
// mapVar is a map variable, and satisfies the exVar interface.
|
||||
type mapVar map[string] int;
|
||||
|
||||
func (m mapVar) String() string {
|
||||
s := "map:x"; // TODO(dsymonds): the 'x' should be user-specified!
|
||||
for k, v := range m {
|
||||
s += fmt.Sprintf(" %s:%v", k, v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// TODO(dsymonds):
|
||||
// - string-valued vars
|
||||
// - dynamic lookup vars (via chan?)
|
||||
|
||||
// Global state.
|
||||
var (
|
||||
mutex sync.Mutex;
|
||||
intVars = make(map[string] int);
|
||||
mapVars = make(map[string] map[string] int);
|
||||
// TODO(dsymonds):
|
||||
// - string-valued vars
|
||||
// - docstrings
|
||||
// - dynamic lookup vars (via chan)
|
||||
vars = make(map[string] exVar);
|
||||
// TODO(dsymonds): docstrings
|
||||
)
|
||||
|
||||
// Increment adds inc to the var called name.
|
||||
func Increment(name string, inc int) {
|
||||
// getOrInitIntVar either gets or initializes an intVar called name.
|
||||
// Callers should already be holding the mutex.
|
||||
func getOrInitIntVar(name string) *intVar {
|
||||
if v, ok := vars[name]; ok {
|
||||
// Existing var
|
||||
if iv, ok := v.(*intVar); ok {
|
||||
return iv
|
||||
}
|
||||
// Type mismatch.
|
||||
return getOrInitIntVar(mismatchedInt)
|
||||
}
|
||||
// New var
|
||||
iv := new(intVar);
|
||||
vars[name] = iv;
|
||||
return iv
|
||||
}
|
||||
|
||||
// getOrInitMapVar either gets or initializes a mapVar called name.
|
||||
// Callers should already be holding the mutex.
|
||||
func getOrInitMapVar(name string) *mapVar {
|
||||
if v, ok := vars[name]; ok {
|
||||
// Existing var
|
||||
if mv, ok := v.(*mapVar); ok {
|
||||
return mv
|
||||
}
|
||||
// Type mismatch.
|
||||
return getOrInitMapVar(mismatchedMap)
|
||||
}
|
||||
// New var
|
||||
var m mapVar = make(map[string] int);
|
||||
vars[name] = &m;
|
||||
return &m
|
||||
}
|
||||
|
||||
// IncrementInt adds inc to the integer-valued var called name.
|
||||
func IncrementInt(name string, inc int) {
|
||||
mutex.Lock();
|
||||
defer mutex.Unlock();
|
||||
|
||||
if x, ok := intVars[name]; ok {
|
||||
intVars[name] += inc
|
||||
*getOrInitIntVar(name) += inc
|
||||
}
|
||||
|
||||
// IncrementMap adds inc to the keyed value in the map-valued var called name.
|
||||
func IncrementMap(name string, key string, inc int) {
|
||||
mutex.Lock();
|
||||
defer mutex.Unlock();
|
||||
|
||||
mv := getOrInitMapVar(name);
|
||||
// TODO(dsymonds): Change this to just mv[key] when bug143 is fixed.
|
||||
if v, ok := (*mv)[key]; ok {
|
||||
mv[key] += inc
|
||||
} else {
|
||||
intVars[name] = inc
|
||||
mv[key] = inc
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets the var called name to value.
|
||||
func Set(name string, value int) {
|
||||
// SetInt sets the integer-valued var called name to value.
|
||||
func SetInt(name string, value int) {
|
||||
mutex.Lock();
|
||||
defer mutex.Unlock();
|
||||
|
||||
intVars[name] = value
|
||||
*getOrInitIntVar(name) = value
|
||||
}
|
||||
|
||||
// Get retrieves an integer-valued var called name.
|
||||
func Get(name string) (x int, ok bool) {
|
||||
x, ok = intVars[name];
|
||||
return
|
||||
// SetMap sets the keyed value in the map-valued var called name.
|
||||
func SetMap(name string, key string, value int) {
|
||||
mutex.Lock();
|
||||
defer mutex.Unlock();
|
||||
|
||||
getOrInitMapVar(name)[key] = value
|
||||
}
|
||||
|
||||
// TODO(dsymonds): Functions for map-valued vars.
|
||||
// GetInt retrieves an integer-valued var called name.
|
||||
func GetInt(name string) int {
|
||||
mutex.Lock();
|
||||
defer mutex.Unlock();
|
||||
|
||||
return *getOrInitIntVar(name)
|
||||
}
|
||||
|
||||
// GetMap retrieves the keyed value for a map-valued var called name.
|
||||
func GetMap(name string, key string) int {
|
||||
mutex.Lock();
|
||||
defer mutex.Unlock();
|
||||
|
||||
// TODO(dsymonds): Change this to just getOrInitMapVar(name)[key] when
|
||||
// bug143 is fixed.
|
||||
x, ok := (*getOrInitMapVar(name))[key];
|
||||
return x
|
||||
}
|
||||
|
||||
// String produces a string of all the vars in textual format.
|
||||
func String() string {
|
||||
@ -56,7 +153,7 @@ func String() string {
|
||||
defer mutex.Unlock();
|
||||
|
||||
s := "";
|
||||
for name, value := range intVars {
|
||||
for name, value := range vars {
|
||||
s += fmt.Sprintln(name, value)
|
||||
}
|
||||
return s
|
||||
|
@ -11,19 +11,17 @@ import (
|
||||
)
|
||||
|
||||
func TestSimpleCounter(t *testing.T) {
|
||||
// Unknown exvar should be zero, and return !ok.
|
||||
x, ok := Get("requests");
|
||||
if x != 0 || ok {
|
||||
t.Errorf("Get(nonexistent) = (%v, %v), want (%v, %v)",
|
||||
x, ok, 0, false)
|
||||
// Unknown exvar should be zero.
|
||||
x := GetInt("requests");
|
||||
if x != 0 {
|
||||
t.Errorf("Get(nonexistent) = %v, want 0", x)
|
||||
}
|
||||
|
||||
Increment("requests", 1);
|
||||
Increment("requests", 3);
|
||||
x, ok = Get("requests");
|
||||
if x != 4 || !ok {
|
||||
t.Errorf("Get('requests') = (%v, %v), want (%v, %v)",
|
||||
x, ok, 4, true)
|
||||
IncrementInt("requests", 1);
|
||||
IncrementInt("requests", 3);
|
||||
x = GetInt("requests");
|
||||
if x != 4 {
|
||||
t.Errorf("Get('requests') = %v, want 4", x)
|
||||
}
|
||||
|
||||
out := String();
|
||||
@ -33,22 +31,57 @@ func TestSimpleCounter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMismatchedCounters(t *testing.T) {
|
||||
// Make sure some vars exist.
|
||||
GetInt("requests");
|
||||
GetMap("colours", "red");
|
||||
|
||||
IncrementInt("colours", 1);
|
||||
if x := GetInt("x-mismatched-int"); x != 1 {
|
||||
t.Errorf("GetInt('x-mismatched-int') = %v, want 1", x)
|
||||
}
|
||||
|
||||
IncrementMap("requests", "orange", 1);
|
||||
if x := GetMap("x-mismatched-map", "orange"); x != 1 {
|
||||
t.Errorf("GetMap('x-mismatched-int', 'orange') = %v, want 1", x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapCounter(t *testing.T) {
|
||||
// Unknown exvar should be zero.
|
||||
if x := GetMap("colours", "red"); x != 0 {
|
||||
t.Errorf("GetMap(non, existent) = %v, want 0", x)
|
||||
}
|
||||
|
||||
IncrementMap("colours", "red", 1);
|
||||
IncrementMap("colours", "red", 2);
|
||||
IncrementMap("colours", "blue", 4);
|
||||
if x := GetMap("colours", "red"); x != 3 {
|
||||
t.Errorf("GetMap('colours', 'red') = %v, want 3", x)
|
||||
}
|
||||
if x := GetMap("colours", "blue"); x != 4 {
|
||||
t.Errorf("GetMap('colours', 'blue') = %v, want 4", x)
|
||||
}
|
||||
|
||||
// TODO(dsymonds): Test String()
|
||||
}
|
||||
|
||||
func hammer(name string, total int, done chan <- int) {
|
||||
for i := 0; i < total; i++ {
|
||||
Increment(name, 1)
|
||||
IncrementInt(name, 1)
|
||||
}
|
||||
done <- 1
|
||||
}
|
||||
|
||||
func TestHammer(t *testing.T) {
|
||||
Set("hammer-times", 0);
|
||||
SetInt("hammer-times", 0);
|
||||
sync := make(chan int);
|
||||
hammer_times := int(1e5);
|
||||
go hammer("hammer-times", hammer_times, sync);
|
||||
go hammer("hammer-times", hammer_times, sync);
|
||||
<-sync;
|
||||
<-sync;
|
||||
if final, ok := Get("hammer-times"); final != 2 * hammer_times {
|
||||
if final := GetInt("hammer-times"); final != 2 * hammer_times {
|
||||
t.Errorf("hammer-times = %v, want %v", final, 2 * hammer_times)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user