-
Notifications
You must be signed in to change notification settings - Fork 126
Description
The following code fails the assertion and I am not sure, why.
Whenever the mutex is held by the second thread, either the first did its update (a == b == false) or it didn't (a == b == true).
The second thread may have run the compare-exchange (or the swap) after the first thread changed both values, but if either of them is successful, the assertion isn't run. If the function continues, both a and b must still have their old value.
Strangely, this logic appears to hold with the compare-exchange, but not with the swap (which should have the same semantics because these values are just booleans?).
#[cfg(test)]
#[cfg(feature = "loom")]
mod test {
use std::sync::Arc;
use loom::{
thread,
sync::{
Mutex,
atomic::{AtomicBool, Ordering}
}
};
#[test]
fn failing_test() {
pub struct Foo
{
a: Mutex<bool>,
b: AtomicBool,
}
loom::model(|| {
let foo = Arc::new(Foo{
a: Mutex::new(true),
b: AtomicBool::new(true),
});
let thread_1 = {
let foo = Arc::clone(&foo);
thread::spawn(move || {
let maybe_guard = (*foo).a.lock();
let mut a = maybe_guard.unwrap();
foo.b.store(false, Ordering::Relaxed); // Was Release before, but that should not matter.
*a = false;
})
};
let thread_2 = {
let foo = Arc::clone(&foo);
thread::spawn(move || {
// Either those don't have an effect (so the values remain equal),
// or I am exiting the function (not running the assertion):
{
// works:
// if foo.b.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed).is_ok() { return; }
// causes assertion:
if !foo.b.swap(true, Ordering::Relaxed) { return; }
}
{
let maybe_guard = (*foo).a.lock();
let a = maybe_guard.unwrap();
let b = (*foo).b.load(Ordering::Relaxed);
assert!(*a == b);
}
})
};
thread_1.join().unwrap();
thread_2.join().unwrap();
})
}
}Edit: This also happens when we replace the mutex by a typical spinlock and only do try_lock instead of lock. It appears as though the AtomicBool::swap is not actually atomic, but does separate read and write operations, and thereby loses the value written by the first thread.