| use std::future::{Future, join}; | |
| use std::pin::Pin; | |
| use std::sync::Arc; | |
| use std::task::{Context, Poll, Wake}; | |
| use std::thread; | |
| struct PollN { | |
| val: usize, | |
| polled: usize, | |
| num: usize, | |
| } | |
| impl Future for PollN { | |
| type Output = usize; | |
| fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | |
| self.polled += 1; | |
| if self.polled == self.num { | |
| return Poll::Ready(self.val); | |
| } | |
| cx.waker().wake_by_ref(); | |
| Poll::Pending | |
| } | |
| } | |
| fn poll_n(val: usize, num: usize) -> PollN { | |
| PollN { val, num, polled: 0 } | |
| } | |
| #[test] | |
| fn test_join() { | |
| block_on(async move { | |
| let x = join!(async { 0 }).await; | |
| assert_eq!(x, 0); | |
| let x = join!(async { 0 }, async { 1 }).await; | |
| assert_eq!(x, (0, 1)); | |
| let x = join!(async { 0 }, async { 1 }, async { 2 }).await; | |
| assert_eq!(x, (0, 1, 2)); | |
| let x = join!( | |
| poll_n(0, 1), | |
| poll_n(1, 5), | |
| poll_n(2, 2), | |
| poll_n(3, 1), | |
| poll_n(4, 2), | |
| poll_n(5, 3), | |
| poll_n(6, 4), | |
| poll_n(7, 1) | |
| ) | |
| .await; | |
| assert_eq!(x, (0, 1, 2, 3, 4, 5, 6, 7)); | |
| let y = String::new(); | |
| let x = join!(async { | |
| println!("{y}"); | |
| 1 | |
| }) | |
| .await; | |
| assert_eq!(x, 1); | |
| }); | |
| } | |
| /// Tests that `join!(…)` behaves "like a function": evaluating its arguments | |
| /// before applying any of its own logic. | |
| /// | |
| /// _e.g._, `join!(async_fn(&borrowed), …)` does not consume `borrowed`; | |
| /// and `join!(opt_fut?, …)` does let that `?` refer to the callsite scope. | |
| mod test_join_function_like_value_arg_semantics { | |
| use super::*; | |
| async fn async_fn(_: impl Sized) {} | |
| // no need to _run_ this test, just to compile it. | |
| fn _join_does_not_unnecessarily_move_mentioned_bindings() { | |
| let not_copy = vec![()]; | |
| let _ = join!(async_fn(¬_copy)); // should not move `not_copy` | |
| let _ = ¬_copy; // OK | |
| } | |
| #[test] | |
| fn join_lets_control_flow_effects_such_as_try_flow_through() { | |
| let maybe_fut = None; | |
| if false { | |
| *&mut { maybe_fut } = Some(async {}); | |
| loop {} | |
| } | |
| assert!(Option::is_none(&try { join!(maybe_fut?, async { unreachable!() }) })); | |
| } | |
| #[test] | |
| fn join_is_able_to_handle_temporaries() { | |
| let _ = join!(async_fn(&String::from("temporary"))); | |
| let () = block_on(join!(async_fn(&String::from("temporary")))); | |
| } | |
| } | |
| fn block_on(fut: impl Future) { | |
| struct Waker; | |
| impl Wake for Waker { | |
| fn wake(self: Arc<Self>) { | |
| thread::current().unpark() | |
| } | |
| } | |
| let waker = Arc::new(Waker).into(); | |
| let mut cx = Context::from_waker(&waker); | |
| let mut fut = Box::pin(fut); | |
| loop { | |
| match fut.as_mut().poll(&mut cx) { | |
| Poll::Ready(_) => break, | |
| Poll::Pending => thread::park(), | |
| } | |
| } | |
| } | |
| // just tests by whether or not this compiles | |
| fn _pending_impl_all_auto_traits<T>() { | |
| use std::panic::{RefUnwindSafe, UnwindSafe}; | |
| fn all_auto_traits<T: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe>() {} | |
| all_auto_traits::<std::future::Pending<T>>(); | |
| } |