Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atomic operations on part of an atomic integer? #295

Closed
sporksmith opened this issue Nov 23, 2022 · 3 comments
Closed

Atomic operations on part of an atomic integer? #295

sporksmith opened this issue Nov 23, 2022 · 3 comments

Comments

@sporksmith
Copy link

It'd be useful to be able to perform atomic operations on parts of atomic integer in addition to the whole thing.

e.g. the following runs as expected, including under miri:

use std::sync::atomic;

pub fn main() {
    let whole: atomic::AtomicU64 = atomic::AtomicU64::new(0);
    // Valid because AtomicU64 is repr(transparent) around a u64,
    // and likewise AtomicU32 for u32.
    let parts_ptr = &whole as *const _ as *const atomic::AtomicU32;

    // We can create valid references for the "top"
    // and "bottom" parts of the Atomicu64.
    let top: &atomic::AtomicU32 = unsafe { &*parts_ptr };
    let bottom: &atomic::AtomicU32 = unsafe { &*parts_ptr.add(1) };

    // And we can operate on them.
    top.fetch_add(1, atomic::Ordering::Relaxed);
    bottom.fetch_add(1, atomic::Ordering::Relaxed);

    // We can still operate on the whole word.
    assert_eq!(whole.load(atomic::Ordering::Relaxed), 1u64 + (1u64 << 32));
}

This isn't valid using loom's atomics, since they aren't repr(transparent) wrappers around the underlying integer types.

Is there some way that miri atomics might be able to support this?

As a motivating example, I'm implementing a mutex, and would like to be keep the locked/unlocked state, and the number of threads sleeping on the mutex, in a 64 bit integer, in the individual 32 bit pieces. I'd like to be able to e.g. compare and swap the mutex state without caring about the number of sleepers when locking, while still being able to atomically unlock and get the sleeper count when unlocking.

@taiki-e
Copy link
Member

taiki-e commented Nov 24, 2022

including under miri

AFAIK, for Miri, mixed-size access is considered an open issue rather than a supported operation: rust-lang/unsafe-code-guidelines#345

In fact, when I run your example in multi-threaded, Miri reports an error that the operation is not supported.

error: unsupported operation: racy imperfectly overlapping atomic access is not possible in the C++20 memory model, and not supported by Miri's weak memory emulation
  --> src/main.rs:16:9
   |
16 |         top.fetch_add(1, atomic::Ordering::Relaxed);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ racy imperfectly overlapping atomic access is not possible in the C++20 memory model, and not supported by Miri's weak memory emulation
   |
   = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
   = note: BACKTRACE:
   = note: inside closure at src/main.rs:16:9

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=cb58d0a4c130250750025fd68ef15ee7

@sporksmith
Copy link
Author

Thanks for the reference and counterexample! Looks like loom steered me in the right direction here, at least while it's still an open issue in Rust itself.

@Darksonn
Copy link
Contributor

Yeah, the only reason your original code works is that they don't happen in parallel. I don't think we are going to support this unless std defines the behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants