Is accessing an object through the same pointer from multiple threads for atomic operations undefined behavior?

Viewed 181

So I am experimenting with some lock-free algorithms and wait-free algorithms in rust. I have found that the AtomicPtr class is quite useful here as it allows operations like cmpexchange or swapping etc on pointer values for implementations of lock-free data structures. However, I read something potentially concerning on Rust's rules about pointer aliasing :

Breaking the pointer aliasing rules. &mut T and &T follow LLVM’s scoped noalias model, except if the &T contains an UnsafeCell. Mutating immutable data. All data inside a const item is immutable. Moreover, all data reached through a shared reference or data owned by an immutable binding is immutable, unless that data is contained within an UnsafeCell.

More so, the LLVM section they link to also sounds concerning

This indicates that memory locations accessed via pointer values based on the argument or return value are not also accessed, during the execution of the function, via pointer values not based on the argument or return value. This guarantee only holds for memory locations that are modified, by any means, during the execution of the function.

From this, it's not quite clear how atomics could be considered defined behavior, as an atomic operation does mutate the contents of the data a pointer points to. The Rust docs nor the LLVM list atomics as an exception to this.

Thus, I wanted to know if the following code would be considered undefined behavior in Rust:

struct Point {
    x:AtomicUsize,
    y:AtomicUsize
}

impl Point {
    fn new_ptr() -> *mut Point {
        Box::into_raw(Box::new(Point{x:AtomicUsize::new(0), y:AtomicUsize::new(0)}))
    }
}

fn main() {
    let point = Point::new_ptr();
    let cont = AtomicPtr::new(point);
    let handle = thread::spawn(move || {
        let r = unsafe {  cont.load(Ordering::SeqCst).as_ref().unwrap() };
        for _ in 0..10000 {
            r.x.fetch_add(1, Ordering::SeqCst);
        }
    });
    unsafe {  point.as_ref().unwrap().x.fetch_add(1, Ordering::SeqCst); }
    handle.join().unwrap();
    unsafe {
        drop(Box::from_raw(point));
    }
}

It compiles and executes as expected, along with slight variations. But unsure if what I am doing is undefined or not. I want to ensure this allowed behavior is not restricted or changed in the future.

0 Answers
Related