use std::sync::{Arc, Mutex};
pub struct AsyncTask<T> {
result: Arc<Mutex<Option<T>>>,
is_started: Arc<Mutex<bool>>,
}
impl<T> AsyncTask<T> {
pub fn new() -> Self {
Self { result: Arc::new(Mutex::new(None)), is_started: Arc::new(Mutex::new(false)) }
}
pub fn callback(&self) -> impl Fn(T) + Send + Clone + 'static
where
T: Send + 'static,
{
let result = self.result.clone();
*self.is_started.lock().unwrap() = true;
move |value| {
*result.lock().unwrap() = Some(value);
}
}
pub fn take(&self) -> Option<T> {
self.result.lock().unwrap().take()
}
pub fn is_started(&self) -> bool {
*self.is_started.lock().unwrap()
}
pub fn is_ready(&self) -> bool {
self.result.lock().unwrap().is_some()
}
pub fn is_pending(&self) -> bool {
self.is_started() && !self.is_ready()
}
}
impl<T> Default for AsyncTask<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Clone for AsyncTask<T> {
fn clone(&self) -> Self {
Self { result: self.result.clone(), is_started: self.is_started.clone() }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_async_task_basic() {
let task: AsyncTask<i32> = AsyncTask::new();
assert!(!task.is_started());
assert!(!task.is_ready());
assert!(task.take().is_none());
let callback = task.callback();
assert!(task.is_started());
assert!(!task.is_ready());
assert!(task.is_pending());
callback(42);
assert!(task.is_ready());
assert!(!task.is_pending());
assert_eq!(task.take(), Some(42));
assert!(task.take().is_none()); }
#[test]
fn test_async_task_thread() {
let task: AsyncTask<String> = AsyncTask::new();
let callback = task.callback();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_millis(10));
callback("done".to_string());
});
while !task.is_ready() {
std::thread::sleep(std::time::Duration::from_millis(1));
}
assert_eq!(task.take(), Some("done".to_string()));
}
}