use crate::outcome::{PanicInfo, TaskId, TaskOutcome, TaskResult};
use crossbeam_channel::{bounded, Receiver, RecvTimeoutError};
use std::thread;
use std::thread::JoinHandle;
use std::time::Instant;
const SINGLE_TASK_ID: TaskId = TaskId::MAX;
pub fn join_with_deadline<T: Send + 'static>(
handle: JoinHandle<T>,
deadline: Instant,
) -> JoinOutcome<T> {
let (sender, receiver) = bounded(1);
thread::spawn(move || {
let finished_at;
let outcome = match handle.join() {
Ok(value) => {
finished_at = Instant::now();
TaskOutcome::Completed(value)
}
Err(payload) => {
finished_at = Instant::now();
TaskOutcome::Panicked(PanicInfo::from_payload(payload))
}
};
let _ = sender.send(TaskResult {
task_id: SINGLE_TASK_ID,
started_at: None,
finished_at,
outcome,
});
});
match receiver.recv_deadline(deadline) {
Ok(result) => JoinOutcome::Completed(result),
Err(RecvTimeoutError::Timeout) => {
JoinOutcome::TimedOut(LateTask { receiver })
}
Err(RecvTimeoutError::Disconnected) => {
match receiver.recv() {
Ok(result) => JoinOutcome::Completed(result),
Err(_) => JoinOutcome::TimedOut(LateTask { receiver }),
}
}
}
}
#[derive(Debug)]
pub enum JoinOutcome<T> {
Completed(TaskResult<T>),
TimedOut(LateTask<T>),
}
#[derive(Debug)]
pub struct LateTask<T> {
receiver: Receiver<TaskResult<T>>,
}
impl<T> LateTask<T> {
pub fn wait(self) -> TaskResult<T> {
self.receiver
.recv()
.expect("background worker dropped before sending result")
}
}