use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, Waker};
struct State<T> {
result: Option<T>,
completed: bool,
waker: Option<Waker>,
}
pub(crate) struct TaskCompletionSource<T>(Rc<RefCell<State<T>>>);
impl<T> Clone for TaskCompletionSource<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T> TaskCompletionSource<T> {
pub(crate) fn new() -> Self {
Self(Rc::new(RefCell::new(State { result: None, completed: false, waker: None })))
}
pub(crate) fn try_set_result(&self, value: T) -> bool {
let waker = {
let mut state = self.0.borrow_mut();
if state.completed {
return false;
}
state.completed = true;
state.result = Some(value);
state.waker.take()
};
if let Some(waker) = waker {
waker.wake();
}
true
}
pub(crate) fn task(&self) -> CompletionTask<T> {
CompletionTask(self.0.clone())
}
}
pub(crate) struct CompletionTask<T>(Rc<RefCell<State<T>>>);
impl<T> Future for CompletionTask<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
let mut state = self.0.borrow_mut();
match state.result.take() {
Some(result) => Poll::Ready(result),
None => {
state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::task::Wake;
use std::sync::Arc;
struct NoopWaker;
impl Wake for NoopWaker {
fn wake(self: Arc<Self>) {}
}
#[test]
fn completes_once_with_the_first_result() {
let source = TaskCompletionSource::new();
let mut task = source.task();
let waker = Waker::from(Arc::new(NoopWaker));
let mut cx = Context::from_waker(&waker);
assert!(Pin::new(&mut task).poll(&mut cx).is_pending());
assert!(source.try_set_result(1));
assert!(!source.clone().try_set_result(2));
assert_eq!(Poll::Ready(1), Pin::new(&mut task).poll(&mut cx));
}
}