use parking_lot::Mutex;
use std::collections::HashSet;
use std::mem;
use {rand, hazard, mpsc};
use garbage::Garbage;
lazy_static! {
static ref STATE: State = State::new();
}
pub fn create_hazard() -> hazard::Writer {
let (write, read) = hazard::create();
STATE.chan.send(Message::NewHazard(read));
write
}
pub fn export_garbage(garbage: Vec<Garbage>) {
STATE.chan.send(Message::Garbage(garbage));
tick();
}
pub fn gc() {
STATE.try_gc();
}
pub fn tick() {
const GC_PROBABILITY: usize = (!0) / 64;
if rand::random::<usize>() <= GC_PROBABILITY {
gc();
}
}
enum Message {
Garbage(Vec<Garbage>),
NewHazard(hazard::Reader),
}
struct State {
chan: mpsc::Sender<Message>,
garbo: Mutex<Garbo>,
}
impl State {
fn new() -> State {
let (send, recv) = mpsc::channel();
State {
chan: send,
garbo: Mutex::new(Garbo {
chan: recv,
garbage: Vec::new(),
hazards: Vec::new(),
})
}
}
fn try_gc(&self) {
if let Some(mut garbo) = self.garbo.try_lock() {
garbo.handle_all();
garbo.gc();
}
}
}
struct Garbo {
chan: mpsc::Receiver<Message>,
garbage: Vec<Garbage>,
hazards: Vec<hazard::Reader>,
}
impl Garbo {
fn handle(&mut self, msg: Message) {
match msg {
Message::Garbage(mut garbage) => self.garbage.append(&mut garbage),
Message::NewHazard(hazard) => self.hazards.push(hazard),
}
}
fn handle_all(&mut self) {
for msg in self.chan.recv_all() {
self.handle(msg);
}
}
fn gc(&mut self) {
let mut active = HashSet::with_capacity(self.hazards.len());
let len = self.hazards.len(); for hazard in mem::replace(&mut self.hazards, Vec::with_capacity(len)) {
match hazard.get() {
hazard::State::Dead => unsafe { hazard.destroy() },
hazard::State::Free => self.hazards.push(hazard),
hazard::State::Protect(ptr) => {
active.insert(ptr);
self.hazards.push(hazard);
},
}
}
for garbage in mem::replace(&mut self.garbage, Vec::new()) {
if active.contains(&garbage.ptr()) {
self.garbage.push(garbage);
} else {
garbage.destroy();
}
}
}
}