128 lines
3.3 KiB
Rust
128 lines
3.3 KiB
Rust
use std::future::{join, Future};
|
|
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]
|
|
#[cfg_attr(miri, ignore)] // self-referential generators do not work with Miri's aliasing checks
|
|
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>>();
|
|
}
|