├── .travis.yml ├── LICENSE ├── README.md ├── atom.go ├── atom_test.go ├── go.mod ├── unsafe.go └── unsafe_test.go /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | go: 4 | - 1.5.x 5 | - 1.6.x 6 | - 1.7.x 7 | - 1.8.x 8 | - 1.9.x 9 | - 1.10.x 10 | - 1.11.x 11 | - 1.12.x 12 | - 1.13.x 13 | - master 14 | before_install: 15 | - go get github.com/mattn/goveralls 16 | script: 17 | - go test -v -covermode=count -coverprofile=coverage.out 18 | - go test -v -tags purego 19 | - go vet ./... 20 | - test -z "$(gofmt -d -s . | tee /dev/stderr)" 21 | - $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Julien Schmidt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # atom 2 | [![Build Status](https://travis-ci.com/julienschmidt/atom.svg?branch=master)](https://travis-ci.com/julienschmidt/atom) [![Coverage Status](https://coveralls.io/repos/github/julienschmidt/atom/badge.svg?branch=master)](https://coveralls.io/github/julienschmidt/atom?branch=master) [![GoDoc](https://godoc.org/github.com/julienschmidt/atom?status.svg)](https://godoc.org/github.com/julienschmidt/atom) 3 | 4 | Intuitive wrapper types enforcing atomic access for lock-free concurrency. 5 | 6 | A safe and convenient alternative to sync/atomic. 7 | 8 | - Prevents unsafe non-atomic access 9 | - Prevents unsafe copying (which is a non-atomic read) 10 | - No size overhead. The wrappers have the same size as the wrapped type 11 | 12 | ## Usage 13 | 14 | A simple counter can be implemented as follows: 15 | 16 | ```go 17 | var counter atom.Uint64 18 | 19 | // concurrently: 20 | 21 | // optionally set a specific value 22 | counter.Set(42) 23 | 24 | // increase counter 25 | counter.Add(1) 26 | 27 | fmt.Println("Counter value:", counter.Value()) 28 | ``` 29 | 30 | Instead of using a costly [`sync.Mutex`](https://golang.org/pkg/sync/#Mutex) to guard the counter: 31 | 32 | ```go 33 | var ( 34 | counter uint64 35 | counterMu sync.Mutex 36 | ) 37 | 38 | // concurrently: 39 | 40 | // optionally set a specific value 41 | counterMu.Lock() 42 | counter = 42 43 | counterMu.Unlock() 44 | 45 | // increase counter 46 | counterMu.Lock() 47 | counter++ 48 | counterMu.Unlock() 49 | 50 | counterMu.Lock() 51 | value := counter 52 | counterMu.Unlock() 53 | fmt.Println("Counter value:", value) 54 | ``` 55 | 56 | Or using [`sync/atomic`](https://golang.org/pkg/sync/atomic/) directly, using an unintuitive interface and not guarding against non-atomic access to the counter: 57 | 58 | ```go 59 | var counter uint64 60 | 61 | // concurrently: 62 | 63 | // optionally set a specific value 64 | atomic.StoreUint64(&counter, 42) 65 | 66 | // increase counter 67 | atomic.AddUint64(&counter, 1) 68 | 69 | fmt.Println("Counter value:", atomic.LoadUint64(&counter)) 70 | 71 | // beware of direct access! 72 | counter = 0 73 | ``` 74 | -------------------------------------------------------------------------------- /atom.go: -------------------------------------------------------------------------------- 1 | // Package atom provides simple wrappers around types enforcing atomic usage 2 | // 3 | // The wrapper types do not introduce any size overhead and have the same size 4 | // as the wrapped type. 5 | package atom 6 | 7 | import ( 8 | "errors" 9 | "math" 10 | "sync/atomic" 11 | "time" 12 | ) 13 | 14 | // noCopy may be embedded into structs which must not be copied 15 | // after the first use. 16 | // 17 | // See https://github.com/golang/go/issues/8005#issuecomment-190753527 18 | // for details. 19 | type noCopy struct{} 20 | 21 | // Lock is a no-op used by -copylocks checker from `go vet`. 22 | func (*noCopy) Lock() {} 23 | 24 | // Bool is a wrapper around uint32 for usage as a boolean value with 25 | // atomic access. 26 | type Bool struct { 27 | _ noCopy 28 | value uint32 29 | } 30 | 31 | // CompareAndSwap atomically sets the new value only if the current value 32 | // matches the given old value and returns whether the new value was set. 33 | func (b *Bool) CompareAndSwap(old, new bool) (swapped bool) { 34 | var uold, unew uint32 35 | if old { 36 | uold = 1 37 | } 38 | if new { 39 | unew = 1 40 | } 41 | return atomic.CompareAndSwapUint32(&b.value, uold, unew) 42 | } 43 | 44 | // Set sets the new value regardless of the previous value. 45 | func (b *Bool) Set(value bool) { 46 | if value { 47 | atomic.StoreUint32(&b.value, 1) 48 | } else { 49 | atomic.StoreUint32(&b.value, 0) 50 | } 51 | } 52 | 53 | // Swap atomically sets the new value and returns the previous value. 54 | func (b *Bool) Swap(new bool) (old bool) { 55 | if new { 56 | return atomic.SwapUint32(&b.value, 1) > 0 57 | } 58 | return atomic.SwapUint32(&b.value, 0) > 0 59 | } 60 | 61 | // Value returns the current value. 62 | func (b *Bool) Value() (value bool) { 63 | return atomic.LoadUint32(&b.value) > 0 64 | } 65 | 66 | // Duration is a wrapper for atomically accessed time.Duration values. 67 | type Duration struct { 68 | _ noCopy 69 | value int64 70 | } 71 | 72 | // Add atomically adds delta to the current value and returns the new value. 73 | // No arithmetic overflow checks are applied. 74 | func (d *Duration) Add(delta time.Duration) (new time.Duration) { 75 | return time.Duration(atomic.AddInt64(&d.value, int64(delta))) 76 | } 77 | 78 | // CompareAndSwap atomically sets the new value only if the current value 79 | // matches the given old value and returns whether the new value was set. 80 | func (d *Duration) CompareAndSwap(old, new time.Duration) (swapped bool) { 81 | return atomic.CompareAndSwapInt64(&d.value, int64(old), int64(new)) 82 | } 83 | 84 | // Set sets the new value regardless of the previous value. 85 | func (d *Duration) Set(value time.Duration) { 86 | atomic.StoreInt64(&d.value, int64(value)) 87 | } 88 | 89 | // Sub atomically subtracts delta to the current value and returns the new value. 90 | // No arithmetic underflow checks are applied. 91 | func (d *Duration) Sub(delta time.Duration) (new time.Duration) { 92 | return time.Duration(atomic.AddInt64(&d.value, -int64(delta))) 93 | } 94 | 95 | // Swap atomically sets the new value and returns the previous value. 96 | func (d *Duration) Swap(new time.Duration) (old time.Duration) { 97 | return time.Duration(atomic.SwapInt64(&d.value, int64(new))) 98 | } 99 | 100 | // Value returns the current value. 101 | func (d *Duration) Value() (value time.Duration) { 102 | return time.Duration(atomic.LoadInt64(&d.value)) 103 | } 104 | 105 | // errNil is a special error signaling a nil value. 106 | var errNil = errors.New("nil") 107 | 108 | // Error is a wrapper for atomically accessed error values 109 | type Error struct { 110 | _ noCopy 111 | value atomic.Value 112 | } 113 | 114 | // Set sets the new value regardless of the previous value. 115 | // The value may be nil. 116 | func (e *Error) Set(value error) { 117 | // Setting atomic.Value to nil is not allowed. 118 | // Use the special error errNil instead to signal a nil value. 119 | if value == nil { 120 | value = errNil 121 | } 122 | e.value.Store(value) 123 | } 124 | 125 | // Value returns the current error value. 126 | func (e *Error) Value() (value error) { 127 | v := e.value.Load() 128 | if v == nil || v == errNil { 129 | return nil 130 | } 131 | return v.(error) 132 | } 133 | 134 | // Float32 is a wrapper for atomically accessed float32 values. 135 | type Float32 struct { 136 | _ noCopy 137 | value uint32 138 | } 139 | 140 | // Add adds delta to the current value and returns the new value. 141 | // Note: Internally this performs a CompareAndSwap operation within a loop. 142 | func (f *Float32) Add(delta float32) (new float32) { 143 | for { 144 | old := f.Value() 145 | new := old + delta 146 | if f.CompareAndSwap(old, new) { 147 | return new 148 | } 149 | } 150 | } 151 | 152 | // CompareAndSwap atomically sets the new value only if the current value 153 | // matches the given old value and returns whether the new value was set. 154 | func (f *Float32) CompareAndSwap(old, new float32) (swapped bool) { 155 | return atomic.CompareAndSwapUint32(&f.value, math.Float32bits(old), math.Float32bits(new)) 156 | } 157 | 158 | // Set sets the new value regardless of the previous value. 159 | func (f *Float32) Set(value float32) { 160 | atomic.StoreUint32(&f.value, math.Float32bits(value)) 161 | } 162 | 163 | // Sub atomically subtracts delta to the current value and returns the new value. 164 | func (f *Float32) Sub(delta float32) (new float32) { 165 | return f.Add(-delta) 166 | } 167 | 168 | // Swap atomically sets the new value and returns the previous value. 169 | func (f *Float32) Swap(new float32) (old float32) { 170 | return math.Float32frombits(atomic.SwapUint32(&f.value, math.Float32bits(new))) 171 | } 172 | 173 | // Value returns the current value. 174 | func (f *Float32) Value() (value float32) { 175 | return math.Float32frombits(atomic.LoadUint32(&f.value)) 176 | } 177 | 178 | // Float64 is a wrapper for atomically accessed float64 values. 179 | type Float64 struct { 180 | _ noCopy 181 | value uint64 182 | } 183 | 184 | // Add adds delta to the current value and returns the new value. 185 | // Note: Internally this performs a CompareAndSwap operation within a loop. 186 | func (f *Float64) Add(delta float64) (new float64) { 187 | for { 188 | old := f.Value() 189 | new := old + delta 190 | if f.CompareAndSwap(old, new) { 191 | return new 192 | } 193 | } 194 | } 195 | 196 | // CompareAndSwap atomically sets the new value only if the current value 197 | // matches the given old value and returns whether the new value was set. 198 | func (f *Float64) CompareAndSwap(old, new float64) (swapped bool) { 199 | return atomic.CompareAndSwapUint64(&f.value, math.Float64bits(old), math.Float64bits(new)) 200 | } 201 | 202 | // Set sets the new value regardless of the previous value. 203 | func (f *Float64) Set(value float64) { 204 | atomic.StoreUint64(&f.value, math.Float64bits(value)) 205 | } 206 | 207 | // Sub atomically subtracts delta to the current value and returns the new value. 208 | func (f *Float64) Sub(delta float64) (new float64) { 209 | return f.Add(-delta) 210 | } 211 | 212 | // Swap atomically sets the new value and returns the previous value. 213 | func (f *Float64) Swap(new float64) (old float64) { 214 | return math.Float64frombits(atomic.SwapUint64(&f.value, math.Float64bits(new))) 215 | } 216 | 217 | // Value returns the current value. 218 | func (f *Float64) Value() (value float64) { 219 | return math.Float64frombits(atomic.LoadUint64(&f.value)) 220 | } 221 | 222 | // Int is a wrapper for atomically accessed int values. 223 | type Int struct { 224 | _ noCopy 225 | value uintptr 226 | } 227 | 228 | // Add atomically adds delta to the current value and returns the new value. 229 | func (i *Int) Add(delta int) (new int) { 230 | return int(atomic.AddUintptr(&i.value, uintptr(delta))) 231 | } 232 | 233 | // CompareAndSwap atomically sets the new value only if the current value 234 | // matches the given old value and returns whether the new value was set. 235 | func (i *Int) CompareAndSwap(old, new int) (swapped bool) { 236 | return atomic.CompareAndSwapUintptr(&i.value, uintptr(old), uintptr(new)) 237 | } 238 | 239 | // Set sets the new value regardless of the previous value. 240 | func (i *Int) Set(value int) { 241 | atomic.StoreUintptr(&i.value, uintptr(value)) 242 | } 243 | 244 | // Sub atomically subtracts delta to the current value and returns the new value. 245 | func (i *Int) Sub(delta int) (new int) { 246 | return i.Add(-delta) 247 | } 248 | 249 | // Swap atomically sets the new value and returns the previous value. 250 | func (i *Int) Swap(new int) (old int) { 251 | return int(atomic.SwapUintptr(&i.value, uintptr(new))) 252 | } 253 | 254 | // Value returns the current value. 255 | func (i *Int) Value() (value int) { 256 | return int(atomic.LoadUintptr(&i.value)) 257 | } 258 | 259 | // Int32 is a wrapper for atomically accessed int32 values. 260 | type Int32 struct { 261 | _ noCopy 262 | value int32 263 | } 264 | 265 | // Add atomically adds delta to the current value and returns the new value. 266 | func (i *Int32) Add(delta int32) (new int32) { 267 | return atomic.AddInt32(&i.value, delta) 268 | } 269 | 270 | // CompareAndSwap atomically sets the new value only if the current value 271 | // matches the given old value and returns whether the new value was set. 272 | func (i *Int32) CompareAndSwap(old, new int32) (swapped bool) { 273 | return atomic.CompareAndSwapInt32(&i.value, old, new) 274 | } 275 | 276 | // Set sets the new value regardless of the previous value. 277 | func (i *Int32) Set(value int32) { 278 | atomic.StoreInt32(&i.value, value) 279 | } 280 | 281 | // Sub atomically subtracts delta to the current value and returns the new value. 282 | func (i *Int32) Sub(delta int32) (new int32) { 283 | return i.Add(-delta) 284 | } 285 | 286 | // Swap atomically sets the new value and returns the previous value. 287 | func (i *Int32) Swap(new int32) (old int32) { 288 | return atomic.SwapInt32(&i.value, new) 289 | } 290 | 291 | // Value returns the current value. 292 | func (i *Int32) Value() (value int32) { 293 | return atomic.LoadInt32(&i.value) 294 | } 295 | 296 | // Int64 is a wrapper for atomically accessed int64 values. 297 | type Int64 struct { 298 | _ noCopy 299 | value int64 300 | } 301 | 302 | // Add atomically adds delta to the current value and returns the new value. 303 | func (i *Int64) Add(delta int64) (new int64) { 304 | return atomic.AddInt64(&i.value, delta) 305 | } 306 | 307 | // CompareAndSwap atomically sets the new value only if the current value 308 | // matches the given old value and returns whether the new value was set. 309 | func (i *Int64) CompareAndSwap(old, new int64) (swapped bool) { 310 | return atomic.CompareAndSwapInt64(&i.value, old, new) 311 | } 312 | 313 | // Set sets the new value regardless of the previous value. 314 | func (i *Int64) Set(value int64) { 315 | atomic.StoreInt64(&i.value, value) 316 | } 317 | 318 | // Sub atomically subtracts delta to the current value and returns the new value. 319 | func (i *Int64) Sub(delta int64) (new int64) { 320 | return i.Add(-delta) 321 | } 322 | 323 | // Swap atomically sets the new value and returns the previous value. 324 | func (i *Int64) Swap(new int64) (old int64) { 325 | return atomic.SwapInt64(&i.value, new) 326 | } 327 | 328 | // Value returns the current value. 329 | func (i *Int64) Value() (value int64) { 330 | return atomic.LoadInt64(&i.value) 331 | } 332 | 333 | // String is a wrapper for atomically accessed string values. 334 | // Note: The string value is wrapped in an interface. Thus, this wrapper has 335 | // a memory overhead. 336 | type String struct { 337 | _ noCopy 338 | value atomic.Value 339 | } 340 | 341 | // Set sets the new value regardless of the previous value. 342 | // Note: Set requires an allocation as the value is wrapped in an interface. 343 | func (s *String) Set(value string) { 344 | s.value.Store(value) 345 | } 346 | 347 | // Value returns the current error value. 348 | func (s *String) Value() (value string) { 349 | v := s.value.Load() 350 | if v == nil { 351 | return "" 352 | } 353 | return v.(string) 354 | } 355 | 356 | // Uint is a wrapper for atomically accessed uint values. 357 | type Uint struct { 358 | _ noCopy 359 | value uintptr 360 | } 361 | 362 | // Add atomically adds delta to the current value and returns the new value. 363 | func (u *Uint) Add(delta uint) (new uint) { 364 | return uint(atomic.AddUintptr(&u.value, uintptr(delta))) 365 | } 366 | 367 | // CompareAndSwap atomically sets the new value only if the current value 368 | // matches the given old value and returns whether the new value was set. 369 | func (u *Uint) CompareAndSwap(old, new uint) (swapped bool) { 370 | return atomic.CompareAndSwapUintptr(&u.value, uintptr(old), uintptr(new)) 371 | } 372 | 373 | // Set sets the new value regardless of the previous value. 374 | func (u *Uint) Set(value uint) { 375 | atomic.StoreUintptr(&u.value, uintptr(value)) 376 | } 377 | 378 | // Sub atomically subtracts delta to the current value and returns the new value. 379 | func (u *Uint) Sub(delta uint) (new uint) { 380 | return u.Add(^(delta - 1)) 381 | } 382 | 383 | // Swap atomically sets the new value and returns the previous value. 384 | func (u *Uint) Swap(new uint) (old uint) { 385 | return uint(atomic.SwapUintptr(&u.value, uintptr(new))) 386 | } 387 | 388 | // Value returns the current value. 389 | func (u *Uint) Value() (value uint) { 390 | return uint(atomic.LoadUintptr(&u.value)) 391 | } 392 | 393 | // Uint32 is a wrapper for atomically accessed uint32 values. 394 | type Uint32 struct { 395 | _ noCopy 396 | value uint32 397 | } 398 | 399 | // Add atomically adds delta to the current value and returns the new value. 400 | func (u *Uint32) Add(delta uint32) (new uint32) { 401 | return atomic.AddUint32(&u.value, delta) 402 | } 403 | 404 | // CompareAndSwap atomically sets the new value only if the current value 405 | // matches the given old value and returns whether the new value was set. 406 | func (u *Uint32) CompareAndSwap(old, new uint32) (swapped bool) { 407 | return atomic.CompareAndSwapUint32(&u.value, old, new) 408 | } 409 | 410 | // Set sets the new value regardless of the previous value. 411 | func (u *Uint32) Set(value uint32) { 412 | atomic.StoreUint32(&u.value, value) 413 | } 414 | 415 | // Sub atomically subtracts delta to the current value and returns the new value. 416 | func (u *Uint32) Sub(delta uint32) (new uint32) { 417 | return u.Add(^(delta - 1)) 418 | } 419 | 420 | // Swap atomically sets the new value and returns the previous value. 421 | func (u *Uint32) Swap(new uint32) (old uint32) { 422 | return atomic.SwapUint32(&u.value, new) 423 | } 424 | 425 | // Value returns the current value. 426 | func (u *Uint32) Value() (value uint32) { 427 | return atomic.LoadUint32(&u.value) 428 | } 429 | 430 | // Uint64 is a wrapper for atomically accessed uint64 values. 431 | type Uint64 struct { 432 | _ noCopy 433 | value uint64 434 | } 435 | 436 | // Add atomically adds delta to the current value and returns the new value. 437 | func (u *Uint64) Add(delta uint64) (new uint64) { 438 | return atomic.AddUint64(&u.value, delta) 439 | } 440 | 441 | // CompareAndSwap atomically sets the new value only if the current value 442 | // matches the given old value and returns whether the new value was set. 443 | func (u *Uint64) CompareAndSwap(old, new uint64) (swapped bool) { 444 | return atomic.CompareAndSwapUint64(&u.value, old, new) 445 | } 446 | 447 | // Set sets the new value regardless of the previous value. 448 | func (u *Uint64) Set(value uint64) { 449 | atomic.StoreUint64(&u.value, value) 450 | } 451 | 452 | // Sub atomically subtracts delta to the current value and returns the new value. 453 | func (u *Uint64) Sub(delta uint64) (new uint64) { 454 | return u.Add(^(delta - 1)) 455 | } 456 | 457 | // Swap atomically sets the new value and returns the previous value. 458 | func (u *Uint64) Swap(new uint64) (old uint64) { 459 | return atomic.SwapUint64(&u.value, new) 460 | } 461 | 462 | // Value returns the current value. 463 | func (u *Uint64) Value() (value uint64) { 464 | return atomic.LoadUint64(&u.value) 465 | } 466 | 467 | // Uintptr is a wrapper for atomically accessed uintptr values. 468 | type Uintptr struct { 469 | _ noCopy 470 | value uintptr 471 | } 472 | 473 | // Add atomically adds delta to the current value and returns the new value. 474 | func (u *Uintptr) Add(delta uintptr) (new uintptr) { 475 | return atomic.AddUintptr(&u.value, delta) 476 | } 477 | 478 | // CompareAndSwap atomically sets the new value only if the current value 479 | // matches the given old value and returns whether the new value was set. 480 | func (u *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) { 481 | return atomic.CompareAndSwapUintptr(&u.value, old, new) 482 | } 483 | 484 | // Set sets the new value regardless of the previous value. 485 | func (u *Uintptr) Set(value uintptr) { 486 | atomic.StoreUintptr(&u.value, value) 487 | } 488 | 489 | // Sub atomically subtracts delta to the current value and returns the new value. 490 | func (u *Uintptr) Sub(delta uintptr) (new uintptr) { 491 | return u.Add(^(delta - 1)) 492 | } 493 | 494 | // Swap atomically sets the new value and returns the previous value. 495 | func (u *Uintptr) Swap(new uintptr) (old uintptr) { 496 | return atomic.SwapUintptr(&u.value, new) 497 | } 498 | 499 | // Value returns the current value. 500 | func (u *Uintptr) Value() (value uintptr) { 501 | return atomic.LoadUintptr(&u.value) 502 | } 503 | 504 | // Value is a wrapper for atomically accessed consistently typed values. 505 | type Value struct { 506 | _ noCopy 507 | value atomic.Value 508 | } 509 | 510 | // Set sets the new value regardless of the previous value. 511 | // All calls to Set for a given Value must use values of the same concrete type. 512 | // Set of an inconsistent type panics, as does Set(nil). 513 | func (v *Value) Set(value interface{}) { 514 | v.value.Store(value) 515 | } 516 | 517 | // Value returns the current value. 518 | // It returns nil if there has been no call to Set for this Value. 519 | func (v *Value) Value() (value interface{}) { 520 | return v.value.Load() 521 | } 522 | -------------------------------------------------------------------------------- /atom_test.go: -------------------------------------------------------------------------------- 1 | package atom 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | const ( 10 | maxUint = ^uint(0) 11 | minUint = 0 12 | 13 | maxInt = int(maxUint >> 1) 14 | minInt = -maxInt - 1 15 | ) 16 | 17 | func TestBool(t *testing.T) { 18 | // make go cover happy 19 | var nc noCopy 20 | nc.Lock() 21 | 22 | var b Bool 23 | if b.Value() { 24 | t.Fatal("Expected initial value to be false") 25 | } 26 | 27 | b.Set(true) 28 | if v := b.Value(); !v { 29 | t.Fatal("Value is still false") 30 | } 31 | b.Set(false) 32 | if v := b.Value(); v { 33 | t.Fatal("Value unchanged") 34 | } 35 | 36 | if b.CompareAndSwap(true, false) { 37 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 38 | } 39 | if v := b.Value(); v { 40 | t.Fatal("Value changed") 41 | } 42 | 43 | if !b.CompareAndSwap(false, true) { 44 | t.Fatal("CompareAndSwap did not report a swap") 45 | } 46 | if v := b.Value(); !v { 47 | t.Fatal("Value unchanged") 48 | } 49 | 50 | if !b.Swap(true) { 51 | t.Fatal("Old value does not match") 52 | } 53 | if v := b.Value(); !v { 54 | t.Fatal("Value unchanged") 55 | } 56 | if !b.Swap(false) { 57 | t.Fatal("Old value does not match") 58 | } 59 | if v := b.Value(); v { 60 | t.Fatal("Value unchanged") 61 | } 62 | } 63 | 64 | func TestDuration(t *testing.T) { 65 | var d Duration 66 | if d.Value() != 0 { 67 | t.Fatal("Expected initial value to be 0") 68 | } 69 | 70 | v1 := time.Duration(1337) 71 | d.Set(v1) 72 | if v := d.Value(); v != v1 { 73 | t.Fatal("Value unchanged") 74 | } 75 | 76 | if v := d.Sub(v1); v != 0 { 77 | t.Fatal("New value does not match:", v) 78 | } 79 | if v := d.Add(v1); v != v1 { 80 | t.Fatal("New value does not match:", v) 81 | } 82 | 83 | v2 := time.Duration(987654321) 84 | if d.CompareAndSwap(v2, v2) { 85 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 86 | } 87 | if v := d.Value(); v != v1 { 88 | t.Fatal("Value changed") 89 | } 90 | 91 | if !d.CompareAndSwap(v1, v2) { 92 | t.Fatal("CompareAndSwap did not report a swap") 93 | } 94 | if v := d.Value(); v != v2 { 95 | t.Fatal("Value unchanged") 96 | } 97 | 98 | if v := d.Swap(v1); v != v2 { 99 | t.Fatal("Old value does not match:", v) 100 | } 101 | if v := d.Value(); v != v1 { 102 | t.Fatal("Value unchanged") 103 | } 104 | } 105 | 106 | func TestError(t *testing.T) { 107 | var e Error 108 | if e.Value() != nil { 109 | t.Fatal("Expected initial value to be nil") 110 | } 111 | 112 | a := errors.New("a") 113 | 114 | e.Set(a) 115 | if v := e.Value(); v != a { 116 | if v == nil { 117 | t.Fatal("Value is still nil") 118 | } 119 | t.Fatal("Value did not match") 120 | } 121 | e.Set(nil) 122 | if v := e.Value(); v == a { 123 | t.Fatal("Value still matches initial value") 124 | } else if v != nil { 125 | t.Fatal("Value did not match") 126 | } 127 | } 128 | 129 | func TestFloat32(t *testing.T) { 130 | var f Float32 131 | if f.Value() != 0 { 132 | t.Fatal("Expected initial value to be 0") 133 | } 134 | 135 | var v1 float32 = 13.37 136 | f.Set(v1) 137 | if v := f.Value(); v != v1 { 138 | t.Fatal("Value unchanged") 139 | } 140 | 141 | if v := f.Sub(v1); v != 0 { 142 | t.Fatal("New value does not match:", v) 143 | } 144 | if v := f.Add(v1); v != v1 { 145 | t.Fatal("New value does not match:", v) 146 | } 147 | 148 | var v2 float32 = 98765.4321 149 | if f.CompareAndSwap(v2, v2) { 150 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 151 | } 152 | if v := f.Value(); v != v1 { 153 | t.Fatal("Value changed") 154 | } 155 | 156 | if !f.CompareAndSwap(v1, v2) { 157 | t.Fatal("CompareAndSwap did not report a swap") 158 | } 159 | if v := f.Value(); v != v2 { 160 | t.Fatal("Value unchanged") 161 | } 162 | 163 | if v := f.Swap(v1); v != v2 { 164 | t.Fatal("Old value does not match:", v) 165 | } 166 | if v := f.Value(); v != v1 { 167 | t.Fatal("Value unchanged") 168 | } 169 | } 170 | 171 | func TestFloat64(t *testing.T) { 172 | var f Float64 173 | if f.Value() != 0 { 174 | t.Fatal("Expected initial value to be 0") 175 | } 176 | 177 | v1 := 13.37 178 | f.Set(v1) 179 | if v := f.Value(); v != v1 { 180 | t.Fatal("Value unchanged") 181 | } 182 | 183 | if v := f.Sub(v1); v != 0 { 184 | t.Fatal("New value does not match:", v) 185 | } 186 | if v := f.Add(v1); v != v1 { 187 | t.Fatal("New value does not match:", v) 188 | } 189 | 190 | v2 := 98765.4321 191 | if f.CompareAndSwap(v2, v2) { 192 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 193 | } 194 | if v := f.Value(); v != v1 { 195 | t.Fatal("Value changed") 196 | } 197 | 198 | if !f.CompareAndSwap(v1, v2) { 199 | t.Fatal("CompareAndSwap did not report a swap") 200 | } 201 | if v := f.Value(); v != v2 { 202 | t.Fatal("Value unchanged") 203 | } 204 | 205 | if v := f.Swap(v1); v != v2 { 206 | t.Fatal("Old value does not match:", v) 207 | } 208 | if v := f.Value(); v != v1 { 209 | t.Fatal("Value unchanged") 210 | } 211 | } 212 | 213 | func TestInt(t *testing.T) { 214 | var i Int 215 | if i.Value() != 0 { 216 | t.Fatal("Expected initial value to be 0") 217 | } 218 | 219 | var v1 int = 1337 220 | i.Set(v1) 221 | if v := i.Value(); v != v1 { 222 | t.Fatal("Value unchanged") 223 | } 224 | 225 | if v := i.Sub(v1); v != 0 { 226 | t.Fatal("New value does not match:", v) 227 | } 228 | if v := i.Add(v1); v != v1 { 229 | t.Fatal("New value does not match:", v) 230 | } 231 | 232 | var v2 int = 987654321 233 | if i.CompareAndSwap(v2, v2) { 234 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 235 | } 236 | if v := i.Value(); v != v1 { 237 | t.Fatal("Value changed") 238 | } 239 | 240 | if !i.CompareAndSwap(v1, v2) { 241 | t.Fatal("CompareAndSwap did not report a swap") 242 | } 243 | if v := i.Value(); v != v2 { 244 | t.Fatal("Value unchanged") 245 | } 246 | 247 | if v := i.Swap(v1); v != v2 { 248 | t.Fatal("Old value does not match:", v) 249 | } 250 | if v := i.Value(); v != v1 { 251 | t.Fatal("Value unchanged") 252 | } 253 | 254 | // test underflow behavior 255 | v3 := minInt 256 | i.Set(v3) 257 | if v := i.Value(); v != v3 { 258 | t.Fatal("Value unchanged") 259 | } 260 | if v := i.Sub(1); v != (v3 - 1) { 261 | t.Fatal("New value does not match:", v) 262 | } 263 | 264 | // test overflow behavior 265 | v4 := maxInt 266 | i.Set(v4) 267 | if v := i.Value(); v != v4 { 268 | t.Fatal("Value unchanged") 269 | } 270 | if v := i.Add(1); v != (v4 + 1) { 271 | t.Fatal("New value does not match:", v) 272 | } 273 | } 274 | 275 | func TestInt32(t *testing.T) { 276 | var i Int32 277 | if i.Value() != 0 { 278 | t.Fatal("Expected initial value to be 0") 279 | } 280 | 281 | var v1 int32 = 1337 282 | i.Set(v1) 283 | if v := i.Value(); v != v1 { 284 | t.Fatal("Value unchanged") 285 | } 286 | 287 | if v := i.Sub(v1); v != 0 { 288 | t.Fatal("New value does not match:", v) 289 | } 290 | if v := i.Add(v1); v != v1 { 291 | t.Fatal("New value does not match:", v) 292 | } 293 | 294 | var v2 int32 = 987654321 295 | if i.CompareAndSwap(v2, v2) { 296 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 297 | } 298 | if v := i.Value(); v != v1 { 299 | t.Fatal("Value changed") 300 | } 301 | 302 | if !i.CompareAndSwap(v1, v2) { 303 | t.Fatal("CompareAndSwap did not report a swap") 304 | } 305 | if v := i.Value(); v != v2 { 306 | t.Fatal("Value unchanged") 307 | } 308 | 309 | if v := i.Swap(v1); v != v2 { 310 | t.Fatal("Old value does not match:", v) 311 | } 312 | if v := i.Value(); v != v1 { 313 | t.Fatal("Value unchanged") 314 | } 315 | } 316 | 317 | func TestInt64(t *testing.T) { 318 | var i Int64 319 | if i.Value() != 0 { 320 | t.Fatal("Expected initial value to be 0") 321 | } 322 | 323 | var v1 int64 = 1337 324 | i.Set(v1) 325 | if v := i.Value(); v != v1 { 326 | t.Fatal("Value unchanged") 327 | } 328 | 329 | if v := i.Sub(v1); v != 0 { 330 | t.Fatal("New value does not match:", v) 331 | } 332 | if v := i.Add(v1); v != v1 { 333 | t.Fatal("New value does not match:", v) 334 | } 335 | 336 | var v2 int64 = 987654321 337 | if i.CompareAndSwap(v2, v2) { 338 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 339 | } 340 | if v := i.Value(); v != v1 { 341 | t.Fatal("Value changed") 342 | } 343 | 344 | if !i.CompareAndSwap(v1, v2) { 345 | t.Fatal("CompareAndSwap did not report a swap") 346 | } 347 | if v := i.Value(); v != v2 { 348 | t.Fatal("Value unchanged") 349 | } 350 | 351 | if v := i.Swap(v1); v != v2 { 352 | t.Fatal("Old value does not match:", v) 353 | } 354 | if v := i.Value(); v != v1 { 355 | t.Fatal("Value unchanged") 356 | } 357 | } 358 | 359 | func TestString(t *testing.T) { 360 | var s String 361 | if s.Value() != "" { 362 | t.Fatal("Expected initial value to be an empty string") 363 | } 364 | 365 | s.Set("a") 366 | if v := s.Value(); v != "a" { 367 | if v == "" { 368 | t.Fatal("Value is still an empty string") 369 | } 370 | t.Fatal("Value did not match") 371 | } 372 | s.Set("") 373 | if v := s.Value(); v == "a" { 374 | t.Fatal("Value still matches initial value") 375 | } else if v != "" { 376 | t.Fatal("Value did not match") 377 | } 378 | } 379 | 380 | func TestUint(t *testing.T) { 381 | var u Uint 382 | if u.Value() != 0 { 383 | t.Fatal("Expected initial value to be 0") 384 | } 385 | 386 | var v1 uint = 1337 387 | u.Set(v1) 388 | if v := u.Value(); v != v1 { 389 | t.Fatal("Value unchanged") 390 | } 391 | 392 | if v := u.Sub(v1); v != 0 { 393 | t.Fatal("New value does not match:", v) 394 | } 395 | if v := u.Add(v1); v != v1 { 396 | t.Fatal("New value does not match:", v) 397 | } 398 | 399 | var v2 uint = 987654321 400 | if u.CompareAndSwap(v2, v2) { 401 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 402 | } 403 | if v := u.Value(); v != v1 { 404 | t.Fatal("Value changed") 405 | } 406 | 407 | if !u.CompareAndSwap(v1, v2) { 408 | t.Fatal("CompareAndSwap did not report a swap") 409 | } 410 | if v := u.Value(); v != v2 { 411 | t.Fatal("Value unchanged") 412 | } 413 | 414 | if v := u.Swap(v1); v != v2 { 415 | t.Fatal("Old value does not match:", v) 416 | } 417 | if v := u.Value(); v != v1 { 418 | t.Fatal("Value unchanged") 419 | } 420 | 421 | // test underflow behavior 422 | v3 := uint(minUint) 423 | u.Set(v3) 424 | if v := u.Value(); v != v3 { 425 | t.Fatal("Value unchanged") 426 | } 427 | if v := u.Sub(1); v != (v3 - 1) { 428 | t.Fatal("New value does not match:", v) 429 | } 430 | 431 | // test overflow behavior 432 | v4 := maxUint 433 | u.Set(v4) 434 | if v := u.Value(); v != v4 { 435 | t.Fatal("Value unchanged") 436 | } 437 | if v := u.Add(1); v != (v4 + 1) { 438 | t.Fatal("New value does not match:", v) 439 | } 440 | } 441 | 442 | func TestUint32(t *testing.T) { 443 | var u Uint32 444 | if u.Value() != 0 { 445 | t.Fatal("Expected initial value to be 0") 446 | } 447 | 448 | var v1 uint32 = 1337 449 | u.Set(v1) 450 | if v := u.Value(); v != v1 { 451 | t.Fatal("Value unchanged") 452 | } 453 | 454 | if v := u.Sub(v1); v != 0 { 455 | t.Fatal("New value does not match:", v) 456 | } 457 | if v := u.Add(v1); v != v1 { 458 | t.Fatal("New value does not match:", v) 459 | } 460 | 461 | var v2 uint32 = 987654321 462 | if u.CompareAndSwap(v2, v2) { 463 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 464 | } 465 | if v := u.Value(); v != v1 { 466 | t.Fatal("Value changed") 467 | } 468 | 469 | if !u.CompareAndSwap(v1, v2) { 470 | t.Fatal("CompareAndSwap did not report a swap") 471 | } 472 | if v := u.Value(); v != v2 { 473 | t.Fatal("Value unchanged") 474 | } 475 | 476 | if v := u.Swap(v1); v != v2 { 477 | t.Fatal("Old value does not match:", v) 478 | } 479 | if v := u.Value(); v != v1 { 480 | t.Fatal("Value unchanged") 481 | } 482 | } 483 | 484 | func TestUint64(t *testing.T) { 485 | var u Uint64 486 | if u.Value() != 0 { 487 | t.Fatal("Expected initial value to be 0") 488 | } 489 | 490 | var v1 uint64 = 1337 491 | u.Set(v1) 492 | if v := u.Value(); v != v1 { 493 | t.Fatal("Value unchanged") 494 | } 495 | 496 | if v := u.Sub(v1); v != 0 { 497 | t.Fatal("New value does not match:", v) 498 | } 499 | if v := u.Add(v1); v != v1 { 500 | t.Fatal("New value does not match:", v) 501 | } 502 | 503 | var v2 uint64 = 987654321 504 | if u.CompareAndSwap(v2, v2) { 505 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 506 | } 507 | if v := u.Value(); v != v1 { 508 | t.Fatal("Value changed") 509 | } 510 | 511 | if !u.CompareAndSwap(v1, v2) { 512 | t.Fatal("CompareAndSwap did not report a swap") 513 | } 514 | if v := u.Value(); v != v2 { 515 | t.Fatal("Value unchanged") 516 | } 517 | 518 | if v := u.Swap(v1); v != v2 { 519 | t.Fatal("Old value does not match:", v) 520 | } 521 | if v := u.Value(); v != v1 { 522 | t.Fatal("Value unchanged") 523 | } 524 | } 525 | 526 | func TestUintptr(t *testing.T) { 527 | var u Uintptr 528 | if u.Value() != 0 { 529 | t.Fatal("Expected initial value to be 0") 530 | } 531 | 532 | v1 := uintptr(1337) 533 | u.Set(v1) 534 | if v := u.Value(); v != v1 { 535 | t.Fatal("Value unchanged") 536 | } 537 | 538 | if v := u.Sub(v1); v != 0 { 539 | t.Fatal("New value does not match:", v) 540 | } 541 | if v := u.Add(v1); v != v1 { 542 | t.Fatal("New value does not match:", v) 543 | } 544 | 545 | v2 := uintptr(987654321) 546 | if u.CompareAndSwap(v2, v2) { 547 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 548 | } 549 | if v := u.Value(); v != v1 { 550 | t.Fatal("Value changed") 551 | } 552 | 553 | if !u.CompareAndSwap(v1, v2) { 554 | t.Fatal("CompareAndSwap did not report a swap") 555 | } 556 | if v := u.Value(); v != v2 { 557 | t.Fatal("Value unchanged") 558 | } 559 | 560 | if v := u.Swap(v1); v != v2 { 561 | t.Fatal("Old value does not match:", v) 562 | } 563 | if v := u.Value(); v != v1 { 564 | t.Fatal("Value unchanged") 565 | } 566 | } 567 | 568 | func TestValue(t *testing.T) { 569 | var v Value 570 | if v.Value() != nil { 571 | t.Fatal("Expected initial value to be nil") 572 | } 573 | 574 | var v1 uint64 = 1337 575 | v.Set(v1) 576 | if val := v.Value(); val != v1 { 577 | t.Fatal("Value does not match") 578 | } 579 | 580 | var v2 uint64 = 987654321 581 | v.Set(v2) 582 | if val := v.Value(); val != v2 { 583 | t.Fatal("Value does not match") 584 | } 585 | } 586 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/julienschmidt/atom 2 | 3 | go 1.4 4 | -------------------------------------------------------------------------------- /unsafe.go: -------------------------------------------------------------------------------- 1 | // +build !purego,!appengine,!js 2 | 3 | package atom 4 | 5 | import ( 6 | "sync/atomic" 7 | "unsafe" 8 | ) 9 | 10 | // Pointer is a wrapper for atomically accessed unsafe.Pointer values. 11 | type Pointer struct { 12 | _ noCopy 13 | value unsafe.Pointer 14 | } 15 | 16 | // CompareAndSwap atomically sets the new value only if the current value 17 | // matches the given old value and returns whether the new value was set. 18 | func (p *Pointer) CompareAndSwap(old, new unsafe.Pointer) (swapped bool) { 19 | return atomic.CompareAndSwapPointer(&p.value, old, new) 20 | } 21 | 22 | // Set sets the new value regardless of the previous value. 23 | func (p *Pointer) Set(value unsafe.Pointer) { 24 | atomic.StorePointer(&p.value, value) 25 | } 26 | 27 | // Swap atomically sets the new value and returns the previous value. 28 | func (p *Pointer) Swap(new unsafe.Pointer) (old unsafe.Pointer) { 29 | return atomic.SwapPointer(&p.value, new) 30 | } 31 | 32 | // Value returns the current value. 33 | func (p *Pointer) Value() (value unsafe.Pointer) { 34 | return atomic.LoadPointer(&p.value) 35 | } 36 | -------------------------------------------------------------------------------- /unsafe_test.go: -------------------------------------------------------------------------------- 1 | // +build !purego,!appengine,!js 2 | 3 | package atom 4 | 5 | import ( 6 | "testing" 7 | "unsafe" 8 | ) 9 | 10 | func TestPointer(t *testing.T) { 11 | var p Pointer 12 | if p.Value() != nil { 13 | t.Fatal("Expected initial value to be nil") 14 | } 15 | 16 | var t1, t2 uint64 17 | v1 := unsafe.Pointer(&t1) 18 | p.Set(v1) 19 | if v := p.Value(); v != v1 { 20 | t.Fatal("Value unchanged") 21 | } 22 | 23 | v2 := unsafe.Pointer(&t2) 24 | if p.CompareAndSwap(v2, v2) { 25 | t.Fatal("CompareAndSwap reported swap when the old value did not match") 26 | } 27 | if v := p.Value(); v != v1 { 28 | t.Fatal("Value changed") 29 | } 30 | 31 | if !p.CompareAndSwap(v1, v2) { 32 | t.Fatal("CompareAndSwap did not report a swap") 33 | } 34 | if v := p.Value(); v != v2 { 35 | t.Fatal("Value unchanged") 36 | } 37 | 38 | if p.Swap(v1) != v2 { 39 | t.Fatal("Old value does not match") 40 | } 41 | if v := p.Value(); v != v1 { 42 | t.Fatal("Value unchanged") 43 | } 44 | } 45 | --------------------------------------------------------------------------------