tempest-rt 0.0.1

TempestDB Deterministic Async Runtime
Documentation
//! Async file metadata operation.

use std::{
    io,
    pin::Pin,
    task::{Context, Poll},
};

use tempest_io::{FstatHandle, Io, OpHandle};

use crate::context::{current_io, get_op_handle};

enum StatFileState<I: Io> {
    NeedsSubmit {
        fd: I::Fd,
        op_handle: Option<OpHandle>,
    },
    InFlight {
        fstat_handle: FstatHandle<I::Statx>,
        op_handle: OpHandle,
    },
    Done,
}

/// Future that retrieves file metadata, resolving to [`Io::Statx`].
#[must_use = "futures do nothing unless awaited"]
pub struct StatFile<I: Io> {
    state: StatFileState<I>,
}

/// Retrieves metadata for `fd`, resolving to the platform-specific statx structure.
pub fn stat_file<I: Io>(fd: I::Fd) -> StatFile<I> {
    StatFile {
        state: StatFileState::NeedsSubmit {
            fd,
            op_handle: None,
        },
    }
}

// NB: required because of Fd, which is Copy
impl<I: Io> Unpin for StatFile<I> {}

impl<I: Io> Future for StatFile<I> {
    type Output = io::Result<I::Statx>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.get_mut();

        match std::mem::replace(&mut this.state, StatFileState::Done) {
            StatFileState::NeedsSubmit { fd, op_handle } => {
                let op_handle = op_handle.unwrap_or_else(|| unsafe { get_op_handle(cx) });

                // SAFETY: we do not hold on to io outside of this function
                let io = unsafe { current_io::<I>() };
                match io.fstat(fd, op_handle) {
                    Ok(fstat_handle) => {
                        this.state = StatFileState::InFlight {
                            fstat_handle,
                            op_handle,
                        };
                        Poll::Pending
                    }
                    Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
                        this.state = StatFileState::NeedsSubmit {
                            fd,
                            op_handle: Some(op_handle),
                        };
                        cx.waker().wake_by_ref();
                        Poll::Pending
                    }
                    Err(e) => Poll::Ready(Err(e)),
                }
            }
            StatFileState::InFlight {
                fstat_handle,
                op_handle,
            } => {
                // SAFETY: we do not hold on to io outside of this function
                let io = unsafe { current_io::<I>() };
                match io.get_cqe(op_handle).transpose()? {
                    Some(_) => Poll::Ready(Ok(fstat_handle.complete())),
                    None => {
                        this.state = StatFileState::InFlight {
                            fstat_handle,
                            op_handle,
                        };
                        Poll::Pending
                    }
                }
            }
            StatFileState::Done => panic!("polled after completion"),
        }
    }
}