future_clicker/
lib.rs

1//! A [`Future`] value that resolves once it's explicitly completed, potentially
2//! from a different thread or task, similar to Java's `CompletableFuture`.
3//!
4//! Currently, this is implemented using [`Mutex`][parking_lot::Mutex] from the [`parking_lot`] crate.
5//!
6//! # Examples
7//!
8//! Create an incomplete [`ControlledFuture`] and explicitly complete it with the
9//! completer:
10//! ```
11//! # use future_clicker::ControlledFuture;
12//! # use futures::executor::block_on;
13//! let (future, completer) = ControlledFuture::<i32>::new();
14//! completer.complete(5).unwrap();
15//! assert_eq!(block_on(future), Ok(5));
16//! ```
17//!
18//! Create an initially complete [`ControlledFuture`] that can be immediately
19//! resolved:
20//! ```
21//! # use future_clicker::ControlledFuture;
22//! # use futures::executor::block_on;
23//! assert_eq!(block_on(ControlledFuture::new_completed(10)), Ok(10));
24//! ```
25
26#![warn(clippy::pedantic, missing_docs)]
27
28mod completer;
29mod error;
30mod state;
31
32use std::{
33    future::Future,
34    marker::Unpin,
35    pin::Pin,
36    sync::Arc,
37    task::{Context, Poll},
38};
39
40use parking_lot::Mutex;
41use tracing::{instrument, trace};
42
43use self::state::State;
44pub use self::{
45    completer::FutureClicker,
46    error::{Error, Result},
47};
48
49/// A [`Future`] that will resolve either immediately, or in the future.
50///
51/// Will not resolve unless it has been explicitly completed, either
52/// by constructing it with [`ControlledFuture::new_completed`], or using [`FutureClicker::complete`].
53#[derive(Debug)]
54pub struct ControlledFuture<T: Unpin> {
55    state: Arc<Mutex<State<T>>>,
56}
57
58impl<T: Unpin + Send + 'static> ControlledFuture<T> {
59    /// Construct a `ControlledFuture` that will resolve once the returned
60    /// `FutureClicker` is used to set a value.
61    #[must_use]
62    pub fn new() -> (Self, FutureClicker<T>) {
63        let s = State::new();
64        (Self { state: s.0 }, FutureClicker { state: s.1 })
65    }
66
67    /// Construct a [`ControlledFuture`] that will resolve immediately to the
68    /// given value.
69    ///
70    /// No [`FutureClicker`] is returned as the [`ControlledFuture`] is already complete.
71    #[must_use]
72    pub fn new_completed(value: T) -> Self {
73        Self {
74            state: State::new_completed(value),
75        }
76    }
77}
78
79impl<T: Unpin + 'static + Send> Future for ControlledFuture<T> {
80    type Output = Result<T>;
81
82    #[instrument(skip_all)]
83    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
84        use Poll::{Pending, Ready};
85        use State::{Complete, Dropped, Incomplete, Waiting};
86
87        trace!("poll");
88        let mut state = self.state.lock_arc();
89        trace!("locked");
90
91        match &mut *state {
92            Waiting(w) if w.will_wake(cx.waker()) => {
93                trace!("state Waiting will_wake");
94                Pending
95            }
96            state @ (Waiting(_) | Incomplete) => {
97                trace!("state {state:?}");
98                *state = Waiting(cx.waker().clone());
99                Pending
100            }
101            Complete(value) => {
102                if let Some(value) = value.take() {
103                    trace!("state Complete Some");
104                    Ready(Ok(value))
105                } else {
106                    trace!("state Complete None");
107                    Ready(Err(Error::AlreadyCompleted))
108                }
109            }
110            Dropped => {
111                trace!("state Dropped");
112                Ready(Err(Error::CompleterDropped))
113            }
114        }
115    }
116}