github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/sync/atomic/value.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 6 7 import ( 8 "unsafe" 9 ) 10 11 // A Value provides an atomic load and store of a consistently typed value. 12 // The zero value for a Value returns nil from Load. 13 // Once Store has been called, a Value must not be copied. 14 // 15 // A Value must not be copied after first use. 16 type Value struct { 17 v any 18 } 19 20 // efaceWords is interface{} internal representation. 21 type efaceWords struct { 22 typ unsafe.Pointer 23 data unsafe.Pointer 24 } 25 26 // Load returns the value set by the most recent Store. 27 // It returns nil if there has been no call to Store for this Value. 28 func (v *Value) Load() (val any) { 29 vp := (*efaceWords)(unsafe.Pointer(v)) 30 typ := LoadPointer(&vp.typ) 31 if typ == nil || typ == unsafe.Pointer(&firstStoreInProgress) { 32 // First store not yet completed. 33 return nil 34 } 35 data := LoadPointer(&vp.data) 36 vlp := (*efaceWords)(unsafe.Pointer(&val)) 37 vlp.typ = typ 38 vlp.data = data 39 return 40 } 41 42 var firstStoreInProgress byte 43 44 // Store sets the value of the Value v to val. 45 // All calls to Store for a given Value must use values of the same concrete type. 46 // Store of an inconsistent type panics, as does Store(nil). 47 func (v *Value) Store(val any) { 48 if val == nil { 49 panic("sync/atomic: store of nil value into Value") 50 } 51 vp := (*efaceWords)(unsafe.Pointer(v)) 52 vlp := (*efaceWords)(unsafe.Pointer(&val)) 53 for { 54 typ := LoadPointer(&vp.typ) 55 if typ == nil { 56 // Attempt to start first store. 57 // Disable preemption so that other goroutines can use 58 // active spin wait to wait for completion. 59 runtime_procPin() 60 if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) { 61 runtime_procUnpin() 62 continue 63 } 64 // Complete first store. 65 StorePointer(&vp.data, vlp.data) 66 StorePointer(&vp.typ, vlp.typ) 67 runtime_procUnpin() 68 return 69 } 70 if typ == unsafe.Pointer(&firstStoreInProgress) { 71 // First store in progress. Wait. 72 // Since we disable preemption around the first store, 73 // we can wait with active spinning. 74 continue 75 } 76 // First store completed. Check type and overwrite data. 77 if typ != vlp.typ { 78 panic("sync/atomic: store of inconsistently typed value into Value") 79 } 80 StorePointer(&vp.data, vlp.data) 81 return 82 } 83 } 84 85 // Swap stores new into Value and returns the previous value. It returns nil if 86 // the Value is empty. 87 // 88 // All calls to Swap for a given Value must use values of the same concrete 89 // type. Swap of an inconsistent type panics, as does Swap(nil). 90 func (v *Value) Swap(new any) (old any) { 91 if new == nil { 92 panic("sync/atomic: swap of nil value into Value") 93 } 94 vp := (*efaceWords)(unsafe.Pointer(v)) 95 np := (*efaceWords)(unsafe.Pointer(&new)) 96 for { 97 typ := LoadPointer(&vp.typ) 98 if typ == nil { 99 // Attempt to start first store. 100 // Disable preemption so that other goroutines can use 101 // active spin wait to wait for completion; and so that 102 // GC does not see the fake type accidentally. 103 runtime_procPin() 104 if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) { 105 runtime_procUnpin() 106 continue 107 } 108 // Complete first store. 109 StorePointer(&vp.data, np.data) 110 StorePointer(&vp.typ, np.typ) 111 runtime_procUnpin() 112 return nil 113 } 114 if typ == unsafe.Pointer(&firstStoreInProgress) { 115 // First store in progress. Wait. 116 // Since we disable preemption around the first store, 117 // we can wait with active spinning. 118 continue 119 } 120 // First store completed. Check type and overwrite data. 121 if typ != np.typ { 122 panic("sync/atomic: swap of inconsistently typed value into Value") 123 } 124 op := (*efaceWords)(unsafe.Pointer(&old)) 125 op.typ, op.data = np.typ, SwapPointer(&vp.data, np.data) 126 return old 127 } 128 } 129 130 // CompareAndSwap executes the compare-and-swap operation for the Value. 131 // 132 // All calls to CompareAndSwap for a given Value must use values of the same 133 // concrete type. CompareAndSwap of an inconsistent type panics, as does 134 // CompareAndSwap(old, nil). 135 func (v *Value) CompareAndSwap(old, new any) (swapped bool) { 136 if new == nil { 137 panic("sync/atomic: compare and swap of nil value into Value") 138 } 139 vp := (*efaceWords)(unsafe.Pointer(v)) 140 np := (*efaceWords)(unsafe.Pointer(&new)) 141 op := (*efaceWords)(unsafe.Pointer(&old)) 142 if op.typ != nil && np.typ != op.typ { 143 panic("sync/atomic: compare and swap of inconsistently typed values") 144 } 145 for { 146 typ := LoadPointer(&vp.typ) 147 if typ == nil { 148 if old != nil { 149 return false 150 } 151 // Attempt to start first store. 152 // Disable preemption so that other goroutines can use 153 // active spin wait to wait for completion; and so that 154 // GC does not see the fake type accidentally. 155 runtime_procPin() 156 if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) { 157 runtime_procUnpin() 158 continue 159 } 160 // Complete first store. 161 StorePointer(&vp.data, np.data) 162 StorePointer(&vp.typ, np.typ) 163 runtime_procUnpin() 164 return true 165 } 166 if typ == unsafe.Pointer(&firstStoreInProgress) { 167 // First store in progress. Wait. 168 // Since we disable preemption around the first store, 169 // we can wait with active spinning. 170 continue 171 } 172 // First store completed. Check type and overwrite data. 173 if typ != np.typ { 174 panic("sync/atomic: compare and swap of inconsistently typed value into Value") 175 } 176 // Compare old and current via runtime equality check. 177 // This allows value types to be compared, something 178 // not offered by the package functions. 179 // CompareAndSwapPointer below only ensures vp.data 180 // has not changed since LoadPointer. 181 data := LoadPointer(&vp.data) 182 var i any 183 (*efaceWords)(unsafe.Pointer(&i)).typ = typ 184 (*efaceWords)(unsafe.Pointer(&i)).data = data 185 if i != old { 186 return false 187 } 188 return CompareAndSwapPointer(&vp.data, data, np.data) 189 } 190 } 191 192 // Disable/enable preemption, implemented in runtime. 193 func runtime_procPin() int 194 func runtime_procUnpin()