github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/atomicbitops/atomicbitops_float64.go (about)

     1  // Copyright 2023 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  package atomicbitops
    16  
    17  import (
    18  	"math"
    19  
    20  	"github.com/MerlinKodo/gvisor/pkg/sync"
    21  )
    22  
    23  // Float64 is an atomic 64-bit floating-point number.
    24  //
    25  // +stateify savable
    26  type Float64 struct {
    27  	_ sync.NoCopy
    28  	// bits stores the bit of a 64-bit floating point number.
    29  	// It is not (and should not be interpreted as) a real uint64.
    30  	bits Uint64
    31  }
    32  
    33  // FromFloat64 returns a Float64 initialized to value v.
    34  //
    35  //go:nosplit
    36  func FromFloat64(v float64) Float64 {
    37  	return Float64{bits: FromUint64(math.Float64bits(v))}
    38  }
    39  
    40  // Load loads the floating-point value.
    41  //
    42  //go:nosplit
    43  func (f *Float64) Load() float64 {
    44  	return math.Float64frombits(f.bits.Load())
    45  }
    46  
    47  // RacyLoad is analogous to reading an atomic value without using
    48  // synchronization.
    49  //
    50  // It may be helpful to document why a racy operation is permitted.
    51  //
    52  //go:nosplit
    53  func (f *Float64) RacyLoad() float64 {
    54  	return math.Float64frombits(f.bits.RacyLoad())
    55  }
    56  
    57  // Store stores the given floating-point value in the Float64.
    58  //
    59  //go:nosplit
    60  func (f *Float64) Store(v float64) {
    61  	f.bits.Store(math.Float64bits(v))
    62  }
    63  
    64  // RacyStore is analogous to setting an atomic value without using
    65  // synchronization.
    66  //
    67  // It may be helpful to document why a racy operation is permitted.
    68  //
    69  //go:nosplit
    70  func (f *Float64) RacyStore(v float64) {
    71  	f.bits.RacyStore(math.Float64bits(v))
    72  }
    73  
    74  // Swap stores the given value and returns the previously-stored one.
    75  //
    76  //go:nosplit
    77  func (f *Float64) Swap(v float64) float64 {
    78  	return math.Float64frombits(f.bits.Swap(math.Float64bits(v)))
    79  }
    80  
    81  // CompareAndSwap does a compare-and-swap operation on the float64 value.
    82  // Note that unlike typical IEEE 754 semantics, this function will treat NaN
    83  // as equal to itself if all of its bits exactly match.
    84  //
    85  //go:nosplit
    86  func (f *Float64) CompareAndSwap(oldVal, newVal float64) bool {
    87  	return f.bits.CompareAndSwap(math.Float64bits(oldVal), math.Float64bits(newVal))
    88  }
    89  
    90  // Add increments the float by the given value.
    91  // Note that unlike an atomic integer, this requires spin-looping until we win
    92  // the compare-and-swap race, so this may take an indeterminate amount of time.
    93  //
    94  //go:nosplit
    95  func (f *Float64) Add(v float64) {
    96  	// We do a racy load here because we optimistically think it may pass the
    97  	// compare-and-swap operation. If it doesn't, we'll load it safely, so this
    98  	// is OK and not a race for the overall intent of the user to add a number.
    99  	sync.RaceDisable()
   100  	oldVal := f.RacyLoad()
   101  	for !f.CompareAndSwap(oldVal, oldVal+v) {
   102  		oldVal = f.Load()
   103  	}
   104  	sync.RaceEnable()
   105  }