|
|
|
use super::*;
|
|
|
|
use std::fs::{
|
|
|
|
File, Metadata, OpenOptions,
|
|
|
|
};
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
use std::path::Path;
|
|
|
|
use std::io::{
|
|
|
|
self,
|
|
|
|
Read,
|
|
|
|
};
|
|
|
|
use std::{fmt,error};
|
|
|
|
|
|
|
|
/// A job's completion status
|
|
|
|
///
|
|
|
|
/// If the job has not completed, it will be the default value `Pending`.
|
|
|
|
/// Once the job completes, the result of the job can be inserted to set to `Complete`.
|
|
|
|
/// Afterwards, you can extract the value and it will have the state `Taken`; which means "job has completed, but the value has been consumed".
|
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
/// This is not coupled to anything, it's the user's responsibility to update this status when a job completes.
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub enum Status<T>
|
|
|
|
{
|
|
|
|
/// For running operations
|
|
|
|
Pending,
|
|
|
|
/// An operation that has completed with its value
|
|
|
|
Complete(T),
|
|
|
|
|
|
|
|
/// An operation who's completed value has been extracted and processed
|
|
|
|
Taken,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Status<T>
|
|
|
|
{
|
|
|
|
/// Assign a completed value to this instance.
|
|
|
|
///
|
|
|
|
/// If there was one already set, it is ignored.
|
|
|
|
#[inline] pub fn complete(&mut self, value: T)
|
|
|
|
{
|
|
|
|
*self = Self::Complete(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Has the value not yet been assigned or taken?
|
|
|
|
pub fn is_pending(&self) -> bool
|
|
|
|
{
|
|
|
|
match self {
|
|
|
|
Self::Pending => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Take the completed value from this instance
|
|
|
|
pub fn take(&mut self) -> Option<T>
|
|
|
|
{
|
|
|
|
match std::mem::replace(self, Self::Taken) {
|
|
|
|
Self::Complete(t) => Some(t),
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a mutable reference to the completed value if there is one.
|
|
|
|
pub fn peek_mut(&mut self) -> Option<&mut T>
|
|
|
|
{
|
|
|
|
match self {
|
|
|
|
Self::Complete(ref mut t) => Some(t),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Get a reference to the completed value if there is one.
|
|
|
|
pub fn peek(&self) -> Option<&T>
|
|
|
|
{
|
|
|
|
match self {
|
|
|
|
Self::Complete(ref t) => Some(t),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Default for Status<T>
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
fn default() -> Self
|
|
|
|
{
|
|
|
|
Self::Pending
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Prelude
|
|
|
|
{
|
|
|
|
file: File,
|
|
|
|
stat: Metadata,
|
|
|
|
|
|
|
|
file_num: usize,
|
|
|
|
offset: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Prelude{
|
|
|
|
pub fn len(&self) -> usize
|
|
|
|
{
|
|
|
|
usize::try_from(self.stat.len()).expect("Failed to fit file size into pointer size")
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert this job prelude into a job, assigning it this state
|
|
|
|
pub fn start(self, state: state::State) -> Job
|
|
|
|
{
|
|
|
|
Job {
|
|
|
|
fd: self.file,
|
|
|
|
stat: self.stat,
|
|
|
|
offset: self.offset,
|
|
|
|
file_num: self.file_num,
|
|
|
|
state,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Job
|
|
|
|
{
|
|
|
|
fd: File,
|
|
|
|
stat: Metadata,
|
|
|
|
|
|
|
|
file_num: usize,
|
|
|
|
/// We grab the slice of memory we write to from here
|
|
|
|
state: state::State,
|
|
|
|
/// From this offset
|
|
|
|
offset: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Job
|
|
|
|
{
|
|
|
|
pub fn state(&self) -> &state::State
|
|
|
|
{
|
|
|
|
&self.state
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn info(&self) -> (&File, &Metadata)
|
|
|
|
{
|
|
|
|
(&self.fd, &self.stat)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn len(&self) -> usize
|
|
|
|
{
|
|
|
|
usize::try_from(self.stat.len()).expect("Failed to fit file size into pointer size")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn start(&self) -> usize
|
|
|
|
{
|
|
|
|
self.offset
|
|
|
|
}
|
|
|
|
pub fn end(&self) -> usize
|
|
|
|
{
|
|
|
|
self.len() + self.offset
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the output slice for this job.
|
|
|
|
pub fn output_slice<'a, 'b>(&'a mut self) -> &'b mut [u8]
|
|
|
|
where 'a: 'b
|
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
self.state.slice(self.start() .. self.end())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Complete this job
|
|
|
|
pub fn complete(self, size: usize) -> Result<(), CompletionError>
|
|
|
|
{
|
|
|
|
self.state.send_complete(self.file_num, size).map_err(|_| CompletionError)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Read for Job
|
|
|
|
{
|
|
|
|
#[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>
|
|
|
|
{
|
|
|
|
self.fd.read(buf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a job description for this opened input
|
|
|
|
///
|
|
|
|
/// `sz` is the offset of the *end* of the last job. (or 0 for the first).
|
|
|
|
/// `sz` is then updated with this file's size for this method to be used again on the next file.
|
|
|
|
pub fn create(file_num: usize, open::Input(file, stat): open::Input, sz: &mut usize) -> Prelude
|
|
|
|
{
|
|
|
|
let offset = *sz;
|
|
|
|
|
|
|
|
let prelude = Prelude {
|
|
|
|
file, stat, offset, file_num
|
|
|
|
};
|
|
|
|
*sz += prelude.len();
|
|
|
|
|
|
|
|
prelude
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a job description for this file.
|
|
|
|
///
|
|
|
|
/// `sz` is the offset of the *end* of the last job. (or 0 for the first).
|
|
|
|
/// `sz` is then updated with this file's size for this method to be used again on the next file.
|
|
|
|
pub fn create_from_file(file_num: usize, file: impl AsRef<Path>, sz: &mut usize) -> io::Result<Prelude>
|
|
|
|
{
|
|
|
|
let file = OpenOptions::new()
|
|
|
|
.read(true)
|
|
|
|
.open(file.as_ref())?;
|
|
|
|
|
|
|
|
let stat = file.metadata()?;
|
|
|
|
let offset = *sz;
|
|
|
|
|
|
|
|
let prelude = Prelude {
|
|
|
|
file, stat, offset, file_num
|
|
|
|
};
|
|
|
|
*sz += prelude.len();
|
|
|
|
|
|
|
|
Ok(prelude)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Error returned when main thread's completion receiver was dropped. This should be fatal.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct CompletionError;
|
|
|
|
|
|
|
|
impl error::Error for CompletionError{}
|
|
|
|
impl fmt::Display for CompletionError
|
|
|
|
{
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
|
|
{
|
|
|
|
write!(f, "unable to send completion signal because main thread's completion receiver was dropped.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|