-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Tracking Issue for atomic_from_mut #76314
Comments
For some types, these functions introduce a significant risk of accidentally writing non-portable code: when I am on x86_64, I might not realize that by calling |
But the same holds for the other atomics operations, right? Using 64-bit atomic cas operations will also exclude a lot of platforms. The alternative is that people write this manually using transmute. That code will be equally non-portable, except it'll compile just fine and silently cause UB. That said, it might be good to extend the note in the documentation to mention platforms like x86 and x64_32 specifically. |
Yeah, I am not saying this is a blocker. This could be something that good documentation can solve. |
Can |
@RustyYato Is there a use case for that? There's not much more you can do with a |
@m-ou-se No specific use-case, it just seems odd to me that |
Okay. I've added your suggestion to the unresolved questions to resolve before stabilizing this. |
I think the following two make sense.
|
What would be the safety requirements on Overlapping atomics of different sizes is probably an obscure use case, but it would be really helpful in some situations. However, Rust's memory model (i.e., C++ memory model) does not appear to account for that possibility. |
Such overlapping atomics are called "mixed accesses" in the weak memory literature, and indeed most memory models do not support them. |
What is the reasoning behind it being invalid to cast from |
&usize is immutable, the compiler is allowed to assume the value behind the reference will never change. &AtomicUsize is "mutable" through its atomic operations. So any sort of cast like that is unsound. |
Ah right, that makes sense. It's not the same as a |
These methods were in As a first step, I can make a PR that changes the output from |
One concern we might have is that AFAIK C++ does not have a comparable API, so this could move us into a situation where we cannot just copy the C++ memory model for atomics. |
I'm not very familiar with C++, but after a quick scan of its atomic docs it seems like C++ doesn't have |
C++ has But I think I just realized that |
However,
In reality, this isn't quite allowed, because you can separately make multiple However, this can be fixed by adding a layer of indirection. Instead of I'm not sure whether the fact this is possible is an intentional choice to support architectures that can't manipulate arbitrary memory atomically, or just a coincidence. Either way, it means that That said, |
…_ref, r=m-ou-se Make `Atomic*::from_mut` return `&mut Atomic*` ```rust impl Atomic* { pub fn from_mut(v: &mut bool) -> &mut Self; // ^^^^---- previously was just a & } ``` This PR makes `from_mut` atomic methods tracked in rust-lang#76314 return unique references to atomic types, instead of shared ones. This makes `from_mut` and `get_mut` inverses of each other, allowing to undo either of them by the other. r? `@RalfJung` (as Ralf was [concerned](rust-lang#76314 (comment)) about this)
Add Atomic*::from_mut_slice Tracking issue rust-lang#76314 for `from_mut` has a question about the possibility of `from_mut_slice`, and I found a real case for it. A user in the forum had a parallelism problem that could be solved by open-indexing updates to a vector of atomics, but they didn't want to affect the other code using that vector. Using `from_mut_slice`, they could borrow that data as atomics just long enough for their parallel loop. ref: https://users.rust-lang.org/t/sharing-vector-with-rayon-par-iter-correctly/72022
Is it possible to write a signature that consumes the &mut? The point of from_mut is that an AtomicUxxx can be accessed safely from multiple threads, without causing UB. It supports interior mutability, so it should be possible to get a shared reference to an atomic without using unsafe. But that's only possible if one cannot write let mut x: usize = 42;
let xref = &mut x;
let xaref = AtomicUsize::from_mut(xref); // Shared ref
// Send xaref around...
*xref = 53; // Undefined behavior!!! If that's not possible then yeah it has to be unsafe. |
@bonzini borrow checker will stop you from doing any undefined behavior here :)
let mut x: usize = 42;
let xref = &mut x;
let xaref = AtomicUsize::from_mut(xref);
// Send xaref around...
*xref = 53;
dbg!(xaref);
|
Your example uses an &, not an &mut, so I am a bit confused by the question in general. |
Don't write Rust on a phone is the logical conclusion of that (I edited now). Anyway, then I think from_mut should return a shared reference. |
But why? There is no benefit from that, only less (well-defined) code that can compile. |
The benefit is being able to go safely from single-thread mutable Here is a playground link that demonstrate this possibility. |
You can do that with the current signature. So, what you want can already be done. No need to make Here is your playground example adjusted accordingly. (Due to the closure, you have to tell the type checker to apply the coercion. In most situations it would be applied implicitly.) |
Ah, of course ( |
could the alignment requirement be integrated into the type signatures for the cases where that is problematic? (perhaps via some |
I don't think that this function makes sense as a fallible one. I don't see a usecase where you'd be able to recover somehow/having a function work only sometimes is valuable... |
this is why I'd like to see it integrated into the type signatures, to make sure it can't even be called with under-aligned objects. |
The function is unavailable on targets where |
It would be nice to have an alternative on platforms where the alignment doesn't match per default, that is, a way to force the alignment and also specify that in the type signature would be nice (on platforms where the alignment wouldn't be widened, that would be a no-op) |
We could have some sort of |
Have you considered shared memory (Linux)? I am writing a traffic generator (in rust) where the statistics is stored in shared memory. An independent presentation program (in any language) can map the shared memory and present statistics in real time. The traffic generator is multi-threaded (tokio), so counters must be updated atomically. Example: // The "stats" struct is mapped to shared memory
let bytes_sent = AtomicU64::from_mut(&mut stats.bytes_sent);
bytes_sent.fetch_add(bytes, Ordering::Relaxed); This works fine, but I must use Is there a way to accomplish this in a way that can be used in "stable"? Or shall I wait until this feature makes it to "stable". |
If you are using shared memory, and you ever have a mutable reference to that memory, something already went terribly wrong. Mutable references must be unique, which implies "not shared".
|
Um, are you saying that the use of Linux shared memory is forbidden in rust? Or at least writing to it? |
You shouldn't use |
Um, are you saying that the use of Linux shared memory is forbidden in rust? Or at least writing to it?
Where did I say that? I said you can't have an `&mut` to such memory. I was quite specific.
Shared memory has to be behind a raw pointer or behind a shared reference inside an UnsafeCell (or immutable, then it can be a regular shared reference). It's the same rules as when sharing memory between multiple threads inside a single process, so this should not be surprising: reference can be either aliased or mutable, but never both at the same time (except via `&UnsafeCell`). Shared memory is always aliased -- the "shared" is even in the name, therefore if it is mutable, one has to use interior mutability (or not use references at all).
|
Changed to: let bytes_sent = unsafe {
AtomicU64::from_ptr(&mut stats.bytes_sent as *mut u64)
}; and switched back to |
You're still creating a mutable reference here, so this code still breaks the aliasing rules (and hence has Undefined Behavior). Also, if |
Will do. You are right, I thought it wasn't possible to use an existing u64 for atomic operations without "nightly". Hence my comment in the issue. Sorry. Updated again to; let bytes_sent = unsafe {
AtomicU64::from_ptr(addr_of_mut!(stats.bytes_sent))
}; Thanks. |
Feature gate:
#![feature(atomic_from_mut)]
Public API
Each one is gated on
#[cfg(target_has_atomic_equal_alignment = "..")]
and is only available on platforms whereAtomic<size>
has the same alignment asu<size>
.Steps / History
Atomic*::from_mut
return&mut Atomic*
#92671Atomic*::get_mut_slice
#94816Unresolved Questions
Should these return— yes: Make&mut Self
instead, such that it's the exact inverse ofget_mut
?Atomic*::from_mut
return&mut Atomic*
#92671Also add— yes: Add Atomic*::from_mut_slice #94384from_mut_slice
?The text was updated successfully, but these errors were encountered: