Use RefCell and update the content in a mutex lock guard.
The content is wrapped in Arc so that readers can access concurrently.
The RefCell is wrapped in Mutex so the update can happen in a writer thread concurrently to active readers.
Arc makes sure the old resource is released after all old readers are done.
(Updated to use borrow() instead of get_mut() as suggested by Mihnea Stefan Popeanga.)
Code:
use std::thread;
use std::sync::{Arc, Mutex};
use std::cell::RefCell;
use std::time;
struct Rcu<T> {
data: Mutex<RefCell<Arc<T>>>,
}
impl<T> Rcu<T> {
fn new(t: T) -> Self {
let cell = RefCell::new(Arc::new(t));
let data = Mutex::new(cell);
Self {
data,
}
}
fn write(&self, t: T) {
let cell = self.data.lock().unwrap();
cell.replace(Arc::new(t));
}
fn read(&self) -> Arc<T> {
let cell = self.data.lock().unwrap();
let r = cell.borrow().clone();
r
}
}
fn main() {
struct MyStruct {
generation: i32,
}
let data = Arc::new(Rcu::new(MyStruct {
generation: 0,
}));
for i in 1..5 {
let d = data.clone();
thread::spawn(move || {
loop {
let ms = d.read();
println!("read loop {}, generation: {}", i, ms.generation);
thread::sleep(time::Duration::from_millis(100));
println!("read loop {} doing some complex compuation on d...", i);
thread::sleep(time::Duration::from_millis(100));
println!("read loop {} done on generation {}...", i, ms.generation);
}
});
}
let d = data.clone();
loop {
println!("write loop");
let ng = d.read().generation + 1;
d.write(MyStruct {
generation: ng,
});
thread::sleep(time::Duration::from_millis(1000));
}
}
Test output:
