arty 0.5.0

Single-threaded, thread-aware application runtime.
Documentation
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Accepted factories remain task-owned until invocation or shutdown disposal.

#![cfg(feature = "rt")]
#![cfg(test)]

mod panic_support;

mod support;

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

use arty::runtime::RuntimeOperations;
use panic_support::runtime;
use support::JoinHandleExt as _;

testing_aids::init_tracing!();

struct CaptureDropPanic(Arc<AtomicUsize>);

impl Drop for CaptureDropPanic {
    fn drop(&mut self) {
        self.0.fetch_add(1, Ordering::SeqCst);
        assert!(!std::thread::panicking());
        panic!("factory capture destructor");
    }
}

#[test]
fn queued_remote_factory_drop_panic_does_not_interrupt_shutdown() {
    let runtime = runtime();
    let drops = Arc::new(AtomicUsize::new(0));
    let invoked = Arc::new(AtomicUsize::new(0));
    let capture = CaptureDropPanic(Arc::clone(&drops));
    let captured_invoked = Arc::clone(&invoked);
    #[expect(
        clippy::async_yields_async,
        reason = "the join must be observed after root destruction and shutdown, not awaited inside the root"
    )]
    let queued = runtime
        .scheduler()
        .block_on(async move |cx| {
            let queued = cx.scheduler().spawn(move |_| {
                captured_invoked.fetch_add(1, Ordering::SeqCst);
                drop(capture);
                std::future::ready(())
            });
            RuntimeOperations::from(&cx).request_stop();
            queued
        })
        .unwrap();
    runtime.stop().unwrap();
    assert_eq!(drops.load(Ordering::SeqCst), 1);
    assert_eq!(invoked.load(Ordering::SeqCst), 0);
    assert!(queued.join().unwrap_err().is_shutdown());
}

#[test]
fn remote_factory_capture_drop_panic_is_a_join_error() {
    let runtime = runtime();
    let drops = Arc::new(AtomicUsize::new(0));
    let remote_capture = CaptureDropPanic(Arc::clone(&drops));
    runtime
        .scheduler()
        .block_on(async move |cx| {
            let remote = cx.scheduler().spawn(move |_| {
                drop(remote_capture);
                std::future::ready(())
            });
            assert!(remote.await.unwrap_err().is_panic());
            assert_eq!(cx.scheduler().spawn(async |_| 7).await.unwrap(), 7);
        })
        .unwrap();
    assert_eq!(drops.load(Ordering::SeqCst), 1);
    runtime.stop().unwrap();
}