I decided to go a completely different route and try to use a custom future. The idea being that each HasherFuture launches a thread which hashes the function and reports back once its done.
Using:
sha2 = "0.8.0"
hex = "0.4.0"
I used the example code from the async book and got this:
use {
std::{
future::Future,
sync::{Arc,Mutex},
task::{Context,Poll,Waker},
pin::Pin,
thread,
path::PathBuf
},
sha2::{Sha256, Digest}
};
pub struct HasherFuture {
shared_state: Arc<Mutex<SharedState>>
}
struct SharedState {
waker: Option<Waker>,
hash: Option<String>,
id: String
}
pub struct HashRet {
id: String,
hash: String
}
impl Future for HasherFuture {
type Output = HashRet;
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
let mut shared_state = self.shared_state.lock().unwrap();
if let Some(hash) = &shared_state.hash {
let hr = HashRet { id: shared_state.id.clone(), hash: hash.clone() };
Poll::Ready(hr)
} else {
shared_state.waker = Some(ctx.waker().clone());
Poll::Pending
}
}
}
impl HasherFuture {
pub fn new(id: &str, fname: &PathBuf) -> Self {
let shstate = SharedState { waker: None, hash: None, id: id.to_string() };
let shared_state = Arc::new(Mutex::new(shstate));
let mut file = std::fs::File::open(&fname).expect("Unable to open file");
let mut sha256 = Sha256::new();
let thread_shared_state = shared_state.clone();
thread::spawn(move || {
let _n = std::io::copy(&mut file, &mut sha256);
let hash = sha256.result();
let mut shared_state = thread_shared_state.lock().unwrap();
shared_state.hash = Some(hex::encode(hash));
if let Some(waker) = shared_state.waker.take() {
waker.wake()
}
});
HasherFuture { shared_state }
}
}
How do I actually utilize this from, say, async-std? I know the async
keyword is supposed to create a function which returns a future which needs to be fed to an executor -- but how does rust know it is supposed to create a HasherFuture
as a return type? I assume that's not how it's supposed to work, which means that I'm suffering from a rather large knowledge gap here.