From the description of AtomicUsize's fetch_max, it seems like the return value should always be smaller than or equal to the argument passed to the val
parameter.
However, consider the following code:
use rand::Rng;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
const fn generate_bools() -> [bool; 100] {
// An array of false values, except for certain indexes.
let mut bools = [false; 100];
bools[2] = true;
bools[7] = true;
bools[12] = true;
bools[14] = true;
bools[22] = true;
bools[30] = true;
bools[36] = true;
bools[41] = true;
bools[43] = true;
bools[53] = true;
bools[56] = true;
bools[63] = true;
bools[72] = true;
bools[79] = true;
bools[82] = true;
bools
}
const BOOLS: [bool; 100] = generate_bools();
fn main() {
let counter = Arc::new(AtomicUsize::new(0));
// Spawn 10 threads, for counter increments of +1 to +10 inclusively.
for inc in 1..=10 {
let counter = counter.clone();
std::thread::spawn(move || loop {
// Load the current counter and add the increment for this thread.
let current = counter.load(Ordering::SeqCst);
let new = current + inc;
// Wait for a random duration between 100 and 355 ms.
let random = rand::thread_rng().gen::<u8>() + 100;
std::thread::sleep(Duration::from_millis(random as u64));
// If the array is true at that index, "fetch_max" it!
let is_true = BOOLS[new];
if is_true {
let old = counter.fetch_max(new, Ordering::SeqCst);
println!("old: {:02} / new: {:02}", old, new);
}
std::thread::sleep(Duration::from_secs(1));
});
}
// Put main thread to sleep for 60s to give time for the children threads.
std::thread::sleep(Duration::from_secs(60));
}
When I run it, I get unexpected results. Here are two samples:
old: 00 / new: 02
old: 02 / new: 07
old: 07 / new: 14
old: 14 / new: 22
old: 22 / new: 30
old: 30 / new: 36
old: 36 / new: 43
old: 43 / new: 53
old: 53 / new: 63
old: 63 / new: 72
old: 72 / new: 56 <--
old: 72 / new: 79
old: 79 / new: 82
old: 00 / new: 02
old: 02 / new: 07
old: 07 / new: 12
old: 12 / new: 14
old: 14 / new: 22
old: 22 / new: 30
old: 30 / new: 36
old: 36 / new: 43
old: 43 / new: 41 <--
old: 43 / new: 53
old: 53 / new: 56
old: 56 / new: 63
old: 63 / new: 63
old: 63 / new: 72
old: 72 / new: 79
old: 79 / new: 82
I understand that the "new" values might not always go up, because there might be a race condition with the fetch_max
and println!
statements. For example, thread A can do the fetch_add
, then thread B can do the fetch_add
and the println!
, then thread A can do the println!
.
What I don't understand is how the old
value can be strictly greater than the new
value! Thank you for any help!