use std::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering};
pub type FrameId = usize;
pub trait EvictionPolicy: Send + Sync {
fn access(&self, frame_id: FrameId);
fn evict(&self) -> Option<FrameId>;
fn remove(&self, frame_id: FrameId);
}
pub struct ClockProPolicy {
hand: AtomicUsize,
capacity: usize,
state: Vec<AtomicU8>,
}
const REF_BIT: u8 = 0b01;
const HOT_BIT: u8 = 0b10;
impl ClockProPolicy {
#[must_use]
pub fn new(capacity: usize) -> Self {
let mut state = Vec::with_capacity(capacity);
for _ in 0..capacity {
state.push(AtomicU8::new(0));
}
Self {
hand: AtomicUsize::new(0),
capacity,
state,
}
}
}
impl EvictionPolicy for ClockProPolicy {
fn access(&self, frame_id: FrameId) {
if frame_id >= self.capacity {
return;
}
let old = self.state[frame_id].load(Ordering::Relaxed);
if (old & HOT_BIT) != 0 {
self.state[frame_id].fetch_or(REF_BIT, Ordering::Relaxed);
} else if (old & REF_BIT) != 0 {
self.state[frame_id].store(HOT_BIT | REF_BIT, Ordering::Relaxed);
} else {
self.state[frame_id].fetch_or(REF_BIT, Ordering::Relaxed);
}
}
fn evict(&self) -> Option<FrameId> {
let max_iterations = self.capacity * 3;
let mut iterations = 0;
while iterations < max_iterations {
let current = self.hand.fetch_add(1, Ordering::Relaxed) % self.capacity;
iterations += 1;
let state = self.state[current].load(Ordering::Relaxed);
if (state & HOT_BIT) != 0 {
if (state & REF_BIT) != 0 {
self.state[current].fetch_and(!REF_BIT, Ordering::Relaxed);
} else {
self.state[current].fetch_and(!HOT_BIT, Ordering::Relaxed);
}
} else {
if (state & REF_BIT) != 0 {
self.state[current].fetch_and(!REF_BIT, Ordering::Relaxed);
} else {
return Some(current);
}
}
}
None }
fn remove(&self, frame_id: FrameId) {
if frame_id < self.capacity {
self.state[frame_id].store(0, Ordering::Relaxed);
}
}
}
pub struct ClockPolicy {
hand: AtomicUsize,
capacity: usize,
reference_bits: Vec<AtomicBool>,
}
impl ClockPolicy {
#[must_use]
pub fn new(capacity: usize) -> Self {
let mut bits = Vec::with_capacity(capacity);
for _ in 0..capacity {
bits.push(AtomicBool::new(false));
}
Self {
hand: AtomicUsize::new(0),
capacity,
reference_bits: bits,
}
}
}
impl EvictionPolicy for ClockPolicy {
fn access(&self, frame_id: FrameId) {
if frame_id < self.capacity {
self.reference_bits[frame_id].store(true, Ordering::Relaxed);
}
}
fn evict(&self) -> Option<FrameId> {
let start_hand = self.hand.load(Ordering::Relaxed);
let mut loops = 0;
loop {
let current_hand = self.hand.fetch_add(1, Ordering::Relaxed) % self.capacity;
if self.reference_bits[current_hand].load(Ordering::Relaxed) {
self.reference_bits[current_hand].store(false, Ordering::Relaxed);
} else {
return Some(current_hand);
}
if current_hand == start_hand {
loops += 1;
if loops >= 2 {
return None;
}
}
}
}
fn remove(&self, frame_id: FrameId) {
if frame_id < self.capacity {
self.reference_bits[frame_id].store(false, Ordering::Relaxed);
}
}
}
pub type LruPolicy = ClockPolicy;