use crate::runtime::value::Value;
use crate::vm::exec::Vm;
#[derive(Copy, Clone, Debug)]
pub(crate) struct HostRootSlot {
pub(crate) value: Value,
pub(crate) generation: u32,
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct HostRootTicket {
pub(crate) idx: u32,
pub(crate) generation: u32,
}
impl HostRootTicket {
pub fn idx(self) -> u32 {
self.idx
}
pub fn generation(self) -> u32 {
self.generation
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct HostRootStale;
impl std::fmt::Display for HostRootStale {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("host root ticket is stale (slot was unpinned and possibly re-pinned)")
}
}
impl std::error::Error for HostRootStale {}
impl Vm {
pub fn pin_host(&mut self, v: Value) -> HostRootTicket {
if let Some(idx) = self.host_roots_free.pop() {
let slot = &mut self.host_roots[idx as usize];
slot.generation = slot.generation.saturating_add(1);
slot.value = v;
HostRootTicket {
idx,
generation: slot.generation,
}
} else {
let idx = self.host_roots.len() as u32;
self.host_roots.push(HostRootSlot {
value: v,
generation: 0,
});
HostRootTicket { idx, generation: 0 }
}
}
pub fn read_host(&self, t: HostRootTicket) -> Option<Value> {
let slot = self.host_roots.get(t.idx as usize)?;
if slot.generation == t.generation {
Some(slot.value)
} else {
None
}
}
pub fn write_host(&mut self, t: HostRootTicket, v: Value) -> Result<(), HostRootStale> {
let slot = self
.host_roots
.get_mut(t.idx as usize)
.ok_or(HostRootStale)?;
if slot.generation == t.generation {
slot.value = v;
Ok(())
} else {
Err(HostRootStale)
}
}
pub fn unpin(&mut self, t: HostRootTicket) -> Result<(), HostRootStale> {
let slot = self
.host_roots
.get_mut(t.idx as usize)
.ok_or(HostRootStale)?;
if slot.generation != t.generation {
return Err(HostRootStale);
}
slot.value = Value::Nil;
if slot.generation == u32::MAX {
return Ok(());
}
slot.generation += 1;
self.host_roots_free.push(t.idx);
Ok(())
}
pub fn host_root_count(&self) -> usize {
self.host_roots.len() - self.host_roots_free.len()
}
pub fn unpin_all(&mut self) {
self.host_roots_free.clear();
for (i, slot) in self.host_roots.iter_mut().enumerate() {
slot.value = Value::Nil;
if slot.generation == u32::MAX {
continue;
}
slot.generation += 1;
self.host_roots_free.push(i as u32);
}
}
}