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