Skip to content

Instantly share code, notes, and snippets.

@mix3
Created May 15, 2014 09:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mix3/8037d5645c738a27755d to your computer and use it in GitHub Desktop.
Save mix3/8037d5645c738a27755d to your computer and use it in GitHub Desktop.
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)
}
}
@mix3
Copy link
Author

mix3 commented May 15, 2014

$ go test main_test.go -v

=== RUN TestMap
--- PASS: TestMap (0.00 seconds)
=== RUN TestThreadSafe
--- PASS: TestThreadSafe (1.50 seconds)
=== RUN TestGoroutineSafe
--- PASS: TestGoroutineSafe (1.95 seconds)
PASS
ok      command-line-arguments  3.466s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment