| // 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 Load. |
| // Once Store has been called, a Value must not be copied. |
| // |
| // A Value must not be copied after first use. |
| type Value struct { |
| v any |
| } |
| |
| // ifaceWords is interface{} internal representation. |
| type ifaceWords 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 := (*ifaceWords)(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 := (*ifaceWords)(unsafe.Pointer(&val)) |
| vlp.typ = typ |
| vlp.data = data |
| return |
| } |
| |
| var firstStoreInProgress byte |
| |
| // Store sets the value of the Value to x. |
| // 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 := (*ifaceWords)(unsafe.Pointer(v)) |
| vlp := (*ifaceWords)(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 := (*ifaceWords)(unsafe.Pointer(v)) |
| np := (*ifaceWords)(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; and so that |
| // GC does not see the fake type accidentally. |
| runtime_procPin() |
| if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) { |
| runtime_procUnpin() |
| continue |
| } |
| // Complete first store. |
| StorePointer(&vp.data, np.data) |
| StorePointer(&vp.typ, np.typ) |
| runtime_procUnpin() |
| return nil |
| } |
| if uintptr(typ) == ^uintptr(0) { |
| // 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 := (*ifaceWords)(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 := (*ifaceWords)(unsafe.Pointer(v)) |
| np := (*ifaceWords)(unsafe.Pointer(&new)) |
| op := (*ifaceWords)(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; and so that |
| // GC does not see the fake type accidentally. |
| runtime_procPin() |
| if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) { |
| runtime_procUnpin() |
| continue |
| } |
| // Complete first store. |
| StorePointer(&vp.data, np.data) |
| StorePointer(&vp.typ, np.typ) |
| runtime_procUnpin() |
| return true |
| } |
| if uintptr(typ) == ^uintptr(0) { |
| // 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 |
| (*ifaceWords)(unsafe.Pointer(&i)).typ = typ |
| (*ifaceWords)(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() |
| func runtime_procUnpin() |