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}