| // Copyright 2014 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package atomic | |
| import ( | |
| "unsafe" | |
| ) | |
| // A Value provides an atomic load and store of a consistently typed value. | |
| // The zero value for a Value returns nil from [Value.Load]. | |
| // Once [Value.Store] has been called, a Value must not be copied. | |
| // | |
| // A Value must not be copied after first use. | |
| type Value struct { | |
| v any | |
| } | |
| // efaceWords is interface{} internal representation. | |
| type efaceWords struct { | |
| typ unsafe.Pointer | |
| data unsafe.Pointer | |
| } | |
| // Load returns the value set by the most recent Store. | |
| // It returns nil if there has been no call to Store for this Value. | |
| func (v *Value) Load() (val any) { | |
| vp := (*efaceWords)(unsafe.Pointer(v)) | |
| typ := LoadPointer(&vp.typ) | |
| if typ == nil || typ == unsafe.Pointer(&firstStoreInProgress) { | |
| // First store not yet completed. | |
| return nil | |
| } | |
| data := LoadPointer(&vp.data) | |
| vlp := (*efaceWords)(unsafe.Pointer(&val)) | |
| vlp.typ = typ | |
| vlp.data = data | |
| return | |
| } | |
| var firstStoreInProgress byte | |
| // Store sets the value of the [Value] v to val. | |
| // All calls to Store for a given Value must use values of the same concrete type. | |
| // Store of an inconsistent type panics, as does Store(nil). | |
| func (v *Value) Store(val any) { | |
| if val == nil { | |
| panic("sync/atomic: store of nil value into Value") | |
| } | |
| vp := (*efaceWords)(unsafe.Pointer(v)) | |
| vlp := (*efaceWords)(unsafe.Pointer(&val)) | |
| for { | |
| typ := LoadPointer(&vp.typ) | |
| if typ == nil { | |
| // Attempt to start first store. | |
| // Disable preemption so that other goroutines can use | |
| // active spin wait to wait for completion. | |
| runtime_procPin() | |
| if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) { | |
| runtime_procUnpin() | |
| continue | |
| } | |
| // Complete first store. | |
| StorePointer(&vp.data, vlp.data) | |
| StorePointer(&vp.typ, vlp.typ) | |
| runtime_procUnpin() | |
| return | |
| } | |
| if typ == unsafe.Pointer(&firstStoreInProgress) { | |
| // First store in progress. Wait. | |
| // Since we disable preemption around the first store, | |
| // we can wait with active spinning. | |
| continue | |
| } | |
| // First store completed. Check type and overwrite data. | |
| if typ != vlp.typ { | |
| panic("sync/atomic: store of inconsistently typed value into Value") | |
| } | |
| StorePointer(&vp.data, vlp.data) | |
| return | |
| } | |
| } | |
| // Swap stores new into Value and returns the previous value. It returns nil if | |
| // the Value is empty. | |
| // | |
| // All calls to Swap for a given Value must use values of the same concrete | |
| // type. Swap of an inconsistent type panics, as does Swap(nil). | |
| func (v *Value) Swap(new any) (old any) { | |
| if new == nil { | |
| panic("sync/atomic: swap of nil value into Value") | |
| } | |
| vp := (*efaceWords)(unsafe.Pointer(v)) | |
| np := (*efaceWords)(unsafe.Pointer(&new)) | |
| for { | |
| typ := LoadPointer(&vp.typ) | |
| if typ == nil { | |
| // Attempt to start first store. | |
| // Disable preemption so that other goroutines can use | |
| // active spin wait to wait for completion. | |
| runtime_procPin() | |
| if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) { | |
| runtime_procUnpin() | |
| continue | |
| } | |
| // Complete first store. | |
| StorePointer(&vp.data, np.data) | |
| StorePointer(&vp.typ, np.typ) | |
| runtime_procUnpin() | |
| return nil | |
| } | |
| if typ == unsafe.Pointer(&firstStoreInProgress) { | |
| // First store in progress. Wait. | |
| // Since we disable preemption around the first store, | |
| // we can wait with active spinning. | |
| continue | |
| } | |
| // First store completed. Check type and overwrite data. | |
| if typ != np.typ { | |
| panic("sync/atomic: swap of inconsistently typed value into Value") | |
| } | |
| op := (*efaceWords)(unsafe.Pointer(&old)) | |
| op.typ, op.data = np.typ, SwapPointer(&vp.data, np.data) | |
| return old | |
| } | |
| } | |
| // CompareAndSwap executes the compare-and-swap operation for the [Value]. | |
| // | |
| // All calls to CompareAndSwap for a given Value must use values of the same | |
| // concrete type. CompareAndSwap of an inconsistent type panics, as does | |
| // CompareAndSwap(old, nil). | |
| func (v *Value) CompareAndSwap(old, new any) (swapped bool) { | |
| if new == nil { | |
| panic("sync/atomic: compare and swap of nil value into Value") | |
| } | |
| vp := (*efaceWords)(unsafe.Pointer(v)) | |
| np := (*efaceWords)(unsafe.Pointer(&new)) | |
| op := (*efaceWords)(unsafe.Pointer(&old)) | |
| if op.typ != nil && np.typ != op.typ { | |
| panic("sync/atomic: compare and swap of inconsistently typed values") | |
| } | |
| for { | |
| typ := LoadPointer(&vp.typ) | |
| if typ == nil { | |
| if old != nil { | |
| return false | |
| } | |
| // Attempt to start first store. | |
| // Disable preemption so that other goroutines can use | |
| // active spin wait to wait for completion. | |
| runtime_procPin() | |
| if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) { | |
| runtime_procUnpin() | |
| continue | |
| } | |
| // Complete first store. | |
| StorePointer(&vp.data, np.data) | |
| StorePointer(&vp.typ, np.typ) | |
| runtime_procUnpin() | |
| return true | |
| } | |
| if typ == unsafe.Pointer(&firstStoreInProgress) { | |
| // First store in progress. Wait. | |
| // Since we disable preemption around the first store, | |
| // we can wait with active spinning. | |
| continue | |
| } | |
| // First store completed. Check type and overwrite data. | |
| if typ != np.typ { | |
| panic("sync/atomic: compare and swap of inconsistently typed value into Value") | |
| } | |
| // Compare old and current via runtime equality check. | |
| // This allows value types to be compared, something | |
| // not offered by the package functions. | |
| // CompareAndSwapPointer below only ensures vp.data | |
| // has not changed since LoadPointer. | |
| data := LoadPointer(&vp.data) | |
| var i any | |
| (*efaceWords)(unsafe.Pointer(&i)).typ = typ | |
| (*efaceWords)(unsafe.Pointer(&i)).data = data | |
| if i != old { | |
| return false | |
| } | |
| return CompareAndSwapPointer(&vp.data, data, np.data) | |
| } | |
| } | |
| // Disable/enable preemption, implemented in runtime. | |
| func runtime_procPin() int | |
| func runtime_procUnpin() |