2021年1月4日星期一

Why can AtomicUsize's fetch_max return a value greater than its argument?

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!

https://stackoverflow.com/questions/65572965/why-can-atomicusizes-fetch-max-return-a-value-greater-than-its-argument January 05, 2021 at 11:44AM

没有评论:

发表评论