github.com/sbinet/go@v0.0.0-20160827155028-54d7de7dd62b/src/sync/atomic/value_test.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package atomic_test 6 7 import ( 8 "math/rand" 9 "runtime" 10 "sync" 11 . "sync/atomic" 12 "testing" 13 "time" 14 ) 15 16 func TestValue(t *testing.T) { 17 var v Value 18 if v.Load() != nil { 19 t.Fatal("initial Value is not nil") 20 } 21 v.Store(42) 22 x := v.Load() 23 if xx, ok := x.(int); !ok || xx != 42 { 24 t.Fatalf("wrong value: got %+v, want 42", x) 25 } 26 v.Store(84) 27 x = v.Load() 28 if xx, ok := x.(int); !ok || xx != 84 { 29 t.Fatalf("wrong value: got %+v, want 84", x) 30 } 31 } 32 33 func TestValueLarge(t *testing.T) { 34 var v Value 35 v.Store("foo") 36 x := v.Load() 37 if xx, ok := x.(string); !ok || xx != "foo" { 38 t.Fatalf("wrong value: got %+v, want foo", x) 39 } 40 v.Store("barbaz") 41 x = v.Load() 42 if xx, ok := x.(string); !ok || xx != "barbaz" { 43 t.Fatalf("wrong value: got %+v, want barbaz", x) 44 } 45 } 46 47 func TestValuePanic(t *testing.T) { 48 const nilErr = "sync/atomic: store of nil value into Value" 49 const badErr = "sync/atomic: store of inconsistently typed value into Value" 50 var v Value 51 func() { 52 defer func() { 53 err := recover() 54 if err != nilErr { 55 t.Fatalf("inconsistent store panic: got '%v', want '%v'", err, nilErr) 56 } 57 }() 58 v.Store(nil) 59 }() 60 v.Store(42) 61 func() { 62 defer func() { 63 err := recover() 64 if err != badErr { 65 t.Fatalf("inconsistent store panic: got '%v', want '%v'", err, badErr) 66 } 67 }() 68 v.Store("foo") 69 }() 70 func() { 71 defer func() { 72 err := recover() 73 if err != nilErr { 74 t.Fatalf("inconsistent store panic: got '%v', want '%v'", err, nilErr) 75 } 76 }() 77 v.Store(nil) 78 }() 79 } 80 81 func TestValueConcurrent(t *testing.T) { 82 tests := [][]interface{}{ 83 {uint16(0), ^uint16(0), uint16(1 + 2<<8), uint16(3 + 4<<8)}, 84 {uint32(0), ^uint32(0), uint32(1 + 2<<16), uint32(3 + 4<<16)}, 85 {uint64(0), ^uint64(0), uint64(1 + 2<<32), uint64(3 + 4<<32)}, 86 {complex(0, 0), complex(1, 2), complex(3, 4), complex(5, 6)}, 87 } 88 p := 4 * runtime.GOMAXPROCS(0) 89 N := int(1e5) 90 if testing.Short() { 91 p /= 2 92 N = 1e3 93 } 94 for _, test := range tests { 95 var v Value 96 done := make(chan bool) 97 for i := 0; i < p; i++ { 98 go func() { 99 r := rand.New(rand.NewSource(rand.Int63())) 100 loop: 101 for j := 0; j < N; j++ { 102 x := test[r.Intn(len(test))] 103 v.Store(x) 104 x = v.Load() 105 for _, x1 := range test { 106 if x == x1 { 107 continue loop 108 } 109 } 110 t.Logf("loaded unexpected value %+v, want %+v", x, test) 111 done <- false 112 } 113 done <- true 114 }() 115 } 116 for i := 0; i < p; i++ { 117 if !<-done { 118 t.FailNow() 119 } 120 } 121 } 122 } 123 124 func BenchmarkValueRead(b *testing.B) { 125 var v Value 126 v.Store(new(int)) 127 b.RunParallel(func(pb *testing.PB) { 128 for pb.Next() { 129 x := v.Load().(*int) 130 if *x != 0 { 131 b.Fatalf("wrong value: got %v, want 0", *x) 132 } 133 } 134 }) 135 } 136 137 // The following example shows how to use Value for periodic program config updates 138 // and propagation of the changes to worker goroutines. 139 func ExampleValue_config() { 140 var config Value // holds current server configuration 141 // Create initial config value and store into config. 142 config.Store(loadConfig()) 143 go func() { 144 // Reload config every 10 seconds 145 // and update config value with the new version. 146 for { 147 time.Sleep(10 * time.Second) 148 config.Store(loadConfig()) 149 } 150 }() 151 // Create worker goroutines that handle incoming requests 152 // using the latest config value. 153 for i := 0; i < 10; i++ { 154 go func() { 155 for r := range requests() { 156 c := config.Load() 157 // Handle request r using config c. 158 _, _ = r, c 159 } 160 }() 161 } 162 } 163 164 func loadConfig() map[string]string { 165 return make(map[string]string) 166 } 167 168 func requests() chan int { 169 return make(chan int) 170 } 171 172 // The following example shows how to maintain a scalable frequently read, 173 // but infrequently updated data structure using copy-on-write idiom. 174 func ExampleValue_readMostly() { 175 type Map map[string]string 176 var m Value 177 m.Store(make(Map)) 178 var mu sync.Mutex // used only by writers 179 // read function can be used to read the data without further synchronization 180 read := func(key string) (val string) { 181 m1 := m.Load().(Map) 182 return m1[key] 183 } 184 // insert function can be used to update the data without further synchronization 185 insert := func(key, val string) { 186 mu.Lock() // synchronize with other potential writers 187 defer mu.Unlock() 188 m1 := m.Load().(Map) // load current value of the data structure 189 m2 := make(Map) // create a new value 190 for k, v := range m1 { 191 m2[k] = v // copy all data from the current object to the new one 192 } 193 m2[key] = val // do the update that we need 194 m.Store(m2) // atomically replace the current object with the new one 195 // At this point all new readers start working with the new version. 196 // The old version will be garbage collected once the existing readers 197 // (if any) are done with it. 198 } 199 _, _ = read, insert 200 }