blob: 5f5e7511750a1066cfa4fef4c299b70ac76cf410 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::mpsc;
use std::task::Context;
use std::task::Poll;
use std::task::Waker;
use std::thread;
use std::time::Duration;
use asyncband::blocking::FutureExt as _;
// Long timeouts in this test target are watchdogs for detecting a stalled test process, not
// assertions about elapsed-time precision.
const TEST_WATCHDOG: Duration = Duration::from_secs(5);
#[test]
fn block_on_supports_method_and_ufcs_syntax() {
assert_eq!(async { 42 }.block_on(), 42);
assert_eq!(asyncband::blocking::FutureExt::block_on(async { 7 }), 7);
}
#[test]
fn wait_timeout_polls_before_checking_the_deadline() {
assert_eq!(async { 42 }.wait_timeout(Duration::ZERO), Some(42));
assert_eq!(
std::future::pending::<()>().wait_timeout(Duration::ZERO),
None
);
}
#[test]
fn pending_future_times_out() {
assert_eq!(
std::future::pending::<()>().wait_timeout(Duration::from_millis(1)),
None
);
}
#[test]
fn wake_notification_resumes_a_timed_wait() {
let (completion, future, polls) = controlled_future();
let producer = thread::spawn(move || {
polls.recv().unwrap();
completion.complete(7);
});
assert_eq!(future.wait_timeout(TEST_WATCHDOG), Some(7));
producer.join().unwrap();
}
#[test]
fn nested_waits_use_independent_notifications() {
let (done_tx, done_rx) = mpsc::channel();
let worker = thread::spawn(move || {
let mut outer_polled = false;
let output = std::future::poll_fn(|outer_context| {
if outer_polled {
Poll::Ready(42)
} else {
outer_polled = true;
outer_context.waker().wake_by_ref();
let mut inner_polled = false;
let inner_output = std::future::poll_fn(|inner_context| {
if inner_polled {
Poll::Ready(7)
} else {
inner_polled = true;
inner_context.waker().wake_by_ref();
Poll::Pending
}
})
.wait_timeout(TEST_WATCHDOG);
assert_eq!(inner_output, Some(7));
Poll::Pending
}
})
.block_on();
done_tx.send(output).unwrap();
});
assert_eq!(
done_rx
.recv_timeout(TEST_WATCHDOG)
.expect("nested waits shared or lost a notification"),
42
);
worker.join().unwrap();
}
#[test]
fn block_on_preserves_the_current_threads_park_token() {
let (completion, future, polls) = controlled_future();
let (worker_thread_tx, worker_thread_rx) = mpsc::channel();
let (done_tx, done_rx) = mpsc::channel();
let worker = thread::spawn(move || {
worker_thread_tx.send(thread::current()).unwrap();
assert_eq!(future.block_on(), 7);
thread::park();
done_tx.send(()).unwrap();
});
let worker_thread = worker_thread_rx.recv().unwrap();
polls.recv_timeout(TEST_WATCHDOG).unwrap();
worker_thread.unpark();
completion.wake();
polls.recv_timeout(TEST_WATCHDOG).unwrap();
completion.complete(7);
done_rx
.recv_timeout(TEST_WATCHDOG)
.expect("block_on consumed an unrelated thread park token");
worker.join().unwrap();
}
struct Shared<T> {
state: Mutex<State<T>>,
}
struct State<T> {
output: Option<T>,
waker: Option<Waker>,
}
struct Completion<T> {
shared: Arc<Shared<T>>,
}
impl<T> Completion<T> {
fn wake(&self) {
let waker = self.shared.state.lock().unwrap().waker.clone();
if let Some(waker) = waker {
waker.wake();
}
}
fn complete(&self, output: T) {
let waker = {
let mut state = self.shared.state.lock().unwrap();
state.output = Some(output);
state.waker.take()
};
if let Some(waker) = waker {
waker.wake();
}
}
}
struct ControlledFuture<T> {
shared: Arc<Shared<T>>,
polls: mpsc::Sender<()>,
}
impl<T> Future for ControlledFuture<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let mut state = this.shared.state.lock().unwrap();
if let Some(output) = state.output.take() {
return Poll::Ready(output);
}
state.waker = Some(cx.waker().clone());
drop(state);
this.polls.send(()).unwrap();
Poll::Pending
}
}
fn controlled_future<T>() -> (Completion<T>, ControlledFuture<T>, mpsc::Receiver<()>) {
let shared = Arc::new(Shared {
state: Mutex::new(State {
output: None,
waker: None,
}),
});
let (polls_tx, polls_rx) = mpsc::channel();
(
Completion {
shared: shared.clone(),
},
ControlledFuture {
shared,
polls: polls_tx,
},
polls_rx,
)
}