Commit de489fb3 authored by David Symonds's avatar David Symonds

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
parent d5fa81e1
...@@ -11,44 +11,141 @@ import ( ...@@ -11,44 +11,141 @@ import (
"sync"; "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. // Global state.
var ( var (
mutex sync.Mutex; mutex sync.Mutex;
intVars = make(map[string] int); vars = make(map[string] exVar);
mapVars = make(map[string] map[string] int); // TODO(dsymonds): docstrings
// TODO(dsymonds):
// - string-valued vars
// - docstrings
// - dynamic lookup vars (via chan)
) )
// Increment adds inc to the var called name. // getOrInitIntVar either gets or initializes an intVar called name.
func Increment(name string, inc int) { // 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(); mutex.Lock();
defer mutex.Unlock(); defer mutex.Unlock();
if x, ok := intVars[name]; ok { *getOrInitIntVar(name) += inc
intVars[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 { } else {
intVars[name] = inc mv[key] = inc
} }
} }
// Set sets the var called name to value. // SetInt sets the integer-valued var called name to value.
func Set(name string, value int) { func SetInt(name string, value int) {
mutex.Lock();
defer mutex.Unlock();
*getOrInitIntVar(name) = value
}
// SetMap sets the keyed value in the map-valued var called name.
func SetMap(name string, key string, value int) {
mutex.Lock(); mutex.Lock();
defer mutex.Unlock(); defer mutex.Unlock();
intVars[name] = value getOrInitMapVar(name)[key] = value
} }
// Get retrieves an integer-valued var called name. // GetInt retrieves an integer-valued var called name.
func Get(name string) (x int, ok bool) { func GetInt(name string) int {
x, ok = intVars[name]; mutex.Lock();
return defer mutex.Unlock();
return *getOrInitIntVar(name)
} }
// TODO(dsymonds): Functions for map-valued vars. // 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. // String produces a string of all the vars in textual format.
func String() string { func String() string {
...@@ -56,7 +153,7 @@ func String() string { ...@@ -56,7 +153,7 @@ func String() string {
defer mutex.Unlock(); defer mutex.Unlock();
s := ""; s := "";
for name, value := range intVars { for name, value := range vars {
s += fmt.Sprintln(name, value) s += fmt.Sprintln(name, value)
} }
return s return s
......
...@@ -11,19 +11,17 @@ import ( ...@@ -11,19 +11,17 @@ import (
) )
func TestSimpleCounter(t *testing.T) { func TestSimpleCounter(t *testing.T) {
// Unknown exvar should be zero, and return !ok. // Unknown exvar should be zero.
x, ok := Get("requests"); x := GetInt("requests");
if x != 0 || ok { if x != 0 {
t.Errorf("Get(nonexistent) = (%v, %v), want (%v, %v)", t.Errorf("Get(nonexistent) = %v, want 0", x)
x, ok, 0, false)
} }
Increment("requests", 1); IncrementInt("requests", 1);
Increment("requests", 3); IncrementInt("requests", 3);
x, ok = Get("requests"); x = GetInt("requests");
if x != 4 || !ok { if x != 4 {
t.Errorf("Get('requests') = (%v, %v), want (%v, %v)", t.Errorf("Get('requests') = %v, want 4", x)
x, ok, 4, true)
} }
out := String(); out := String();
...@@ -33,22 +31,57 @@ func TestSimpleCounter(t *testing.T) { ...@@ -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) { func hammer(name string, total int, done chan <- int) {
for i := 0; i < total; i++ { for i := 0; i < total; i++ {
Increment(name, 1) IncrementInt(name, 1)
} }
done <- 1 done <- 1
} }
func TestHammer(t *testing.T) { func TestHammer(t *testing.T) {
Set("hammer-times", 0); SetInt("hammer-times", 0);
sync := make(chan int); sync := make(chan int);
hammer_times := int(1e5); hammer_times := int(1e5);
go hammer("hammer-times", hammer_times, sync); go hammer("hammer-times", hammer_times, sync);
go hammer("hammer-times", hammer_times, sync); go hammer("hammer-times", hammer_times, sync);
<-sync; <-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) t.Errorf("hammer-times = %v, want %v", final, 2 * hammer_times)
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment