use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};
use crate::scan::LoadPayload;
pub type ScanJob = Box<dyn FnOnce() -> LoadPayload + Send>;
pub type ScanFuture = Pin<Box<dyn Future<Output = LoadPayload> + Send + 'static>>;
pub trait ScanExecutor: Send + Sync {
fn spawn_blocking(&self, job: ScanJob) -> ScanFuture;
}
#[derive(Debug, Default, Clone, Copy)]
pub struct ThreadExecutor;
impl ScanExecutor for ThreadExecutor {
fn spawn_blocking(&self, job: ScanJob) -> ScanFuture {
let state = Arc::new(Mutex::new(JobFutureState {
result: None,
waker: None,
}));
let state_for_thread = Arc::clone(&state);
std::thread::spawn(move || {
let result = job();
let mut locked = state_for_thread.lock().unwrap();
locked.result = Some(result);
if let Some(waker) = locked.waker.take() {
waker.wake();
}
});
Box::pin(JobFuture { state })
}
}
struct JobFutureState {
result: Option<LoadPayload>,
waker: Option<Waker>,
}
struct JobFuture {
state: Arc<Mutex<JobFutureState>>,
}
unsafe impl Send for JobFuture {}
impl Future for JobFuture {
type Output = LoadPayload;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut state = self.state.lock().unwrap();
if let Some(result) = state.result.take() {
Poll::Ready(result)
} else {
state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}