1// Copyright (c) 2016 Uber Technologies, Inc. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a copy 4// of this software and associated documentation files (the "Software"), to deal 5// in the Software without restriction, including without limitation the rights 6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7// copies of the Software, and to permit persons to whom the Software is 8// furnished to do so, subject to the following conditions: 9// 10// The above copyright notice and this permission notice shall be included in 11// all copies or substantial portions of the Software. 12// 13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19// THE SOFTWARE. 20 21package atomic 22 23import ( 24 "errors" 25 "math" 26 "runtime" 27 "sync" 28 "sync/atomic" 29 "testing" 30 "time" 31) 32 33const ( 34 _parallelism = 4 35 _iterations = 1000 36) 37 38var _stressTests = map[string]func() func(){ 39 "i32/std": stressStdInt32, 40 "i32": stressInt32, 41 "i64/std": stressStdInt64, 42 "i64": stressInt64, 43 "u32/std": stressStdUint32, 44 "u32": stressUint32, 45 "u64/std": stressStdUint64, 46 "u64": stressUint64, 47 "f64": stressFloat64, 48 "bool": stressBool, 49 "string": stressString, 50 "duration": stressDuration, 51 "error": stressError, 52 "time": stressTime, 53} 54 55func TestStress(t *testing.T) { 56 for name, ff := range _stressTests { 57 t.Run(name, func(t *testing.T) { 58 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism)) 59 60 start := make(chan struct{}) 61 var wg sync.WaitGroup 62 wg.Add(_parallelism) 63 f := ff() 64 for i := 0; i < _parallelism; i++ { 65 go func() { 66 defer wg.Done() 67 <-start 68 for j := 0; j < _iterations; j++ { 69 f() 70 } 71 }() 72 } 73 close(start) 74 wg.Wait() 75 }) 76 } 77} 78 79func BenchmarkStress(b *testing.B) { 80 for name, ff := range _stressTests { 81 b.Run(name, func(b *testing.B) { 82 f := ff() 83 84 b.Run("serial", func(b *testing.B) { 85 for i := 0; i < b.N; i++ { 86 f() 87 } 88 }) 89 90 b.Run("parallel", func(b *testing.B) { 91 b.RunParallel(func(pb *testing.PB) { 92 for pb.Next() { 93 f() 94 } 95 }) 96 }) 97 }) 98 } 99} 100 101func stressStdInt32() func() { 102 var atom int32 103 return func() { 104 atomic.LoadInt32(&atom) 105 atomic.AddInt32(&atom, 1) 106 atomic.AddInt32(&atom, -2) 107 atomic.AddInt32(&atom, 1) 108 atomic.AddInt32(&atom, -1) 109 atomic.CompareAndSwapInt32(&atom, 1, 0) 110 atomic.SwapInt32(&atom, 5) 111 atomic.StoreInt32(&atom, 1) 112 } 113} 114 115func stressInt32() func() { 116 var atom Int32 117 return func() { 118 atom.Load() 119 atom.Add(1) 120 atom.Sub(2) 121 atom.Inc() 122 atom.Dec() 123 atom.CAS(1, 0) 124 atom.Swap(5) 125 atom.Store(1) 126 } 127} 128 129func stressStdInt64() func() { 130 var atom int64 131 return func() { 132 atomic.LoadInt64(&atom) 133 atomic.AddInt64(&atom, 1) 134 atomic.AddInt64(&atom, -2) 135 atomic.AddInt64(&atom, 1) 136 atomic.AddInt64(&atom, -1) 137 atomic.CompareAndSwapInt64(&atom, 1, 0) 138 atomic.SwapInt64(&atom, 5) 139 atomic.StoreInt64(&atom, 1) 140 } 141} 142 143func stressInt64() func() { 144 var atom Int64 145 return func() { 146 atom.Load() 147 atom.Add(1) 148 atom.Sub(2) 149 atom.Inc() 150 atom.Dec() 151 atom.CAS(1, 0) 152 atom.Swap(5) 153 atom.Store(1) 154 } 155} 156 157func stressStdUint32() func() { 158 var atom uint32 159 return func() { 160 atomic.LoadUint32(&atom) 161 atomic.AddUint32(&atom, 1) 162 // Adding `MaxUint32` is the same as subtracting 1 163 atomic.AddUint32(&atom, math.MaxUint32-1) 164 atomic.AddUint32(&atom, 1) 165 atomic.AddUint32(&atom, math.MaxUint32) 166 atomic.CompareAndSwapUint32(&atom, 1, 0) 167 atomic.SwapUint32(&atom, 5) 168 atomic.StoreUint32(&atom, 1) 169 } 170} 171 172func stressUint32() func() { 173 var atom Uint32 174 return func() { 175 atom.Load() 176 atom.Add(1) 177 atom.Sub(2) 178 atom.Inc() 179 atom.Dec() 180 atom.CAS(1, 0) 181 atom.Swap(5) 182 atom.Store(1) 183 } 184} 185 186func stressStdUint64() func() { 187 var atom uint64 188 return func() { 189 atomic.LoadUint64(&atom) 190 atomic.AddUint64(&atom, 1) 191 // Adding `MaxUint64` is the same as subtracting 1 192 atomic.AddUint64(&atom, math.MaxUint64-1) 193 atomic.AddUint64(&atom, 1) 194 atomic.AddUint64(&atom, math.MaxUint64) 195 atomic.CompareAndSwapUint64(&atom, 1, 0) 196 atomic.SwapUint64(&atom, 5) 197 atomic.StoreUint64(&atom, 1) 198 } 199} 200 201func stressUint64() func() { 202 var atom Uint64 203 return func() { 204 atom.Load() 205 atom.Add(1) 206 atom.Sub(2) 207 atom.Inc() 208 atom.Dec() 209 atom.CAS(1, 0) 210 atom.Swap(5) 211 atom.Store(1) 212 } 213} 214 215func stressFloat64() func() { 216 var atom Float64 217 return func() { 218 atom.Load() 219 atom.CAS(1.0, 0.1) 220 atom.Add(1.1) 221 atom.Sub(0.2) 222 atom.Store(1.0) 223 } 224} 225 226func stressBool() func() { 227 var atom Bool 228 return func() { 229 atom.Load() 230 atom.Store(false) 231 atom.Swap(true) 232 atom.CAS(true, false) 233 atom.CAS(true, false) 234 atom.Load() 235 atom.Toggle() 236 atom.Toggle() 237 } 238} 239 240func stressString() func() { 241 var atom String 242 return func() { 243 atom.Load() 244 atom.Store("abc") 245 atom.Load() 246 atom.Store("def") 247 atom.Load() 248 atom.Store("") 249 } 250} 251 252func stressDuration() func() { 253 var atom = NewDuration(0) 254 return func() { 255 atom.Load() 256 atom.Add(1) 257 atom.Sub(2) 258 atom.CAS(1, 0) 259 atom.Swap(5) 260 atom.Store(1) 261 } 262} 263 264func stressError() func() { 265 var atom = NewError(nil) 266 var err1 = errors.New("err1") 267 var err2 = errors.New("err2") 268 return func() { 269 atom.Load() 270 atom.Store(err1) 271 atom.Load() 272 atom.Store(err2) 273 atom.Load() 274 atom.Store(nil) 275 } 276} 277 278func stressTime() func() { 279 var atom = NewTime(time.Date(2021, 6, 17, 9, 0, 0, 0, time.UTC)) 280 var dayAgo = time.Date(2021, 6, 16, 9, 0, 0, 0, time.UTC) 281 var weekAgo = time.Date(2021, 6, 10, 9, 0, 0, 0, time.UTC) 282 return func() { 283 atom.Load() 284 atom.Store(dayAgo) 285 atom.Load() 286 atom.Store(weekAgo) 287 atom.Store(time.Time{}) 288 } 289} 290