github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/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  	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  }