aligned_32bit_unsafe.go raw

   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