Open ibraheemdev opened 2 years ago
Here's another trace of the execution: (gist)
t2 SWAP LOCK 0 to 2 (acqrel)
t2 LOAD DATA = 0 (relaxed)
t2 STORE OBSERVED = 0 (mutex)
t1 STORE DATA = 42 (relaxed)
t1 STORE LOCK = 1 (release)
t3 LOAD LOCK = 1 (relaxed)
t3 LOAD LOCK = 2 (relaxed)
t3 LOAD OBSERVED = 0 (mutex)
t3 LOAD OBSERVED = 0 (mutex)
So it seems like two consecutive relaxed loads on the same thread can return the values out of order.
Changing STORE LOCK
in thread one to a swap makes the issue go away.
I simplified somewhat the test to make sure this was not Mutex-related:
loom::lazy_static! {
static ref LOCK: AtomicU32 = AtomicU32::new(0);
static ref DATA: AtomicU32 = AtomicU32::new(0);
static ref OBSERVED: AtomicU32 = AtomicU32::new(21);
}
let t1 = loom::thread::spawn(|| {
DATA.store(42, Relaxed);
LOCK.store(1, Release);
});
let t2 = loom::thread::spawn(|| {
LOCK.swap(2, AcqRel);
OBSERVED.store(DATA.load(Relaxed), Release);
});
let t3 = loom::thread::spawn(|| {
let lock1 = LOCK.load(Relaxed);
let lock2 = LOCK.load(Relaxed);
let observed = OBSERVED.load(Acquire);
if lock1 == 1 && lock2 == 2 && observed != 21 {
assert_eq!(observed, 42);
}
});
t1.join().unwrap();
t2.join().unwrap();
t3.join().unwrap();
It fails and can be cured in the same way as the original test. As @Darksonn wrote, it looks like this fails for good reasons: some sequential consistency would be required to make sure all 3 threads observe the same modification order.
The above test case fails under loom with the following execution:
If t3 observes
LOCK=1, LOCK=2
, then t2's RMW operation must see t1's store of1
and thusDATA=42
. In the failing execution t2 in fact runs first, yet t3 observes t2's store after t1s, which is incoherent with the modification order ofLOCK
.