|  | use std::mem; | 
|  | use std::panic::catch_unwind; | 
|  | use std::sync::atomic::{AtomicUsize, Ordering}; | 
|  | use std::sync::Mutex; | 
|  | use std::task::{Poll, Waker}; | 
|  |  | 
|  | use async_executor::{Executor, Task}; | 
|  | use futures_lite::future; | 
|  | use once_cell::sync::Lazy; | 
|  |  | 
|  | #[test] | 
|  | fn executor_cancels_everything() { | 
|  | static DROP: AtomicUsize = AtomicUsize::new(0); | 
|  | static WAKER: Lazy<Mutex<Option<Waker>>> = Lazy::new(|| Default::default()); | 
|  |  | 
|  | let ex = Executor::new(); | 
|  |  | 
|  | let task = ex.spawn(async { | 
|  | let _guard = CallOnDrop(|| { | 
|  | DROP.fetch_add(1, Ordering::SeqCst); | 
|  | }); | 
|  |  | 
|  | future::poll_fn(|cx| { | 
|  | *WAKER.lock().unwrap() = Some(cx.waker().clone()); | 
|  | Poll::Pending::<()> | 
|  | }) | 
|  | .await; | 
|  | }); | 
|  |  | 
|  | future::block_on(ex.tick()); | 
|  | assert!(WAKER.lock().unwrap().is_some()); | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 0); | 
|  |  | 
|  | drop(ex); | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 1); | 
|  |  | 
|  | assert!(catch_unwind(|| future::block_on(task)).is_err()); | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 1); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn leaked_executor_leaks_everything() { | 
|  | static DROP: AtomicUsize = AtomicUsize::new(0); | 
|  | static WAKER: Lazy<Mutex<Option<Waker>>> = Lazy::new(|| Default::default()); | 
|  |  | 
|  | let ex = Executor::new(); | 
|  |  | 
|  | let task = ex.spawn(async { | 
|  | let _guard = CallOnDrop(|| { | 
|  | DROP.fetch_add(1, Ordering::SeqCst); | 
|  | }); | 
|  |  | 
|  | future::poll_fn(|cx| { | 
|  | *WAKER.lock().unwrap() = Some(cx.waker().clone()); | 
|  | Poll::Pending::<()> | 
|  | }) | 
|  | .await; | 
|  | }); | 
|  |  | 
|  | future::block_on(ex.tick()); | 
|  | assert!(WAKER.lock().unwrap().is_some()); | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 0); | 
|  |  | 
|  | mem::forget(ex); | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 0); | 
|  |  | 
|  | assert!(future::block_on(future::poll_once(task)).is_none()); | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 0); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn await_task_after_dropping_executor() { | 
|  | let s: String = "hello".into(); | 
|  |  | 
|  | let ex = Executor::new(); | 
|  | let task: Task<&str> = ex.spawn(async { &*s }); | 
|  | assert!(ex.try_tick()); | 
|  |  | 
|  | drop(ex); | 
|  | assert_eq!(future::block_on(task), "hello"); | 
|  | drop(s); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn drop_executor_and_then_drop_finished_task() { | 
|  | static DROP: AtomicUsize = AtomicUsize::new(0); | 
|  |  | 
|  | let ex = Executor::new(); | 
|  | let task = ex.spawn(async { | 
|  | CallOnDrop(|| { | 
|  | DROP.fetch_add(1, Ordering::SeqCst); | 
|  | }) | 
|  | }); | 
|  | assert!(ex.try_tick()); | 
|  |  | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 0); | 
|  | drop(ex); | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 0); | 
|  | drop(task); | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 1); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn drop_finished_task_and_then_drop_executor() { | 
|  | static DROP: AtomicUsize = AtomicUsize::new(0); | 
|  |  | 
|  | let ex = Executor::new(); | 
|  | let task = ex.spawn(async { | 
|  | CallOnDrop(|| { | 
|  | DROP.fetch_add(1, Ordering::SeqCst); | 
|  | }) | 
|  | }); | 
|  | assert!(ex.try_tick()); | 
|  |  | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 0); | 
|  | drop(task); | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 1); | 
|  | drop(ex); | 
|  | assert_eq!(DROP.load(Ordering::SeqCst), 1); | 
|  | } | 
|  |  | 
|  | struct CallOnDrop<F: Fn()>(F); | 
|  |  | 
|  | impl<F: Fn()> Drop for CallOnDrop<F> { | 
|  | fn drop(&mut self) { | 
|  | (self.0)(); | 
|  | } | 
|  | } |