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 	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 }
    201