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 for _, test := range tests { 90 var v Value 91 done := make(chan bool) 92 for i := 0; i < p; i++ { 93 go func() { 94 r := rand.New(rand.NewSource(rand.Int63())) 95 loop: 96 for j := 0; j < 1e5; j++ { 97 x := test[r.Intn(len(test))] 98 v.Store(x) 99 x = v.Load() 100 for _, x1 := range test { 101 if x == x1 { 102 continue loop 103 } 104 } 105 t.Logf("loaded unexpected value %+v, want %+v", x, test) 106 done <- false 107 } 108 done <- true 109 }() 110 } 111 for i := 0; i < p; i++ { 112 if !<-done { 113 t.FailNow() 114 } 115 } 116 } 117 } 118 119 func BenchmarkValueRead(b *testing.B) { 120 var v Value 121 v.Store(new(int)) 122 b.RunParallel(func(pb *testing.PB) { 123 for pb.Next() { 124 x := v.Load().(*int) 125 if *x != 0 { 126 b.Fatalf("wrong value: got %v, want 0", *x) 127 } 128 } 129 }) 130 } 131 132 // The following example shows how to use Value for periodic program config updates 133 // and propagation of the changes to worker goroutines. 134 func ExampleValue_config() { 135 var config Value // holds current server configuration 136 // Create initial config value and store into config. 137 config.Store(loadConfig()) 138 go func() { 139 // Reload config every 10 seconds 140 // and update config value with the new version. 141 for { 142 time.Sleep(10 * time.Second) 143 config.Store(loadConfig()) 144 } 145 }() 146 // Create worker goroutines that handle incoming requests 147 // using the latest config value. 148 for i := 0; i < 10; i++ { 149 go func() { 150 for r := range requests() { 151 c := config.Load() 152 // Handle request r using config c. 153 _, _ = r, c 154 } 155 }() 156 } 157 } 158 159 func loadConfig() map[string]string { 160 return make(map[string]string) 161 } 162 163 func requests() chan int { 164 return make(chan int) 165 } 166 167 // The following example shows how to maintain a scalable frequently read, 168 // but infrequently updated data structure using copy-on-write idiom. 169 func ExampleValue_readMostly() { 170 type Map map[string]string 171 var m Value 172 m.Store(make(Map)) 173 var mu sync.Mutex // used only by writers 174 // read function can be used to read the data without further synchronization 175 read := func(key string) (val string) { 176 m1 := m.Load().(Map) 177 return m1[key] 178 } 179 // insert function can be used to update the data without further synchronization 180 insert := func(key, val string) { 181 mu.Lock() // synchronize with other potential writers 182 defer mu.Unlock() 183 m1 := m.Load().(Map) // load current value of the data structure 184 m2 := make(Map) // create a new value 185 for k, v := range m1 { 186 m2[k] = v // copy all data from the current object to the new one 187 } 188 m2[key] = val // do the update that we need 189 m.Store(m2) // atomically replace the current object with the new one 190 // At this point all new readers start working with the new version. 191 // The old version will be garbage collected once the existing readers 192 // (if any) are done with it. 193 } 194 _, _ = read, insert 195 } 196