Atomicity Violations
-
A bug pattern where multiple operations on shared data need to be atomic to ensure correct functioning of application, but the program performs only individual operations atomically.
-
Common pattern is
lock shared stateobtain information from shared state and store it in local varunlock shared state/* meanwhile, other thread may change the state */(re-) lock shared stateact on previously obtained informationunlock shared state
- Rust’s safety mechanisms do not avoid this
Atomicity Violation
use std::sync::Mutex;
fn main() {
let protected_shared_counter = Mutex::new(0);
std::thread::scope(|s| {
for _i in 0..4 {
let counter = &protected_shared_counter;
s.spawn(move || {
for _i in 0..1000000 {
let mut current_value = 0;
if let Ok(v) = counter.lock() { current_value = *v; }
current_value += 1;
if let Ok(mut v) = counter.lock() { *v = current_value; }
}
});
}
});
let final_value = protected_shared_counter.lock().unwrap();
println!("final_value {}", final_value);
}
Resource Deadlocks
-
Deadlocks occurring when multiple threads attempt to make use of resources
-
Coffman Conditions:
- mutually exclusive access
- sequential acquisition of multiple resources (hold-and-wait)
- inability to preempt access to resource
- circular wait
Deadlock Canonical Example
use std::sync::Mutex;
fn main() {
let account_a = Mutex::new(50000);
let account_b = Mutex::new(50000);
std::thread::scope(|s| {
for (from, to) in vec![(&account_a, &account_b), (&account_b, &account_a)] {
s.spawn(move || {
for _i in 0..3000 {
if let Ok(mut u) = from.lock() {
if let Ok(mut v) = to.lock() { *u -= 1; *v += 1; }
}
}
});
}
});
println!("final amounts {} {}", account_a.lock().unwrap(),
account_b.lock().unwrap());
}