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 [Value.Load].
13 // Once [Value.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()
195