// 467. Epoch-based garbage collection concept
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use std::collections::VecDeque;
use std::thread;
use std::time::Duration;
struct EpochMgr {
epoch: AtomicU64,
retired: Mutex<VecDeque<(u64, String)>>,
pinned: Mutex<Vec<u64>>,
}
impl EpochMgr {
fn new() -> Self {
EpochMgr { epoch: AtomicU64::new(0), retired: Mutex::new(VecDeque::new()), pinned: Mutex::new(Vec::new()) }
}
fn pin(&self) -> u64 {
let e = self.epoch.load(Ordering::Acquire);
self.pinned.lock().unwrap().push(e); e
}
fn unpin(&self, e: u64) {
let mut p = self.pinned.lock().unwrap();
if let Some(i) = p.iter().position(|&x| x==e) { p.remove(i); }
}
fn retire(&self, desc: &str) {
let e = self.epoch.load(Ordering::Relaxed);
self.retired.lock().unwrap().push_back((e, desc.to_string()));
}
fn collect(&self) {
let new_e = self.epoch.fetch_add(1, Ordering::AcqRel) + 1;
let min_active = self.pinned.lock().unwrap().iter().cloned().min().unwrap_or(new_e);
let safe_before = min_active.saturating_sub(1);
let mut r = self.retired.lock().unwrap(); let mut n=0;
while r.front().map(|(e,_)| *e<=safe_before).unwrap_or(false) {
let (_,d)=r.pop_front().unwrap(); println!(" freed: {}", d); n+=1;
}
println!("epochβ{}; freed {}; deferred {}", new_e, n, r.len());
}
}
fn main() {
let m = Arc::new(EpochMgr::new());
let rs: Vec<_> = (0..3).map(|id|{let m=Arc::clone(&m); thread::spawn(move || {
let e=m.pin(); println!("reader {} pinned epoch {}", id, e);
thread::sleep(Duration::from_millis(10*(id as u64+1)));
m.unpin(e); println!("reader {} unpinned", id);
})}).collect();
m.retire("old-A"); m.retire("old-B");
thread::sleep(Duration::from_millis(5));
println!("first collect:"); m.collect();
for r in rs { r.join().unwrap(); }
println!("second collect:"); m.collect();
}
#[cfg(test)]
mod tests {
use super::*;
#[test] fn test_epoch_advances() { let m=EpochMgr::new(); m.collect(); assert_eq!(m.epoch.load(Ordering::SeqCst),1); }
#[test] fn test_retire() { let m=EpochMgr::new(); m.retire("x"); assert_eq!(m.retired.lock().unwrap().len(),1); }
}
(* 467. Epoch GC concept β OCaml *)
(* OCaml has a GC; this demonstrates the concept of deferred reclamation *)
type 'a retired = { value: 'a; epoch: int }
let global_epoch = ref 0
let retired_list : 'a retired list ref = ref []
let mutex = Mutex.create ()
let retire v =
Mutex.lock mutex;
let e = !global_epoch in
retired_list := { value=v; epoch=e } :: !retired_list;
Mutex.unlock mutex
let collect min_safe_epoch =
Mutex.lock mutex;
let (safe, keep) = List.partition (fun r -> r.epoch < min_safe_epoch) !retired_list in
retired_list := keep;
Mutex.unlock mutex;
Printf.printf "reclaiming %d objects\n" (List.length safe)
let advance () =
Mutex.lock mutex; incr global_epoch; Mutex.unlock mutex
let () =
retire "node-A"; retire "node-B"; retire "node-C";
Printf.printf "retired 3, epoch=%d\n" !global_epoch;
advance ();
Printf.printf "advanced to epoch=%d\n" !global_epoch;
collect 1; (* safe epoch: reclaim anything from epoch < 1 *)
Printf.printf "remaining: %d\n" (List.length !retired_list)