mod test_util; use crate::test_util::{expect_elapsed, FakeWaker}; use core::future::Future; use core::pin::Pin; use core::sync::atomic::{AtomicBool, Ordering}; use core::task::{Context, Poll}; use core::time::Duration; use rusty_fork::rusty_fork_test; use safina::timer::{ sleep_for, start_timer_thread, with_deadline, with_timeout, DeadlineExceededError, }; use std::sync::Arc; use std::time::Instant; struct PendingFuture; impl Future for PendingFuture { type Output = (); fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { Poll::Pending } } fn timer_thread_not_started_inner() { let deadline = Instant::now() + Duration::from_millis(1000); assert_eq!( "TimerThreadNotStarted", *(std::panic::catch_unwind(|| { safina::executor::block_on( async move { with_deadline(PendingFuture {}, deadline).await }, ) }) .unwrap_err() .downcast::<&'static str>() .unwrap()) ); for _ in 0..2 { start_timer_thread(); assert_eq!( DeadlineExceededError, safina::executor::block_on( async move { with_deadline(PendingFuture {}, deadline).await } ) .unwrap_err() ); } } rusty_fork_test! { #[test] fn timer_thread_not_started() { timer_thread_not_started_inner(); } } #[test] fn with_deadline_should_timeout() { safina::executor::block_on(async { start_timer_thread(); let before = Instant::now(); assert_eq!( DeadlineExceededError, with_deadline( async { sleep_for(Duration::from_millis(200)).await }, before + Duration::from_millis(100) ) .await .unwrap_err() ); expect_elapsed(before, 100..200); }); } #[test] fn with_deadline_should_return_result() { safina::executor::block_on(async { start_timer_thread(); let before = Instant::now(); assert_eq!( 42_u8, with_deadline( async { sleep_for(Duration::from_millis(100)).await; 42_u8 }, before + Duration::from_millis(200) ) .await .unwrap() ); expect_elapsed(before, 100..200); }); } #[test] fn with_timeout_should_timeout() { safina::executor::block_on(async { start_timer_thread(); let before = Instant::now(); assert_eq!( DeadlineExceededError, with_timeout( async { sleep_for(Duration::from_millis(200)).await }, Duration::from_millis(100) ) .await .unwrap_err() ); expect_elapsed(before, 100..200); }); } #[test] fn with_timeout_should_return_result() { safina::executor::block_on(async { start_timer_thread(); let before = Instant::now(); assert_eq!( 42_u8, with_timeout( async { sleep_for(Duration::from_millis(100)).await; 42_u8 }, Duration::from_millis(200) ) .await .unwrap() ); expect_elapsed(before, 100..200); }); } #[test] fn should_return_immediately_when_inner_already_ready() { safina::executor::block_on(async { start_timer_thread(); let before = Instant::now(); with_deadline(async { 42_u8 }, before + Duration::from_millis(100)) .await .unwrap(); expect_elapsed(before, 0..20); }); } #[test] fn deadline_already_past() { safina::executor::block_on(async { start_timer_thread(); let before = Instant::now(); assert_eq!( DeadlineExceededError, with_deadline( async { sleep_for(Duration::from_millis(200)).await }, before.checked_sub(Duration::from_millis(100)).unwrap() ) .await .unwrap_err() ); expect_elapsed(before, 0..20); }); } #[test] fn should_use_most_recent_waker_passed_to_poll() { // "Note that on multiple calls to poll, only the Waker from the Context // passed to the most recent call should be scheduled to receive a wakeup." // https://doc.rust-lang.org/stable/std/future/trait.Future.html#tymethod.poll start_timer_thread(); let deadline = Instant::now() + Duration::from_millis(100); let mut fut = Box::pin(async move { with_deadline(PendingFuture {}, deadline).await }); let waker1_called = Arc::new(AtomicBool::new(false)); { let waker1 = FakeWaker::new(&waker1_called).into_waker(); let mut cx = Context::from_waker(&waker1); assert_eq!(Poll::Pending, fut.as_mut().poll(&mut cx)); } let waker2_called = Arc::new(AtomicBool::new(false)); { let waker2 = FakeWaker::new(&waker2_called).into_waker(); let mut cx = Context::from_waker(&waker2); assert_eq!(Poll::Pending, fut.as_mut().poll(&mut cx)); } std::thread::sleep(Duration::from_millis(200)); { let waker3_called = Arc::new(AtomicBool::new(true /* should never get called */)); let waker3 = FakeWaker::new(&waker3_called).into_waker(); let mut cx = Context::from_waker(&waker3); assert_eq!( Poll::Ready(Err(DeadlineExceededError)), fut.as_mut().poll(&mut cx) ); } assert!(!waker1_called.load(Ordering::Acquire)); assert!(waker2_called.load(Ordering::Acquire)); }