I'm using the crossbeam crate's AtomicCellfor a parameter which can range from 1-8 — but should never be 0 or 9. If I do a load, check, and then set, it's not Atomic — there's a small chance the value was changed inside my check. So I ended up with this:
…. which is a lot of compares and would get out of hand if the range were larger. Is there a better way to do this? Or should I use a Mutex or RwLock for this instead?
The typical solution to this would be to compare_and_swap in a loop.
fn incr_max(i: &AtomicU32, max: u32) -> u32 {
loop {
let value = i.load(Ordering::Relaxed);
if value == max { return value; }
if i.compare_and_swap(value, value + 1, Ordering::Relaxed) == value {
return value;
}
}
}
Also note, since the original code didn't check the return value of compare_and_swap, it can fail if two threads try to increment the value:
// Supposed the value is 6 when the code begins running.
self.zoom.compare_and_swap(7,8); // This compare fails.
// Now another thread increments the value to 7
self.zoom.compare_and_swap(6,7); // so this compare fails...
self.zoom.compare_and_swap(5,6); // ...and so do all the others...
self.zoom.compare_and_swap(4,5);
self.zoom.compare_and_swap(3,4);
self.zoom.compare_and_swap(2,3);
self.zoom.compare_and_swap(1,2);
// ...so the value is still 7 here.
The value started at 6 and ended at 7, even though the "increment" operation ran twice. Fixing this would again require a loop, to try the whole operation from the beginning if none of its parts succeeded.