@@ -140,19 +140,19 @@ impl AtomicallyUpdate<i64> for i64 {
140
140
}
141
141
}
142
142
143
- pub ( crate ) struct F64AtomicTracker {
143
+ pub ( crate ) struct F64AtomicValue {
144
144
inner : Mutex < f64 > , // Floating points don't have true atomics, so we need to use mutex for them
145
145
}
146
146
147
- impl F64AtomicTracker {
147
+ impl F64AtomicValue {
148
148
pub ( crate ) fn new ( ) -> Self {
149
- F64AtomicTracker {
149
+ F64AtomicValue {
150
150
inner : Mutex :: new ( 0.0 ) ,
151
151
}
152
152
}
153
153
}
154
154
155
- impl AtomicValue < f64 > for F64AtomicTracker {
155
+ impl AtomicValue < f64 > for F64AtomicValue {
156
156
fn add ( & self , value : f64 ) {
157
157
let mut guard = self . inner . lock ( ) . expect ( "F64 mutex was poisoned" ) ;
158
158
* guard += value;
@@ -171,10 +171,10 @@ impl AtomicValue<f64> for F64AtomicTracker {
171
171
}
172
172
173
173
impl AtomicallyUpdate < f64 > for f64 {
174
- type AtomicValue = F64AtomicTracker ;
174
+ type AtomicValue = F64AtomicValue ;
175
175
176
176
fn new_atomic_tracker ( ) -> AtomicTracker < f64 , Self :: AtomicValue > {
177
- AtomicTracker :: new ( F64AtomicTracker :: new ( ) )
177
+ AtomicTracker :: new ( F64AtomicValue :: new ( ) )
178
178
}
179
179
}
180
180
@@ -188,6 +188,15 @@ impl<N, T: AtomicValue<N>> AtomicTracker<N, T> {
188
188
}
189
189
190
190
pub ( crate ) fn add ( & self , value : N ) {
191
+ // Technically we lose atomicity from using 2 atomics. However, the `add()` is specifically
192
+ // designed mutate the value *then* set `has_value`, while the `get_value()` is designed
193
+ // to read `has_value` *then* read the value. This means that in a worst case race
194
+ // condition, the value added may get picked up from a previous `get_value()` call, but
195
+ // the `has_value` being true will be picked up in the next `get_value()` call. This really
196
+ // should only mean that the first export gets the added value, and the 2nd export will
197
+ // get a 0 value.
198
+ //
199
+ // This doesn't seem like a big deal, and we avoid the cost of locking.
191
200
self . value . add ( value) ;
192
201
self . has_value . store ( true , Ordering :: Release ) ;
193
202
}
@@ -226,11 +235,11 @@ mod tests {
226
235
let atomic = u64:: new_atomic_tracker ( ) ;
227
236
atomic. add ( 15 ) ;
228
237
229
- let value = atomic. get_value ( true ) . unwrap ( ) ;
230
- let value2 = atomic. get_value ( false ) . unwrap ( ) ;
238
+ let value = atomic. get_value ( true ) ;
239
+ let value2 = atomic. get_value ( false ) ;
231
240
232
- assert_eq ! ( value, 15 , "Incorrect first value" ) ;
233
- assert_eq ! ( value2, 0 , "Incorrect second value" ) ;
241
+ assert_eq ! ( value, Some ( 15 ) , "Incorrect first value" ) ;
242
+ assert_eq ! ( value2, None , "Incorrect second value" ) ;
234
243
}
235
244
236
245
#[ test]
@@ -248,11 +257,11 @@ mod tests {
248
257
let atomic = i64:: new_atomic_tracker ( ) ;
249
258
atomic. add ( 15 ) ;
250
259
251
- let value = atomic. get_value ( true ) . unwrap ( ) ;
252
- let value2 = atomic. get_value ( false ) . unwrap ( ) ;
260
+ let value = atomic. get_value ( true ) ;
261
+ let value2 = atomic. get_value ( false ) ;
253
262
254
- assert_eq ! ( value, 15 , "Incorrect first value" ) ;
255
- assert_eq ! ( value2, 0 , "Incorrect second value" ) ;
263
+ assert_eq ! ( value, Some ( 15 ) , "Incorrect first value" ) ;
264
+ assert_eq ! ( value2, None , "Incorrect second value" ) ;
256
265
}
257
266
258
267
#[ test]
@@ -272,9 +281,9 @@ mod tests {
272
281
atomic. add ( 15.5 ) ;
273
282
274
283
let value = atomic. get_value ( true ) . unwrap ( ) ;
275
- let value2 = atomic. get_value ( false ) . unwrap ( ) ;
284
+ let value2 = atomic. get_value ( false ) ;
276
285
277
286
assert ! ( f64 :: abs( 15.5 - value) < 0.0001 , "Incorrect first value" ) ;
278
- assert ! ( f64 :: abs ( 0.0 - value2) < 0.0001 , "Incorrect second value" ) ;
287
+ assert_eq ! ( value2, None , "Expected no value from second get_value call " ) ;
279
288
}
280
289
}
0 commit comments