use super::PendingEventsSender;
use super::data::TestLoopData;
use crate::futures::{AsyncComputationSpawner, FutureSpawner};
use futures::future::BoxFuture;
use futures::task::{ArcWake, waker_ref};
use near_time::Duration;
use parking_lot::Mutex;
use std::sync::Arc;
use std::task::Context;
pub type TestLoopFutureSpawner = PendingEventsSender;
impl FutureSpawner for TestLoopFutureSpawner {
fn spawn_boxed(&self, description: &str, f: BoxFuture<'static, ()>) {
let task = Arc::new(FutureTask {
future: Mutex::new(Some(f)),
sender: self.clone(),
description: description.to_string(),
});
let callback = move |_: &mut TestLoopData| drive_futures(&task);
self.send(format!("FutureSpawn({})", description), Box::new(callback));
}
}
struct FutureTask {
future: Mutex<Option<BoxFuture<'static, ()>>>,
sender: PendingEventsSender,
description: String,
}
impl ArcWake for FutureTask {
fn wake_by_ref(arc_self: &Arc<Self>) {
let clone = arc_self.clone();
arc_self.sender.send(
format!("FutureTask({})", arc_self.description),
Box::new(move |_: &mut TestLoopData| drive_futures(&clone)),
);
}
}
fn drive_futures(task: &Arc<FutureTask>) {
let mut future_slot = task.future.lock();
if let Some(mut future) = future_slot.take() {
let waker = waker_ref(&task);
let context = &mut Context::from_waker(&*waker);
if future.as_mut().poll(context).is_pending() {
*future_slot = Some(future);
}
}
}
pub struct TestLoopAsyncComputationSpawner {
sender: PendingEventsSender,
artificial_delay: Box<dyn Fn(&str) -> Duration + Send + Sync>,
}
impl TestLoopAsyncComputationSpawner {
pub fn new(
sender: PendingEventsSender,
artificial_delay: impl Fn(&str) -> Duration + Send + Sync + 'static,
) -> Self {
Self { sender, artificial_delay: Box::new(artificial_delay) }
}
}
impl AsyncComputationSpawner for TestLoopAsyncComputationSpawner {
fn spawn_boxed(&self, name: &str, f: Box<dyn FnOnce() + Send>) {
self.sender.send_with_delay(
format!("AsyncComputation({})", name),
Box::new(move |_| f()),
(self.artificial_delay)(name),
);
}
}