Skip to content

Instantly share code, notes, and snippets.

@mbillingr
Created July 11, 2025 07:38
Show Gist options
  • Select an option

  • Save mbillingr/6cf44401542265e113dd19c343df9800 to your computer and use it in GitHub Desktop.

Select an option

Save mbillingr/6cf44401542265e113dd19c343df9800 to your computer and use it in GitHub Desktop.
An implementation of atoms using locks to guard the inner value.
use std::sync::{Arc, RwLock};
fn main() {
let state = Atom::new(0i64);
for _ in 0..100 {
state.update::<()>(|x| Ok(x + 1)).unwrap();
}
println!("{}", state.get());
let state = Atom::new(0i64);
let mut threads = vec![];
for _ in 0..100 {
let state = state.clone();
let t = std::thread::spawn(move || {
state.update::<()>(|x| Ok(x + 1)).unwrap();
});
threads.push(t);
}
for t in threads {
t.join().unwrap();
}
println!("{}", state.get());
}
struct Atom<T> {
data: Arc<RwLock<T>>,
}
impl<T> Clone for Atom<T> {
fn clone(&self) -> Self {
Atom {
data: self.data.clone(),
}
}
}
impl<T: Clone> Atom<T> {
pub fn new(value: T) -> Self {
Atom {
data: Arc::new(RwLock::new(value)),
}
}
pub fn get(&self) -> T {
self.data.read().unwrap().clone()
}
pub fn set(&self, value: T) -> T {
let mut lock = self.data.write().unwrap();
std::mem::replace(&mut *lock, value)
}
}
impl<T: Clone + PartialEq> Atom<T> {
pub fn compare_and_set(&self, expected_old: &T, new: T) -> Result<T, T> {
let mut lock = self.data.write().unwrap();
if &*lock == expected_old {
Ok(std::mem::replace(&mut *lock, new))
} else {
Err(new)
}
}
pub fn update<E>(&self, mut mutator: impl FnMut(&T) -> Result<T, E>) -> Result<T, E> {
let mut fork_point = self.get();
loop {
// Note that we don't hold a lock while running the mutator.
let new_value = mutator(&fork_point)?;
let mut lock = self.data.write().unwrap();
if *lock == fork_point {
*lock = new_value.clone();
return Ok(new_value);
} else {
fork_point = lock.clone();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment