Skip to content

Instantly share code, notes, and snippets.

@mbillingr
Created July 29, 2024 20:29
Show Gist options
  • Save mbillingr/5db02e1e302cb10ace8849b65e6dfd25 to your computer and use it in GitHub Desktop.
Save mbillingr/5db02e1e302cb10ace8849b65e6dfd25 to your computer and use it in GitHub Desktop.
An Atom as in data-oriented programming. Unfortunately, it only works with static references.
use std::sync::atomic::{AtomicPtr, Ordering};
use std::time::Duration;
fn main() {
let counter = Box::leak(Box::new(Atom::new(0)));
for _ in 0..100 {
std::thread::spawn(|| {
counter.swap(inc);
});
}
println!("x {}", counter.swap(inc));
println!("x {}", counter.swap(inc));
let y = counter.get();
println!("y {}", y);
println!("x {}", counter.swap(inc));
println!("x {}", counter.swap(inc));
std::thread::sleep(Duration::from_secs(1));
println!("y {}", y);
println!("x {}", counter.swap(inc));
println!("x {}", counter.swap(inc));
println!("x {}", counter.swap(inc));
println!("x {}", counter.swap(inc));
println!("y {}", y);
// The resulting x should be 107 (old value of the 108th increase)
// y could be anything.
}
fn inc(x: &i64) -> &'static i64 {
Box::leak(Box::new(x + 1))
}
struct Atom<T> {
state: AtomicPtr<T>,
}
impl<T> Atom<T> {
pub fn new(state: T) -> Self {
Atom {
state: AtomicPtr::new(Box::leak(Box::new(state))),
}
}
pub fn get(&self) -> &T {
let state_ptr = self.state.load(Ordering::SeqCst);
unsafe { &mut *state_ptr }
}
pub fn swap(&self, f: impl Fn(&T) -> &'static T) -> &'static T {
loop {
let state_snapshot_ptr = self.state.load(Ordering::SeqCst);
let state_snapshot_ref = unsafe { &*state_snapshot_ptr };
let next_state = f(state_snapshot_ref) as *const T as *mut T;
match self.state.compare_exchange(
state_snapshot_ptr,
next_state,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(old_state) => return unsafe { &*old_state },
Err(_) => continue,
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment