Pre-RFC: New Atomic methods: compare_exchange_loop

I found a phenomenon that every time I called compare_exchange_weak, it almost appeared in a circular form, and almost the same code logic had to be written every time, increasing the mental burden of the user. I have a new idea here, adding a new function: compare_exchange_loop, which greatly simplifies the use of the cas function. It's implementation example (Here, it is given by trait to facilitate users to test):

use std::sync::atomic::{AtomicI64, Ordering};

pub trait AtomicExt<T> {
    fn compare_exchange_loop(&self, f: impl Fn(T) -> T) -> (T, T);
}

impl AtomicExt<i64> for AtomicI64 {
    fn compare_exchange_loop(&self, f: impl Fn(i64) -> i64) -> (i64, i64) {
        let mut old = self.load(Ordering::Relaxed);
        let mut new = f(old);
        loop {
            match self.compare_exchange_weak(old, new, Ordering::AcqRel, Ordering::Relaxed) {
                Ok(_) => break,
                Err(val) => (old, new) = (val, f(val)),
            }
        }
        (old, new)
    }
}

For example, I need to obtain a unique timestamp with a microsecond accuracy:

// use compare_exchange_weak
fn get_unique_timestamp1() -> i64 {
    static TIMESTAMP: AtomicI64 = AtomicI64::new(0);
    let ts = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_micros() as i64;
    let mut old = TIMESTAMP.load(Ordering::Relaxed);
    let mut new = ts.max(old + 1);
    loop {
        match TIMESTAMP.compare_exchange_weak(old, new, Ordering::AcqRel, Ordering::Relaxed) {
            Ok(_) => break,
            Err(val) => (old, new) = (val, ts.max(val + 1)),
        }
    }
    new
}

// use compare_exchange_loop
fn get_unique_timestamp2() -> i64 {
    static TIMESTAMP: AtomicI64 = AtomicI64::new(0);
    let ts = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_micros() as i64;
    let (_, new) = TIMESTAMP.compare_exchange_loop(|old| ts.max(old + 1));
    new
}

Like this?

1 Like

even if this didn't exist, this would only need to be an ACP (API Change Proposal), not an RFC.