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