github.com/hslam/atomic@v1.0.0/value.go (about) 1 // Copyright (c) 2020 Meng Huang (mhboy@outlook.com) 2 // This package is licensed under a MIT license that can be found in the LICENSE file. 3 4 package atomic 5 6 import ( 7 "sync/atomic" 8 "unsafe" 9 ) 10 11 // AddFunc is a add function. 12 type AddFunc func(old, delta interface{}) (new interface{}) 13 14 // EqualFunc is a equal function. 15 type EqualFunc func(old, load interface{}) (equal bool) 16 17 // Value provides an atomic load and store of a consistently typed value. 18 // The zero value for a Value returns nil from Load. 19 // Once Store has been called, a Value must not be copied. 20 // 21 // A Value must not be copied after first use. 22 type Value struct { 23 v atomic.Value 24 EqualFunc EqualFunc 25 AddFunc AddFunc 26 } 27 28 // ifaceWords is interface{} internal representation. 29 type ifaceWords struct { 30 typ unsafe.Pointer 31 data unsafe.Pointer 32 } 33 34 // NewValue returns a new Value. 35 func NewValue(val interface{}, equalFunc EqualFunc, addFunc AddFunc) *Value { 36 addr := &Value{EqualFunc: equalFunc, AddFunc: addFunc} 37 addr.Store(val) 38 return addr 39 } 40 41 // Swap atomically stores new into *addr and returns the previous *addr value. 42 func (v *Value) Swap(new interface{}) (old interface{}) { 43 for { 44 old = v.Load() 45 if v.compareAndSwap(old, new) { 46 return 47 } 48 } 49 } 50 51 // CompareAndSwap executes the compare-and-swap operation for an interface{} value. 52 func (v *Value) CompareAndSwap(old, new interface{}) (swapped bool) { 53 if v.EqualFunc == nil { 54 panic("EqualFunc is nil") 55 } 56 load := v.Load() 57 if !v.EqualFunc(old, load) { 58 return false 59 } 60 return v.compareAndSwap(load, new) 61 } 62 63 // compareAndSwap executes the compare-and-swap operation for an interface{} value. 64 func (v *Value) compareAndSwap(old, new interface{}) (swapped bool) { 65 if new == nil { 66 panic("github.com/hslam/atomic: new is nil") 67 } 68 vp := (*ifaceWords)(unsafe.Pointer(&v.v)) 69 np := (*ifaceWords)(unsafe.Pointer(&new)) 70 typ := LoadPointer(&vp.typ) 71 if typ == nil { 72 // Attempt to start first store. 73 if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) { 74 return false 75 } 76 // Complete first store. 77 StorePointer(&vp.data, np.data) 78 StorePointer(&vp.typ, np.typ) 79 return 80 } 81 if uintptr(typ) == ^uintptr(0) { 82 // First store in progress. 83 return false 84 } 85 if old == nil { 86 panic("github.com/hslam/atomic: old is nil") 87 } 88 // First store completed. Check type. 89 op := (*ifaceWords)(unsafe.Pointer(&old)) 90 if typ != op.typ { 91 panic("github.com/hslam/atomic: old is inconsistently typed value") 92 } 93 if typ != np.typ { 94 panic("github.com/hslam/atomic: new is inconsistently typed value") 95 } 96 return atomic.CompareAndSwapPointer(&vp.data, op.data, np.data) 97 } 98 99 // Add atomically adds delta to *addr and returns the new value. 100 func (v *Value) Add(delta interface{}) (new interface{}) { 101 if v.AddFunc == nil { 102 panic("AddFunc is nil") 103 } 104 for { 105 old := v.Load() 106 new = v.AddFunc(old, delta) 107 if v.compareAndSwap(old, new) { 108 return 109 } 110 } 111 } 112 113 // Load returns the value set by the most recent Store. 114 // It returns nil if there has been no call to Store for this Value. 115 func (v *Value) Load() (x interface{}) { 116 return v.v.Load() 117 } 118 119 // Store sets the value of the Value to x. 120 // All calls to Store for a given Value must use values of the same concrete type. 121 // Store of an inconsistent type panics, as does Store(nil). 122 func (v *Value) Store(x interface{}) { 123 v.v.Store(x) 124 }