use std::sync::Arc;
use crate::persistent_artrie_core::concurrency::EpochManager;
use crate::persistent_artrie_core::eviction::EvictionCoordinator;
use crate::persistent_artrie_core::key_encoding::KeyEncoding;
use crate::persistent_artrie_core::overlay::atomic_ptr::AtomicNodePtr;
use crate::persistent_artrie_core::overlay::faulter::OverlayFaulter;
use crate::persistent_artrie_core::overlay::node::{Child, OverlayNode};
use crate::persistent_artrie_core::swizzled_ptr::SwizzledPtr;
use crate::value::DictionaryValue;
pub(crate) const DEFAULT_MAX_FAULTIN_RETRIES: usize = 16;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum OverlayEvictOutcome {
Evicted,
RootCasLost,
NotEvictable,
}
pub(crate) trait OverlayEvictable<K: KeyEncoding, V: DictionaryValue, S>:
OverlayFaulter<K, V>
{
#[allow(dead_code)]
fn overlay_root_slot(&self) -> Option<&AtomicNodePtr<K, V>>;
fn overlay_epoch_manager(&self) -> &EpochManager;
#[allow(dead_code)]
fn overlay_eviction_coordinator(&self) -> Option<Arc<EvictionCoordinator>>;
#[inline]
fn note_faultin_cas(&self) {}
#[allow(dead_code)]
fn evict_overlay_node_at_path(
&self,
path: &[K::Unit],
disk_ptr: SwizzledPtr,
) -> OverlayEvictOutcome {
if path.is_empty() {
return OverlayEvictOutcome::NotEvictable;
}
if disk_ptr.disk_location().is_none() {
return OverlayEvictOutcome::NotEvictable;
}
let root_slot = match self.overlay_root_slot() {
Some(r) => r,
None => return OverlayEvictOutcome::NotEvictable,
};
let _epoch = self.overlay_epoch_manager().enter_read();
let old_root = match root_slot.load() {
Some(r) => r,
None => return OverlayEvictOutcome::NotEvictable,
};
let mut spine: Vec<(Arc<OverlayNode<K, V>>, K::Unit)> = Vec::with_capacity(path.len());
let mut current = Arc::clone(&old_root);
for &edge in path {
let child = match current.find_child(edge) {
Some(c) => c,
None => return OverlayEvictOutcome::NotEvictable, };
let child_arc = match child.as_in_mem() {
Some(a) => Arc::clone(a),
None => return OverlayEvictOutcome::NotEvictable,
};
spine.push((Arc::clone(¤t), edge));
current = child_arc;
}
if current.durable_stamp() != disk_ptr.to_raw() {
return OverlayEvictOutcome::NotEvictable;
}
let mut new_child: Option<Arc<OverlayNode<K, V>>> = None;
for (ancestor, edge) in spine.into_iter().rev() {
let rebuilt = match new_child.take() {
Some(c) => ancestor.with_child(edge, Child::InMem(c)),
None => ancestor.with_child(edge, Child::OnDisk(disk_ptr.clone())),
};
new_child = Some(Arc::new(rebuilt));
}
let new_root = match new_child {
Some(r) => r,
None => return OverlayEvictOutcome::NotEvictable,
};
match root_slot.compare_exchange(&old_root, new_root) {
Ok(_) => OverlayEvictOutcome::Evicted,
Err(_actual) => OverlayEvictOutcome::RootCasLost,
}
}
fn find_leaf_faulting(
&self,
root_slot: &AtomicNodePtr<K, V>,
key: &[K::Unit],
max_faultin_retries: usize,
) -> crate::persistent_artrie_core::error::Result<Option<Arc<OverlayNode<K, V>>>> {
fn walk_no_fault<K: KeyEncoding, V: DictionaryValue>(
root: &Arc<OverlayNode<K, V>>,
key: &[K::Unit],
) -> Option<Arc<OverlayNode<K, V>>> {
let mut current = Arc::clone(root);
for &edge in key {
let child = current.find_child(edge)?;
let child_arc = child.as_in_mem()?;
let next = Arc::clone(child_arc);
current = next;
}
if current.is_final() {
Some(current)
} else {
None
}
}
for _attempt in 0..=max_faultin_retries {
let _epoch = self.overlay_epoch_manager().enter_read();
let old_root = match root_slot.load() {
Some(r) => r,
None => return Ok(None), };
let mut spine: Vec<(Arc<OverlayNode<K, V>>, K::Unit)> = Vec::with_capacity(key.len());
let mut current = Arc::clone(&old_root);
let mut faulted = false;
let mut idx = 0usize;
while idx < key.len() {
let edge = key[idx];
let child = match current.find_child(edge) {
Some(c) => c,
None => return Ok(None), };
match child {
Child::InMem(child_arc) => {
let next = Arc::clone(child_arc);
spine.push((Arc::clone(¤t), edge));
current = next;
idx += 1;
}
Child::OnDisk(ptr) if !ptr.is_null() => {
let loaded = match self.fault_overlay_slot(ptr) {
Some(node) => node,
None => return Ok(None),
};
let mut new_child =
Arc::new(current.with_child(edge, Child::InMem(loaded)));
for (ancestor, anc_edge) in spine.iter().rev() {
new_child =
Arc::new(ancestor.with_child(*anc_edge, Child::InMem(new_child)));
}
let _ = root_slot.compare_exchange(&old_root, new_child);
self.note_faultin_cas();
faulted = true;
break;
}
Child::OnDisk(_) => return Ok(None),
}
}
if faulted {
continue;
}
return Ok(if current.is_final() {
Some(current)
} else {
None
});
}
let final_root = match root_slot.load() {
Some(r) => r,
None => return Ok(None),
};
Ok(walk_no_fault(&final_root, key))
}
}