laputa-async-task 0.3.0

Task abstraction for building executors
Documentation
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

use async_task::Runnable;

struct PendingNoop;

impl Future for PendingNoop {
    type Output = ();

    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
        Poll::Pending
    }
}

#[test]
fn detach_survives_a_scheduler_that_drops_then_panics() {
    let (runnable, task) = async_task::spawn(PendingNoop, |runnable: Runnable| {
        drop(runnable);
        panic!("scheduler panicked");
    });

    assert!(!runnable.run());

    let result =
        std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| task.detach()));
    assert!(result.is_err());
}

#[test]
fn detach_releases_task_when_begin_hook_panics() {
    use std::sync::{Arc, atomic::{AtomicBool, Ordering}};

    struct DropFlag(Arc<AtomicBool>);
    impl Drop for DropFlag {
        fn drop(&mut self) {
            self.0.store(true, Ordering::SeqCst);
        }
    }

    let dropped = Arc::new(AtomicBool::new(false));
    let flag = DropFlag(dropped.clone());
    let first = AtomicBool::new(true);
    let schedule = async_task::ScheduleParts::new(
        move || {
            if first.swap(false, Ordering::SeqCst) {
                panic!("begin panicked");
            }
        },
        || {},
        |runnable: Runnable| drop(runnable),
        || {},
    );
    let (runnable, task) = async_task::spawn(async move {
        let _flag = flag;
        std::future::pending::<()>().await;
    }, schedule);
    assert!(!runnable.run());
    assert!(std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| task.detach())).is_err());
    assert!(dropped.load(Ordering::SeqCst));
}