// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package atomic_test import ( "math/rand" "runtime" "sync" . "sync/atomic" "testing" "time" ) func TestValue(t *testing.T) { var v Value if v.Load() != nil { t.Fatal("initial Value is not nil") } v.Store(42) x := v.Load() if xx, ok := x.(int); !ok || xx != 42 { t.Fatalf("wrong value: got %+v, want 42", x) } v.Store(84) x = v.Load() if xx, ok := x.(int); !ok || xx != 84 { t.Fatalf("wrong value: got %+v, want 84", x) } } func TestValueLarge(t *testing.T) { var v Value v.Store("foo") x := v.Load() if xx, ok := x.(string); !ok || xx != "foo" { t.Fatalf("wrong value: got %+v, want foo", x) } v.Store("barbaz") x = v.Load() if xx, ok := x.(string); !ok || xx != "barbaz" { t.Fatalf("wrong value: got %+v, want barbaz", x) } } func TestValuePanic(t *testing.T) { const nilErr = "sync/atomic: store of nil value into Value" const badErr = "sync/atomic: store of inconsistently typed value into Value" var v Value func() { defer func() { err := recover() if err != nilErr { t.Fatalf("inconsistent store panic: got '%v', want '%v'", err, nilErr) } }() v.Store(nil) }() v.Store(42) func() { defer func() { err := recover() if err != badErr { t.Fatalf("inconsistent store panic: got '%v', want '%v'", err, badErr) } }() v.Store("foo") }() func() { defer func() { err := recover() if err != nilErr { t.Fatalf("inconsistent store panic: got '%v', want '%v'", err, nilErr) } }() v.Store(nil) }() } func TestValueConcurrent(t *testing.T) { tests := [][]interface{}{ {uint16(0), ^uint16(0), uint16(1 + 2<<8), uint16(3 + 4<<8)}, {uint32(0), ^uint32(0), uint32(1 + 2<<16), uint32(3 + 4<<16)}, {uint64(0), ^uint64(0), uint64(1 + 2<<32), uint64(3 + 4<<32)}, {complex(0, 0), complex(1, 2), complex(3, 4), complex(5, 6)}, } p := 4 * runtime.GOMAXPROCS(0) N := int(1e5) if testing.Short() { p /= 2 N = 1e3 } for _, test := range tests { var v Value done := make(chan bool) for i := 0; i < p; i++ { go func() { r := rand.New(rand.NewSource(rand.Int63())) loop: for j := 0; j < N; j++ { x := test[r.Intn(len(test))] v.Store(x) x = v.Load() for _, x1 := range test { if x == x1 { continue loop } } t.Logf("loaded unexpected value %+v, want %+v", x, test) done <- false } done <- true }() } for i := 0; i < p; i++ { if !<-done { t.FailNow() } } } } func BenchmarkValueRead(b *testing.B) { var v Value v.Store(new(int)) b.RunParallel(func(pb *testing.PB) { for pb.Next() { x := v.Load().(*int) if *x != 0 { b.Fatalf("wrong value: got %v, want 0", *x) } } }) } // The following example shows how to use Value for periodic program config updates // and propagation of the changes to worker goroutines. func ExampleValue_config() { var config Value // holds current server configuration // Create initial config value and store into config. config.Store(loadConfig()) go func() { // Reload config every 10 seconds // and update config value with the new version. for { time.Sleep(10 * time.Second) config.Store(loadConfig()) } }() // Create worker goroutines that handle incoming requests // using the latest config value. for i := 0; i < 10; i++ { go func() { for r := range requests() { c := config.Load() // Handle request r using config c. _, _ = r, c } }() } } func loadConfig() map[string]string { return make(map[string]string) } func requests() chan int { return make(chan int) } // The following example shows how to maintain a scalable frequently read, // but infrequently updated data structure using copy-on-write idiom. func ExampleValue_readMostly() { type Map map[string]string var m Value m.Store(make(Map)) var mu sync.Mutex // used only by writers // read function can be used to read the data without further synchronization read := func(key string) (val string) { m1 := m.Load().(Map) return m1[key] } // insert function can be used to update the data without further synchronization insert := func(key, val string) { mu.Lock() // synchronize with other potential writers defer mu.Unlock() m1 := m.Load().(Map) // load current value of the data structure m2 := make(Map) // create a new value for k, v := range m1 { m2[k] = v // copy all data from the current object to the new one } m2[key] = val // do the update that we need m.Store(m2) // atomically replace the current object with the new one // At this point all new readers start working with the new version. // The old version will be garbage collected once the existing readers // (if any) are done with it. } _, _ = read, insert }