-
Notifications
You must be signed in to change notification settings - Fork 15
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
Single-instruction-read/writes atomics #37
Comments
This is |
But is this available on all single-core platforms, e.g. Cortex M0? I thought it wasn't (https://rust-embedded.github.io/book/concurrency/index.html#atomic-access). Also if it does synchronization across cores, it's not exactly the same thing as I'm proposing: atomics across interrupts on the same core, with no cross-core synchronization overhead. |
The atomic types are available everywhere (if not, that's a bug in Rust or LLVM), but CAS operations might not be. You still need to synchronize access between interrupt handlers and idle. As written, |
Thanks, I will double check this.
I don't understand what data race you're talking about. Could you give an example of a scenario where the data race would trigger, and how it's UB? Also, I'm unsure how I can get |
The This is still the case if you replace "thread" with "interrupt handler". For example, the compiler might assume that a value loaded by subsequent The |
Thanks a lot for the explanation 👍 I think I understood almost all of it now! :)
From the UnsafeCell documentation:
I thought this explained it prevented that kind of optimizations.and enforced actually re-reading the value everytime it's asked. |
That was just an example of an assumption that a compiler might make. You'd have to ask that question in terms of Rust's language semantics to get a complete answer, and I'm not sure how the precise answer looks there. |
Here is a real example with your code https://godbolt.org/z/GsYx1q you can see that // Wait for the flag
while FOO.get() == 0 {
// nop
} is compiled to // Wait for the flag
if FOO.get() == 0 {
loop {
// nop
}
} Compiler assumes there is no data races in the code, so value of FOO can never change inside the loop. Which means no need to read it every time we can check it only once. |
This exact problem actually can occur with the bare-metal Mutex, which is why we added documentation in #34 after discussion in #33 (comment). In the cortex-m crate, compiler fences or memory clobbers are used when entering and leaving critical sections to ensure the compiler does not move accesses outside the the critical section, as otherwise @pftbest's example above would miscompile the same way. |
Great, thank you for all the examples and references! |
We have Mutex. However, opening a critical section (disabling interrupts) to just read or write a
Mutex<Cell<u8>>
seems a bit overkill: the read or write is a single instruction, which makes it impossible for an interrupt to hit in the middle.How about this crate also provides some form of atomics with respect to what the
Send
andSync
markers mean in our context?(I beleive they would be implemented this way, I'd be very willing to open a PR)
The text was updated successfully, but these errors were encountered: