1 // Copyright 2021 The gVisor Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 15 //go:build arm || mips || mipsle || 386
16 // +build arm mips mipsle 386
17 18 package atomicbitops
19 20 import (
21 "sync/atomic"
22 "unsafe"
23 24 "gvisor.dev/gvisor/pkg/sync"
25 )
26 27 // Int64 is an atomic int64 that is guaranteed to be 64-bit
28 // aligned, even on 32-bit systems.
29 //
30 // Don't add fields to this struct. It is important that it remain the same
31 // size as its builtin analogue.
32 //
33 // Per https://golang.org/pkg/sync/atomic/#pkg-note-BUG:
34 //
35 // "On ARM, 386, and 32-bit MIPS, it is the caller's responsibility to arrange
36 // for 64-bit alignment of 64-bit words accessed atomically. The first word in
37 // a variable or in an allocated struct, array, or slice can be relied upon to
38 // be 64-bit aligned."
39 //
40 // +stateify savable
41 type Int64 struct {
42 _ sync.NoCopy
43 value int64
44 value32 int32
45 }
46 47 //go:nosplit
48 func (i *Int64) ptr() *int64 {
49 // On 32-bit systems, i.value is guaranteed to be 32-bit aligned. It means
50 // that in the 12-byte i.value, there are guaranteed to be 8 contiguous bytes
51 // with 64-bit alignment.
52 return (*int64)(unsafe.Pointer((uintptr(unsafe.Pointer(&i.value)) + 4) &^ 7))
53 }
54 55 // FromInt64 returns an Int64 initialized to value v.
56 //
57 //go:nosplit
58 func FromInt64(v int64) Int64 {
59 var i Int64
60 *i.ptr() = v
61 return i
62 }
63 64 // Load is analogous to atomic.LoadInt64.
65 //
66 //go:nosplit
67 func (i *Int64) Load() int64 {
68 return atomic.LoadInt64(i.ptr())
69 }
70 71 // RacyLoad is analogous to reading an atomic value without using
72 // synchronization.
73 //
74 // It may be helpful to document why a racy operation is permitted.
75 //
76 //go:nosplit
77 func (i *Int64) RacyLoad() int64 {
78 return *i.ptr()
79 }
80 81 // Store is analogous to atomic.StoreInt64.
82 //
83 //go:nosplit
84 func (i *Int64) Store(v int64) {
85 atomic.StoreInt64(i.ptr(), v)
86 }
87 88 // RacyStore is analogous to setting an atomic value without using
89 // synchronization.
90 //
91 // It may be helpful to document why a racy operation is permitted.
92 //
93 //go:nosplit
94 func (i *Int64) RacyStore(v int64) {
95 *i.ptr() = v
96 }
97 98 // Add is analogous to atomic.AddInt64.
99 //
100 //go:nosplit
101 func (i *Int64) Add(v int64) int64 {
102 return atomic.AddInt64(i.ptr(), v)
103 }
104 105 // RacyAdd is analogous to adding to an atomic value without using
106 // synchronization.
107 //
108 // It may be helpful to document why a racy operation is permitted.
109 //
110 //go:nosplit
111 func (i *Int64) RacyAdd(v int64) int64 {
112 *i.ptr() += v
113 return *i.ptr()
114 }
115 116 // Swap is analogous to atomic.SwapInt64.
117 //
118 //go:nosplit
119 func (i *Int64) Swap(v int64) int64 {
120 return atomic.SwapInt64(i.ptr(), v)
121 }
122 123 // CompareAndSwap is analogous to atomic.CompareAndSwapInt64.
124 //
125 //go:nosplit
126 func (i *Int64) CompareAndSwap(oldVal, newVal int64) bool {
127 return atomic.CompareAndSwapInt64(&i.value, oldVal, newVal)
128 }
129 130 // Uint64 is an atomic uint64 that is guaranteed to be 64-bit
131 // aligned, even on 32-bit systems.
132 //
133 // Don't add fields to this struct. It is important that it remain the same
134 // size as its builtin analogue.
135 //
136 // Per https://golang.org/pkg/sync/atomic/#pkg-note-BUG:
137 //
138 // "On ARM, 386, and 32-bit MIPS, it is the caller's responsibility to arrange
139 // for 64-bit alignment of 64-bit words accessed atomically. The first word in
140 // a variable or in an allocated struct, array, or slice can be relied upon to
141 // be 64-bit aligned."
142 //
143 // +stateify savable
144 type Uint64 struct {
145 _ sync.NoCopy
146 value uint64
147 value32 uint32
148 }
149 150 //go:nosplit
151 func (u *Uint64) ptr() *uint64 {
152 // On 32-bit systems, i.value is guaranteed to be 32-bit aligned. It means
153 // that in the 12-byte i.value, there are guaranteed to be 8 contiguous bytes
154 // with 64-bit alignment.
155 return (*uint64)(unsafe.Pointer((uintptr(unsafe.Pointer(&u.value)) + 4) &^ 7))
156 }
157 158 // FromUint64 returns an Uint64 initialized to value v.
159 //
160 //go:nosplit
161 func FromUint64(v uint64) Uint64 {
162 var u Uint64
163 *u.ptr() = v
164 return u
165 }
166 167 // Load is analogous to atomic.LoadUint64.
168 //
169 //go:nosplit
170 func (u *Uint64) Load() uint64 {
171 return atomic.LoadUint64(u.ptr())
172 }
173 174 // RacyLoad is analogous to reading an atomic value without using
175 // synchronization.
176 //
177 // It may be helpful to document why a racy operation is permitted.
178 //
179 //go:nosplit
180 func (u *Uint64) RacyLoad() uint64 {
181 return *u.ptr()
182 }
183 184 // Store is analogous to atomic.StoreUint64.
185 //
186 //go:nosplit
187 func (u *Uint64) Store(v uint64) {
188 atomic.StoreUint64(u.ptr(), v)
189 }
190 191 // RacyStore is analogous to setting an atomic value without using
192 // synchronization.
193 //
194 // It may be helpful to document why a racy operation is permitted.
195 //
196 //go:nosplit
197 func (u *Uint64) RacyStore(v uint64) {
198 *u.ptr() = v
199 }
200 201 // Add is analogous to atomic.AddUint64.
202 //
203 //go:nosplit
204 func (u *Uint64) Add(v uint64) uint64 {
205 return atomic.AddUint64(u.ptr(), v)
206 }
207 208 // RacyAdd is analogous to adding to an atomic value without using
209 // synchronization.
210 //
211 // It may be helpful to document why a racy operation is permitted.
212 //
213 //go:nosplit
214 func (u *Uint64) RacyAdd(v uint64) uint64 {
215 *u.ptr() += v
216 return *u.ptr()
217 }
218 219 // Swap is analogous to atomic.SwapUint64.
220 //
221 //go:nosplit
222 func (u *Uint64) Swap(v uint64) uint64 {
223 return atomic.SwapUint64(u.ptr(), v)
224 }
225 226 // CompareAndSwap is analogous to atomic.CompareAndSwapUint64.
227 //
228 //go:nosplit
229 func (u *Uint64) CompareAndSwap(oldVal, newVal uint64) bool {
230 return atomic.CompareAndSwapUint64(u.ptr(), oldVal, newVal)
231 }
232