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  }