Created
May 15, 2014 09:45
-
-
Save mix3/8037d5645c738a27755d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"reflect" | |
"runtime" | |
"sync" | |
"testing" | |
) | |
type IMap interface { | |
Get(key string) interface{} | |
All(key string) []interface{} | |
Set(key string, value ...interface{}) | |
Add(key string, value ...interface{}) | |
Del(key string) | |
Itr(key string) *Iterator | |
} | |
type IIterator interface { | |
Next() interface{} | |
} | |
type Map struct { | |
m map[string][]interface{} | |
} | |
func NewMap() *Map { | |
return &Map{m: make(map[string][]interface{})} | |
} | |
func (m *Map) Get(key string) interface{} { | |
if 0 < len(m.m[key]) { | |
return m.m[key][0] | |
} else { | |
return nil | |
} | |
} | |
func (m *Map) All(key string) []interface{} { | |
return m.m[key] | |
} | |
func (m *Map) Set(key string, value ...interface{}) { | |
m.m[key] = append(m.m[key][:0], value...) | |
} | |
func (m *Map) Add(key string, value ...interface{}) { | |
for _, v := range value { | |
m.m[key] = append(m.m[key], v) | |
} | |
} | |
func (m *Map) Del(key string) { | |
delete(m.m, key) | |
} | |
func (m *Map) Itr(key string) *Iterator { | |
return &Iterator{ | |
index: 0, | |
value: m.m[key], | |
} | |
} | |
type Iterator struct { | |
index int | |
value []interface{} | |
} | |
func (i *Iterator) Next() interface{} { | |
var ret interface{} | |
if 0 < len(i.value[i.index:]) { | |
ret = i.value[i.index] | |
i.index++ | |
return ret | |
} | |
return nil | |
} | |
type ThreadSafeMap struct { | |
Map | |
sync.Mutex | |
} | |
func NewThreadSafeMap() *ThreadSafeMap { | |
return &ThreadSafeMap{ | |
*NewMap(), | |
sync.Mutex{}, | |
} | |
} | |
func (m *ThreadSafeMap) Get(key string) interface{} { | |
m.Mutex.Lock() | |
defer m.Mutex.Unlock() | |
return m.Map.Get(key) | |
} | |
func (m *ThreadSafeMap) All(key string) []interface{} { | |
m.Mutex.Lock() | |
defer m.Mutex.Unlock() | |
return m.Map.All(key) | |
} | |
func (m *ThreadSafeMap) Set(key string, value ...interface{}) { | |
m.Mutex.Lock() | |
defer m.Mutex.Unlock() | |
m.Map.Set(key, value...) | |
} | |
func (m *ThreadSafeMap) Add(key string, value ...interface{}) { | |
m.Mutex.Lock() | |
defer m.Mutex.Unlock() | |
m.Map.Add(key, value...) | |
} | |
func (m *ThreadSafeMap) Del(key string) { | |
m.Mutex.Lock() | |
defer m.Mutex.Unlock() | |
m.Map.Del(key) | |
} | |
func (m *ThreadSafeMap) Itr(key string) *Iterator { | |
return m.Map.Itr(key) | |
} | |
func (m *ThreadSafeMap) Atm(key string, f func(value []interface{}) []interface{}) { | |
m.Mutex.Lock() | |
defer m.Mutex.Unlock() | |
m.m[key] = append(m.m[key][:0], f(m.m[key])...) | |
} | |
type ChMutex struct { | |
wait chan int | |
} | |
type Counter struct { | |
count int | |
ChMutex | |
} | |
func (m *ChMutex) Lock() { | |
if m.wait == nil { | |
m.wait = make(chan int, 1) | |
m.wait <- 1 | |
} | |
<-m.wait | |
} | |
func (m *ChMutex) Unlock() { | |
if m.wait == nil { | |
panic("until locked") | |
} | |
m.wait <- 1 | |
} | |
func (c *Counter) Incr() { | |
c.ChMutex.Lock() | |
defer c.ChMutex.Unlock() | |
c.count++ | |
} | |
func (c *Counter) Num() int { | |
return c.count | |
} | |
type GoroutineSafeMap struct { | |
Map | |
ChMutex | |
} | |
func NewGoroutineSafeMap() *GoroutineSafeMap { | |
return &GoroutineSafeMap{ | |
*NewMap(), | |
ChMutex{}, | |
} | |
} | |
func (m *GoroutineSafeMap) Get(key string) interface{} { | |
m.ChMutex.Lock() | |
defer m.ChMutex.Unlock() | |
return m.Map.Get(key) | |
} | |
func (m *GoroutineSafeMap) All(key string) []interface{} { | |
m.ChMutex.Lock() | |
defer m.ChMutex.Unlock() | |
return m.Map.All(key) | |
} | |
func (m *GoroutineSafeMap) Set(key string, value ...interface{}) { | |
m.ChMutex.Lock() | |
defer m.ChMutex.Unlock() | |
m.Map.Set(key, value...) | |
} | |
func (m *GoroutineSafeMap) Add(key string, value ...interface{}) { | |
m.ChMutex.Lock() | |
defer m.ChMutex.Unlock() | |
m.Map.Add(key, value...) | |
} | |
func (m *GoroutineSafeMap) Del(key string) { | |
m.ChMutex.Lock() | |
defer m.ChMutex.Unlock() | |
m.Map.Del(key) | |
} | |
func (m *GoroutineSafeMap) Itr(key string) *Iterator { | |
return m.Map.Itr(key) | |
} | |
func (m *GoroutineSafeMap) Atm(key string, f func(value []interface{}) []interface{}) { | |
m.ChMutex.Lock() | |
defer m.ChMutex.Unlock() | |
m.m[key] = append(m.m[key][:0], f(m.m[key])...) | |
} | |
func main() { | |
log.Printf("start") | |
incr(1) | |
incr(2) | |
threadSafeIncr(1) | |
threadSafeIncr(2) | |
goroutineSafeIncr(1) | |
goroutineSafeIncr(2) | |
} | |
func incr(procs int) { | |
runtime.GOMAXPROCS(procs) | |
m := NewMap() | |
m.Set("key", 0) | |
wait := make(chan int) | |
go func() { | |
for i := 0; i < 10000000; i++ { | |
num := m.Get("key").(int) | |
num++ | |
m.Set("key", num) | |
} | |
wait <- 1 | |
}() | |
go func() { | |
for i := 0; i < 10000000; i++ { | |
num := m.Get("key").(int) | |
num++ | |
m.Set("key", num) | |
} | |
wait <- 1 | |
}() | |
<-wait | |
<-wait | |
fmt.Printf("GOMAXPROCS=%d num=%d\n", procs, m.Get("key").(int)) | |
} | |
func threadSafeIncr(procs int) { | |
runtime.GOMAXPROCS(procs) | |
m := NewThreadSafeMap() | |
m.Set("key", 0) | |
wait := make(chan int) | |
go func() { | |
for i := 0; i < 10000000; i++ { | |
m.Atm("key", func(value []interface{}) []interface{} { | |
var empty []interface{} | |
return append(empty, value[0].(int)+1) | |
}) | |
} | |
wait <- 1 | |
}() | |
go func() { | |
for i := 0; i < 10000000; i++ { | |
m.Atm("key", func(value []interface{}) []interface{} { | |
var empty []interface{} | |
return append(empty, value[0].(int)+1) | |
}) | |
} | |
wait <- 1 | |
}() | |
<-wait | |
<-wait | |
fmt.Printf("GOMAXPROCS=%d num=%d\n", procs, m.Get("key").(int)) | |
} | |
func goroutineSafeIncr(procs int) { | |
runtime.GOMAXPROCS(procs) | |
m := NewGoroutineSafeMap() | |
m.Set("key", 0) | |
wait := make(chan int) | |
go func() { | |
for i := 0; i < 10000000; i++ { | |
m.Atm("key", func(value []interface{}) []interface{} { | |
var empty []interface{} | |
return append(empty, value[0].(int)+1) | |
}) | |
} | |
wait <- 1 | |
}() | |
go func() { | |
for i := 0; i < 10000000; i++ { | |
m.Atm("key", func(value []interface{}) []interface{} { | |
var empty []interface{} | |
return append(empty, value[0].(int)+1) | |
}) | |
} | |
wait <- 1 | |
}() | |
<-wait | |
<-wait | |
fmt.Printf("GOMAXPROCS=%d num=%d\n", procs, m.Get("key").(int)) | |
} | |
func makeInterfaceArray(value ...interface{}) []interface{} { | |
var empty []interface{} | |
return append(empty, value...) | |
} | |
func TestMap(t *testing.T) { | |
var mapTest = func(m IMap) { | |
// Get => nil | |
func() { | |
if actual := m.Get("key"); actual != nil { | |
t.Errorf("Expect %q but %q", nil, actual) | |
} | |
}() | |
// All => | |
func() { | |
expect := makeInterfaceArray() | |
if actual := m.All("key"); !reflect.DeepEqual(actual, expect) { | |
t.Errorf("Expect %q but %q", expect, actual) | |
} | |
}() | |
// Set "foo", "bar", "baz" | |
// Get => foo | |
func() { | |
m.Set("key", "foo", "bar", "baz") | |
expect := "foo" | |
if actual := m.Get("key"); !reflect.DeepEqual(actual, expect) { | |
t.Errorf("Expect %q but %q", expect, actual) | |
} | |
}() | |
// All => "foo", "bar", "baz" | |
func() { | |
expect := makeInterfaceArray("foo", "bar", "baz") | |
if actual := m.All("key"); !reflect.DeepEqual(actual, expect) { | |
t.Errorf("Expect %q but %q", expect, actual) | |
} | |
}() | |
// Add "aaa", "bbb" | |
// All => "foo", "bar", "baz", "aaa", "bbb" | |
func() { | |
m.Add("key", "aaa", "bbb") | |
expect := makeInterfaceArray("foo", "bar", "baz", "aaa", "bbb") | |
if actual := m.All("key"); !reflect.DeepEqual(actual, expect) { | |
t.Errorf("Expect %q but %q", expect, actual) | |
} | |
}() | |
// Del | |
// All => | |
func() { | |
m.Del("key") | |
expect := makeInterfaceArray() | |
if actual := m.All("key"); !reflect.DeepEqual(actual, expect) { | |
t.Errorf("Expect %q but %q", expect, actual) | |
} | |
}() | |
// Itr | |
func() { | |
m.Add("key", "aaa", "bbb") | |
i := m.Itr("key") | |
if actual := i.Next(); actual != "aaa" { | |
t.Errorf("Expect %q but %q", "aaa", actual) | |
} | |
if actual := i.Next(); actual != "bbb" { | |
t.Errorf("Expect %q but %q", "bbb", actual) | |
} | |
if actual := i.Next(); actual != nil { | |
t.Errorf("Expect %q but %q", nil, actual) | |
} | |
expect := makeInterfaceArray("aaa", "bbb") | |
if actual := m.All("key"); !reflect.DeepEqual(actual, expect) { | |
t.Errorf("Expect %q but %q", expect, actual) | |
} | |
}() | |
} | |
mapTest(NewMap()) | |
mapTest(NewThreadSafeMap()) | |
mapTest(NewGoroutineSafeMap()) | |
} | |
func TestThreadSafe(t *testing.T) { | |
var safeTest = func(procs, count int) int { | |
runtime.GOMAXPROCS(procs) | |
m := NewThreadSafeMap() | |
m.Set("key", 0) | |
wait := make(chan int) | |
go func() { | |
for i := 0; i < count; i++ { | |
m.Atm("key", func(value []interface{}) []interface{} { | |
var empty []interface{} | |
return append(empty, value[0].(int)+1) | |
}) | |
} | |
wait <- 1 | |
}() | |
go func() { | |
for i := 0; i < count; i++ { | |
m.Atm("key", func(value []interface{}) []interface{} { | |
var empty []interface{} | |
return append(empty, value[0].(int)+1) | |
}) | |
} | |
wait <- 1 | |
}() | |
<-wait | |
<-wait | |
return m.Get("key").(int) | |
} | |
if actual := safeTest(1, 1000000); actual != 2000000 { | |
t.Errorf("Expect %d but %d", 2000000, actual) | |
} | |
if actual := safeTest(2, 1000000); actual != 2000000 { | |
t.Errorf("Expect %d but %d", 2000000, actual) | |
} | |
} | |
func TestGoroutineSafe(t *testing.T) { | |
var safeTest = func(procs, count int) int { | |
runtime.GOMAXPROCS(procs) | |
m := NewGoroutineSafeMap() | |
m.Set("key", 0) | |
wait := make(chan int) | |
go func() { | |
for i := 0; i < count; i++ { | |
m.Atm("key", func(value []interface{}) []interface{} { | |
var empty []interface{} | |
return append(empty, value[0].(int)+1) | |
}) | |
} | |
wait <- 1 | |
}() | |
go func() { | |
for i := 0; i < count; i++ { | |
m.Atm("key", func(value []interface{}) []interface{} { | |
var empty []interface{} | |
return append(empty, value[0].(int)+1) | |
}) | |
} | |
wait <- 1 | |
}() | |
<-wait | |
<-wait | |
return m.Get("key").(int) | |
} | |
if actual := safeTest(1, 1000000); actual != 2000000 { | |
t.Errorf("Expect %d but %d", 2000000, actual) | |
} | |
if actual := safeTest(2, 1000000); actual != 2000000 { | |
t.Errorf("Expect %d but %d", 2000000, actual) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
$ go test main_test.go -v