atomicbitops_float64.go raw

   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  	"gvisor.dev/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  }
 106