use some_executor::{
Instant, SomeExecutor, SomeStaticExecutor,
current_executor::current_executor,
observer::FinishedObservation,
task::{Configuration, Task},
thread_executor::thread_static_executor,
};
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::task::{Context, Poll};
use test_executors::async_test;
#[cfg(target_arch = "wasm32")]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
#[cfg(not(target_arch = "wasm32"))]
use std::thread;
#[cfg(not(target_arch = "wasm32"))]
use std::time;
#[cfg(target_arch = "wasm32")]
use wasm_safe_thread as thread;
#[cfg(target_arch = "wasm32")]
use web_time as time;
struct YieldOnceFuture {
poll_count: Arc<AtomicU32>,
waker_spawned: Arc<AtomicBool>,
result_value: i32,
}
impl YieldOnceFuture {
fn new(result_value: i32) -> Self {
Self {
poll_count: Arc::new(AtomicU32::new(0)),
waker_spawned: Arc::new(AtomicBool::new(false)),
result_value,
}
}
}
impl Future for YieldOnceFuture {
type Output = i32;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let count = self.poll_count.fetch_add(1, Ordering::Relaxed);
match count {
0 => {
if !self.waker_spawned.swap(true, Ordering::Relaxed) {
let waker = cx.waker().clone();
thread::spawn(move || {
thread::sleep(time::Duration::from_millis(10));
waker.wake();
});
}
Poll::Pending
}
_ => {
Poll::Ready(self.result_value)
}
}
}
}
#[async_test]
async fn test_yield_future_with_current_executor() {
let start_time = Instant::now();
let yield_future = YieldOnceFuture::new(42);
let poll_counter = yield_future.poll_count.clone();
let task = Task::without_notifications(
"yield-current-executor".to_string(),
Configuration::default(),
yield_future,
);
let mut executor = current_executor();
let observer = executor.spawn(task);
let result = match observer.await {
FinishedObservation::Ready(value) => value,
FinishedObservation::Cancelled => panic!("Task was cancelled unexpectedly"),
};
let duration = start_time.elapsed();
assert_eq!(result, 42);
assert!(poll_counter.load(Ordering::Relaxed) >= 2);
assert!(duration.as_millis() < 1000);
}
#[async_test]
async fn test_yield_future_with_thread_static_executor() {
let start_time = Instant::now();
let yield_future = YieldOnceFuture::new(99);
let poll_counter = yield_future.poll_count.clone();
let task = Task::without_notifications(
"yield-static-executor".to_string(),
Configuration::default(),
yield_future,
);
let mut static_executor = thread_static_executor(|executor| executor.clone_box());
let observer = static_executor.spawn_static(task);
let result = match observer.await {
FinishedObservation::Ready(value) => value,
FinishedObservation::Cancelled => panic!("Task was cancelled unexpectedly"),
};
let duration = start_time.elapsed();
assert_eq!(result, 99);
assert!(poll_counter.load(Ordering::Relaxed) >= 2);
assert!(duration.as_millis() < 1000);
}
#[async_test]
async fn test_multiple_yield_futures() {
let start_time = Instant::now();
let futures_data: Vec<_> = (0..5)
.map(|i| {
let future = YieldOnceFuture::new(i * 10);
let counter = future.poll_count.clone();
(future, counter)
})
.collect();
let mut results = Vec::new();
let mut poll_counters = Vec::new();
let mut executor = current_executor();
for (i, (future, counter)) in futures_data.into_iter().enumerate() {
poll_counters.push(counter);
let task = Task::without_notifications(
format!("multi-yield-{}", i),
Configuration::default(),
future,
);
let observer = executor.spawn(task);
let result = match observer.await {
FinishedObservation::Ready(value) => value,
FinishedObservation::Cancelled => panic!("Task {} was cancelled unexpectedly", i),
};
results.push(result);
}
let duration = start_time.elapsed();
for (i, &result) in results.iter().enumerate() {
assert_eq!(result, i as i32 * 10);
}
for (i, counter) in poll_counters.iter().enumerate() {
assert!(
counter.load(Ordering::Relaxed) >= 2,
"Future {} was only polled {} times",
i,
counter.load(Ordering::Relaxed)
);
}
assert!(duration.as_millis() < 2000);
}
struct ComplexYieldFuture {
poll_count: Arc<AtomicU32>,
target_polls: u32,
waker_spawned: Arc<AtomicBool>,
value: String,
}
impl ComplexYieldFuture {
fn new(target_polls: u32, value: String) -> Self {
Self {
poll_count: Arc::new(AtomicU32::new(0)),
target_polls,
waker_spawned: Arc::new(AtomicBool::new(false)),
value,
}
}
}
impl Future for ComplexYieldFuture {
type Output = String;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let count = self.poll_count.fetch_add(1, Ordering::Relaxed);
if count >= self.target_polls {
return Poll::Ready(format!(
"{}-completed-after-{}-polls",
self.value,
count + 1
));
}
if !self.waker_spawned.swap(true, Ordering::Relaxed) {
let waker = cx.waker().clone();
let remaining = self.target_polls - count;
thread::spawn(move || {
for i in 0..remaining {
thread::sleep(time::Duration::from_millis(5 + i as u64));
waker.wake_by_ref();
}
});
}
Poll::Pending
}
}
#[async_test]
async fn test_complex_yield_behavior() {
let complex_future1 = ComplexYieldFuture::new(3, "current-exec".to_string());
let task1 = Task::without_notifications(
"complex-current".to_string(),
Configuration::default(),
complex_future1,
);
let mut executor = current_executor();
let observer1 = executor.spawn(task1);
let result1 = match observer1.await {
FinishedObservation::Ready(value) => value,
FinishedObservation::Cancelled => panic!("Complex task 1 was cancelled unexpectedly"),
};
assert!(result1.contains("current-exec-completed-after"));
assert!(result1.contains("polls"));
let complex_future2 = ComplexYieldFuture::new(4, "static-exec".to_string());
let task2 = Task::without_notifications(
"complex-static".to_string(),
Configuration::default(),
complex_future2,
);
let mut static_executor2 = thread_static_executor(|executor| executor.clone_box());
let observer2 = static_executor2.spawn_static(task2);
let result2 = match observer2.await {
FinishedObservation::Ready(value) => value,
FinishedObservation::Cancelled => panic!("Complex task 2 was cancelled unexpectedly"),
};
assert!(result2.contains("static-exec-completed-after"));
assert!(result2.contains("polls"));
}