Home | History | Annotate | Download | only in atomic
      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