use std::ops::Range;
use std::sync::atomic::Ordering;
use super::{DiskCache, State};
use crate::common::universal_io::simple_disk_cache::local_state::LocalState;
use crate::common::universal_io::simple_disk_cache::{DiskCacheRemote, to_block_range};
use crate::common::universal_io::{OwnedPipeline, UioResult};
pub(crate) struct ReadyRef<'a, R> {
pub remote: &'a R,
pub local: &'a LocalState,
}
impl<R> DiskCache<R>
where
R: DiskCacheRemote,
{
pub(crate) fn state(&self) -> UioResult<ReadyRef<'_, R>> {
if !self.is_ready() {
self.init_state()?;
}
let State::Ready {
remote,
local,
scheduled_reopen: _, } = (unsafe { &*self.state.data_ptr() })
else {
unreachable!("the `ready` flag guarantees the `Ready` variant")
};
Ok(ReadyRef { remote, local })
}
pub(crate) fn is_ready(&self) -> bool {
self.is_ready.load(Ordering::Acquire)
}
pub(super) fn init_state(&self) -> UioResult<()> {
let mut state = self.state.lock();
if self.is_ready() {
return Ok(());
}
let (remote, local) = match std::mem::replace(&mut *state, State::Uninit) {
State::Uninit => self.init_from_scratch(self.open_remote()?)?,
State::OpenPrefill { pipeline } => self.init_from_open_prefill(pipeline)?,
State::PartialPrefill { pipeline, len } => {
self.init_from_partial_prefill(pipeline, len)?
}
State::Ready { .. } => {
unreachable!("We just observed `!ready` while holding the mutex lock")
}
};
*state = State::ready(remote, local);
self.is_ready.store(true, Ordering::Release);
Ok(())
}
fn init_from_scratch(&self, remote: R) -> UioResult<(R, LocalState)> {
let len = remote.len::<u8>()?;
let local = LocalState::new(&self.local_path, len, self.open_options)?;
Ok((remote, local))
}
pub(super) fn init_from_open_prefill(
&self,
mut pipeline: OwnedPipeline<R, ()>,
) -> UioResult<(R, LocalState)> {
match pipeline.wait()? {
Some((_, bytes)) => {
let local =
LocalState::new(&self.local_path, bytes.len() as u64, self.open_options)?;
let blocks_range = to_block_range(0..bytes.len() as u64);
unsafe { local.write_mmap_bytes(&bytes, blocks_range) };
Ok((pipeline.into_inner(), local))
}
None => self.init_from_scratch(pipeline.into_inner()),
}
}
pub(super) fn init_from_partial_prefill(
&self,
mut pipeline: OwnedPipeline<R, Range<u32>>,
len: u64,
) -> UioResult<(R, LocalState)> {
let local = LocalState::new(&self.local_path, len, self.open_options)?;
match pipeline.wait()? {
Some((blocks_range, bytes)) if !bytes.is_empty() => {
unsafe { local.write_mmap_bytes(&bytes, blocks_range) };
}
Some(_) | None => {}
}
Ok((pipeline.into_inner(), local))
}
pub(super) fn prefill_if_uninit(&self) -> UioResult<()> {
if self.is_ready() {
return Ok(());
}
let mut state = self.state.lock();
if state.is_uninit() {
let mut pipeline = OwnedPipeline::new(self.open_remote()?)?;
pipeline.schedule_whole((), 0)?;
*state = State::OpenPrefill { pipeline };
}
Ok(())
}
}