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
5package atomic
6
7import (
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.
16type Value struct {
17	v any
18}
19
20// efaceWords is interface{} internal representation.
21type 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.
28func (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
42var 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).
47func (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).
90func (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).
135func (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.
193func runtime_procPin() int
194func runtime_procUnpin()
195