github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/atomicbitops/aligned_32bit_unsafe.go (about) 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 "github.com/nicocha30/gvisor-ligolo/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 }