use std::sync::{Arc, Mutex};
pub enum JobPoll<T> {
Empty,
Pending,
Ready(T),
Failed(String),
Cancelled,
}
#[derive(Debug, PartialEq)]
enum SlotState<T> {
Empty,
Pending,
Ready(T),
Failed(String),
Cancelled,
}
pub struct JobSlot<T: Send + 'static> {
state: Arc<Mutex<SlotState<T>>>,
}
unsafe impl<T: Send + 'static> Send for JobSlot<T> {}
unsafe impl<T: Send + 'static> Sync for JobSlot<T> {}
impl<T: Send + 'static> Default for JobSlot<T> {
fn default() -> Self {
Self {
state: Arc::new(Mutex::new(SlotState::Empty)),
}
}
}
impl<T: Send + 'static> JobSlot<T> {
pub fn empty() -> Self {
Self::default()
}
pub fn new() -> (Self, JobSender<T>) {
let state = Arc::new(Mutex::new(SlotState::Pending));
let slot = Self {
state: state.clone(),
};
let sender = JobSender {
state,
consumed: false,
};
(slot, sender)
}
pub fn take(&mut self) -> JobPoll<T> {
let mut guard = self.state.lock().unwrap();
match &*guard {
SlotState::Empty => JobPoll::Empty,
SlotState::Pending => JobPoll::Pending,
SlotState::Ready(_) => {
let SlotState::Ready(v) = std::mem::replace(&mut *guard, SlotState::Empty) else {
unreachable!()
};
JobPoll::Ready(v)
}
SlotState::Failed(_) => {
let SlotState::Failed(msg) = std::mem::replace(&mut *guard, SlotState::Empty)
else {
unreachable!()
};
JobPoll::Failed(msg)
}
SlotState::Cancelled => {
*guard = SlotState::Empty;
JobPoll::Cancelled
}
}
}
pub fn is_empty(&self) -> bool {
matches!(*self.state.lock().unwrap(), SlotState::Empty)
}
pub fn is_pending(&self) -> bool {
matches!(*self.state.lock().unwrap(), SlotState::Pending)
}
pub fn is_ready(&self) -> bool {
matches!(*self.state.lock().unwrap(), SlotState::Ready(_))
}
}
pub struct JobSender<T: Send + 'static> {
state: Arc<Mutex<SlotState<T>>>,
consumed: bool,
}
unsafe impl<T: Send + 'static> Send for JobSender<T> {}
impl<T: Send + 'static> JobSender<T> {
pub fn complete(mut self, result: T) {
*self.state.lock().unwrap() = SlotState::Ready(result);
self.consumed = true;
}
pub fn fail(mut self, msg: impl Into<String>) {
*self.state.lock().unwrap() = SlotState::Failed(msg.into());
self.consumed = true;
}
pub fn cancel(mut self) {
*self.state.lock().unwrap() = SlotState::Cancelled;
self.consumed = true;
}
}
impl<T: Send + 'static> Drop for JobSender<T> {
fn drop(&mut self) {
if !self.consumed {
*self.state.lock().unwrap() = SlotState::Cancelled;
}
}
}