use std::sync::atomic::{AtomicI32,Ordering};
static ATOM:AtomicI32 = AtomicI32::new(0);
async fn fun1(i:i32){
blocking::unblock(move ||{
std::thread::sleep(std::time::Duration::from_millis(120));
ATOM.store(1, Ordering::Relaxed);
println!("ok {i}");
}).await;
}
#[tokio::main]
async fn main() {
for i in 0..20{
tokio::select! {
_=tokio::time::sleep(std::time::Duration::from_millis(110))=>{
let r = ATOM.load(Ordering::Relaxed);
//std::thread::sleep(std::time::Duration::from_millis(20));
println!("sleep {i}, detect:{r}");
}
_=fun1(i)=>{
println!("fun1");
}
}
println!("----------------------");
ATOM.store(0, Ordering::Relaxed);
//tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}
}
Consider the following example: Is this result possible?
ok
sleep, detect:0
That is, in the thread pool, the println!("ok")
is executed, however, the branch at #1
wins the race, and sleep
is printed, but the result of the load is 0
? Is this a theoretically possible result
Yes, it's possible, and it doesn’t even depend on the details of atomics at all. Your program’s threads may be executed in this interleaved order (among many other possible orderings):
Thread |
Event |
Main |
timer expires |
Blocking |
timer expires |
Main |
let r = ATOM.load() loads 0 |
Blocking |
ATOM.store(1, Ordering::Relaxed) |
Blocking |
println!("ok") |
Main |
println!("sleep, detect:{r}") prints previously read 0 |
Blocking |
finishes closure and wakes fun1 |
If you wanted to ensure that only one of the two things (ok
or #1
) happens, then you need to arrange so that the logic of #1
also writes a value to the atomic, and both sides use a compare_exchange()
so they can notice if the other side wrote first. That way, both sides can come to agreement on “who wins the race”.
Maybe, I meant, the order could be
Blocking ATOM.store(1, Ordering::Relaxed)
Blocking println!("ok")
Main let r = ATOM.load() loads 0
Main println!("sleep, detect:{r}") prints previously read 0
The inter-thread latency such that the result of the load in this order is 0
.
Given the granularity of timers and the operating system schedule and et cetera it's also possible for the Main and Blocking timers to expire in the other order.
This is also possible, I think, but it doesn't matter because your program doesn't ensure other orderings don't exist that produce 0. Appropriate use of atomics will fix both simultaneously.
Yes, and that doesn't change the results.
1 Like
Post a test result that can prove this is true.
PS: The result is difficult to reproduce.
Also mandatory link with explanation that if you are not using barriers even “impossible” results are, in fact, possible.